Is this content of a rebased commit different to that of the original commit? - git-commit

I am trying to understand why repetitive conflicts occur during rebasing. I begin with the concept that if a conflict is resolved and thus results in new commits being created, there should be no more conflicts from there on.
In the Object Storage section of the git documentation it explains that a commit object contains a header, the previous content of the file and the new content of the file, or before and after states.
If I git rebase and resolve a conflict during the rebase, does the new commit contain a different "before" state than the original commit?
I surmise that if the new commit contains the exact same before and after state content the commit and all it's future duplicates (created from further rebasing) will continue to cause conflicts. It stands to reason that git rerere would use that content as a fingerprint to match future conflicts with past conflicts and be able to auto-resolve them.
If, however, the new commits do not contain updated content in the before state there are two questions:
Why does future rebasing cause repetitive conflicts, and
How does git know what to put in the new before state?

Related

Bitbucket (server API) treats new files as renamed/copied old ones. Is there a way to prevent this?

The problem:
I'm using bitbucket stash (server) API in a script for my project with the {path} api method:
/rest/api/1.0/projects/{projectKey}/repos/{repositorySlug}/browse/{path:.*}
The idea was to save versions of config files in a repository (version01-versionXX for every config). But those configs have the same structure with different names and parameters,
so when I push a new config with a commit message like 'version01' without specifying any sourceCommitId, bitbucket automatically adds a parent commit from the last file with the same structure (if it exists). As a result, in this new file's history I'm getting several 'version01' commits, which is not what I was intended to have.
What I've tried:
If I do specify sourceCommitId as the initial or the last commit on the branch, I get an error message since the file doesn't exist on this commit.
I've tried to experiment with empty sourceBranch parameter, but still some parent commit appears.
The only idea I came up with is to create a new branch for every config, but this seems like overkill to me.
All attempts to find a method for editing file commit history via API also failed.
At the moment as a work around I create every config file with its name as the only line of its content and then change it to the structure I need. This works so far, but doesn't look like a good solution to me and requires 2 API requests instead of one.
Is there a better way to prevent BitBucket from treating those new files as copies of old ones?

Bitbucket server REST API: Does commit API overwrite the file or will it ever have conflicts (does a merge)

I am using /rest/api/1.0/projects/{projectKey}/repos/{repositorySlug}/browse/{path:.*}
to push commit to a file using Bitbucket.
I am able to do that successfully with branch, content, sourceCommitId , etc as parameters.
My question is : Do I need to worry about conflicts when using this API ? The error messages returned are about file not being present, contents not changed, etc but will there ever be a scenario where despite giving the last commit ID (in soureCommitId) , there will be conflicts ?
Does Bitbucket overwrite the file blindly ? (that would work for me)
Thanks!
Yes, BB will overwrite the file.
You can pass branch name instead the commit.
This will update the tip of branch.

How do I load the second to last committed version of my project using git?

Suppose that I made some changes and did this:
git add -A
git commit -m "comment commit_1"
Now I made more changes and did this again:
git add -A
git commit -m "comment commit_2"
Now, I basically want to discard commit_2 and start modifying my project again at the point of commit_1.
How do I do this?
If you don't mind losing the revisions at all (as if they never happened at all), you can do:
git reset --hard HEAD~
If you want to keep the current history and get a new version on top of what you have where you get rid of the changes of the revision, you can do:
git checkout HEAD~
git reset --soft the-branch
git commit -m "Taking back changes from the last revision"
# And if you like the result of this:
git branch -f the-branch
git checkout the-branch
There you go.
Now, I basically want to discard commit_2 and start modifying my project again at the point of commit_1.
If you literally want to discard commit_2, then it may be that you can use
git reset --hard HEAD^
(Note that at the time I wrote this, another post incorrectly told you to use HEAD~2; that would discard both commits, which is not what was asked.)
There are caveats to using commands that remove commits from a branch.
The first is that you could lose the changes you made in affected commits. That may seem a strange warning - you asked how to discard the changes - but realizing that mistakes happen, it's cause to be careful with these commands. (Just to clarify - if you did something like this accidentally, the changes would not be immediately lost forever; the reflogs offer a little safety net.)
Other issues arise if your repo has remotes and you've pushed the commit(s) in question to any of the remotes. It doesn't sound like that's an issue here, but if it is then there is more you should consider before removing commits from a branch.
But if you've looked it over and determined that you really do just want to discard the commits, the above should work. Do note, HEAD^ is just one of the possible expressions that refers to the second-to-last commit. At the moment it may be the simplest thing to use, but it won't always refer to that particular commit. HEAD^ really means "the commit which is the parent of the currently-checked-out commit".
If you're on the master branch, then master^ would also work - and would not depend on what's currently checked out. Even that changes whenever the master branch moves; if for some reason you need an expression that would continue to refer to the particular commit, you could create a tag
git tag mytag master^
and then mytag would work; or you could look up the commit ID of the commit in question (though it won't be a user-friendly name)
But I digress...
This is different from - and more drastic than - merely loading the 2nd-to-last commit as the question title suggests. To simply update your working tree with the 2nd-to-last commit's state, you could
git checkout HEAD^
This moves the HEAD (i.e. changes what's checked out) and updates the index and work tree, but it still keeps the commit on master in case you need it again later. However, it also puts you in a state called "detached head", meaning that git isn't really expecting you to create new commits from here.
If you want to both keep the old commit around, and be in a state where you can add commits on top of commit_1, then you need to create a new branch. If you did the previous checkout command, you could then
git checkout -b mybranch
and new changes could be committed to the new branch while leaving the original commit_2 on master. Or, you could put a branch (or tag) on commit_2 and then move master back to commit_1.
git checkout master
git branch mybranch
git reset --hard HEAD^
That may seem like a confusing number of options; but really that's because each one serves a somewhat different purpose, and only someone involved in your project is likely to have all the context to decide which makes sense.
So tl;dr - it sounds like you might be looking for git reset, but just doing that alone is only suitable if you really intend to permanently discard the changes from commit_2. If you really would want to preserve them for future reference (But maybe just weren't sure if that's an option) then there are ways to do that, too.

What does the git commit --squash option do and why would it be useful?

I've been looking for an alternative solution for squashing a series of commits in a branch. What I've done in the past is to use git rebase -i HEAD~<#commits> and then chose which commits to squash. Typically I picked the latest commit, and squashed redundant commits in between.
This approach works reasonably well, but I was hoping for a faster way. I noticed that there is a --squash option for git commit. But I'm not sure how this works. What does it do exactly, and how do you use it? Would this be a solution for my problem?
From the documentation:
--squash=<commit>
Construct a commit message for use with rebase --autosquash. The commit message subject line is taken from the specified commit with a prefix of "squash! ". Can be used with
additional commit message options (-m/-c/-C/-F). See git-rebase[1] for
details.
--squash (also the --fixup flags) takes a commit as an argument, and formats the message for use with --autosquash.
It's possible to set rebase.autosquash = true in your config to make the whole process shorter.
If you want to squash your last N commits:
git reset --soft HEAD~N && git commit
This will put the head on HEAD~N, so index and the working directory will not be altered at all, meaning that you can now use git commit and create a single commit for all the changes.
Consider you are working on feature branch where you have done say 15 commits. Now you are done with the task and want to merge it master. These 15 commits will make history of master branch long, better option is to squash them in single commit , give it meaningfull comment. Master history is clean and good for you also to review in future.
Git merge master --sqash.
Now IDE also provide very good support for this option.

git (source tree) how to recover unstaged files

I committed and pushed some code that did not work, which I thought would be a good opportunity to learn how to rollback to a previous commit/pull.
I used reset master to this commit which seemed to work but then I noticed the non-working commit was still there and it said I was 1 behind the most up-to date commit (the commit containing the code that did not work).
I then did something that may have been completely stupid, I used the remove button for the commit I was behind (the earlier code I did not want). After this, the commit is still there but now my uncommitted changes list is just showing a deletion of a file from the commit I wanted to lose.
It seems I've lost all my unstaged files I have been working on all day. How can I get these back and lose the useless commit that I pushed earlier?
Thank you for your time and input.
First things first, I'd recommend reading up on git terms and what different operations actually mean: http://git-scm.com/book/en/v2.
A good surface cheat sheet can be found at http://www.ndpsoftware.com/git-cheatsheet.html Now to get to what you are dealing with.
Remove doesn't remove things from git, in fact, it does exactly what the rm command does on the command line in unix. It removes them from your file system. If you rm files that you hadn't staged, they are just plain gone.
Unfortunately, I can't help you with getting back files you deleted. Look into your OS's recovery tools if you can, but don't hold your breath unless you have a delete cache of some sort (recycle bin on windows).
Revert allows you to make an inverse commit that puts everything back as if you had never made the original commit, but leaves the original in history so you can always see what happened. Despite what people tend to expect, this doesn't eliminate the commit, but actually creates a new one. This is the best option if you are going to be touching stuff that's already been pushed.
Reset changes one of the various states in your git process to allow you to change what you are doing. A soft reset changes just the HEAD pointer that you are pointing at. The command git reset --soft will change your HEAD pointer to whatever commit you provide it (by default HEAD accomplishing nothing). The command git reset will change your HEAD and your staging area to match a given commit (by default HEAD so it just unstages everything). The command git reset --hard will reset HEAD, staging area (aka index) and working directory to match a given commit (again, by default HEAD so you go back to a completely clean environment as if you hadn't done anything since last commit).
If you want to remove a commit completely, that is called resetting the head. Technically the commit itself never goes away, but you can make it so it's unreachable through normal means, and effectively take it out of project history. A word of caution: this should NEVER be done on pushed commits with downstream users. The big question is often, how much do you hate your downstream as the only safe option for them is to re-clone blowing away all of their own work after you do this.
The process would be to git reset --hard HEAD taking your entire environment to the state of the last commit. Then you want to reset --soft HEAD^ which changes the HEAD (what commit you're working off of) to one previous. This leaves you in the state just before you hit commit, with all of your staged changes still staged. At this point, you can discard your changes with git reset --hard HEAD, you can unstage everything with git reset HEAD And finally you can build another commit and move forward.
For a more indepth look at changing history, and why it's basically not allowed after it's been pushed you can look here: http://justinhileman.info/article/changing-history/ there is even a very nice flowchart with command references at the end of the article.

Resources