In the previous post, we saw that a
git pull command could lead to a merge commit.
A lot of developers prefer to have a linear git history and deeply despise those merge commits.
git pull doesn’t always lead to a merge commit:
$ git pull remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 33% (1/3) Unpacking objects: 66% (2/3) Unpacking objects: 100% (3/3) Unpacking objects: 100% (3/3), done. From ssh://remote.mygit.com/git-server/repos/paris 848af7d..aeb80a1 master -> origin/master Updating 848af7d..aeb80a1 Fast-forward README.txt | 1 + 1 file changed, 1 insertion(+)
The important word here is Fast-forward: all the commits on the local machine were already on the remote, so git was able to bring back the new commit without having to create a merge commit.
But, if someone else pushed a commit to the same branch before you had time to push yours, then the
git pull will create that pesky merge commit. That’s what happened in the previous post.
git pull will try to perform a fast forward, but if it is not always possible.
To anticipate what will happen, you can either do a
git fetch to get all the remote commits and to then assess the situation.
You can also tell git that you only want to do the
git pull if a fast forward can be performed.
To prevent a merge commit during a git pull, you can perform the command:
git pull --ff-only
$ git pull --ff-only remote: Counting objects: 3, done. remote: Compressing objects: 50% (1/2) remote: Compressing objects: 100% (2/2) remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 33% (1/3) Unpacking objects: 66% (2/3) Unpacking objects: 100% (3/3) Unpacking objects: 100% (3/3), done. From ssh://remote.mygit.com/git-server/repos/paris aeb80a1..26d005b master -> origin/master fatal: Not possible to fast-forward, aborting.
This time the fast forward is not possible so the
git pull aborts without creating any merge commit.
git pull always performs a
git fetchfirst, meaning that all the commits are now on your local machine for you to assess.
After the git fetch:
This new tree shows that:
- your working directory has not changed:
HEADstill points to your last commit
refs/remotesreferences shows you what is happening on the server: the commit
26d0is the commit which prevents the fast forward.
To solve that connundrum, the solution is to perform a
The rebase will bring back the remote commits and then try to reapply your local changes on top of those commits.
$ git rebase origin/master First, rewinding head to replay your work on top of it... Applying: Hello from Adele Using index info to reconstruct a base tree... M README.txt Falling back to patching base and 3-way merge... Auto-merging README.txt CONFLICT (content): Merge conflict in README.txt error: Failed to merge in the changes. Patch failed at 0001 Hello from Adele hint: Use 'git am --show-current-patch' to see the failed patch Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>", then run "git rebase --continue". You can instead skip this commit: run "git rebase --skip". To abort and get back to the state before "git rebase", run "git rebase --abort".
In our current test scenario, the rebase is not smooth : both our local commit and the remote commit changed the same file
$ cat README.txt Repo paris created Hello from Blair. <<<<<<< HEAD Another message from Blair. ======= Hello from Adele. >>>>>>> Hello from Adele
Here, the conflict is easy to fix:
$ cat README.txt Repo paris created Hello from Blair. Hello from Adele. Another message from Blair.
Once you have resolved the conflict(s), you follow the instructions given by git:
git addthe files you have changed during the conflicts resolution
- you complete the rebase with
git rebase --continue
$ git add README.txt $ git rebase --continue Applying: Hello from Adele
Now your commit tree looks like that:
You are back to a situation where you can push your commit tree to the remote — unless someone beat you to the punch and already pushed a new commit to the remote.
847a commit is still there. The
git rebase did not destroy it.
There is no references to that dangling commit though, and it will be garbage collected (aka deleted) by git at some point in the future. That also means that you still have the opportunity to revive it if needed using
reflog (more on that another day)
Time for a final
$ git push Enumerating objects: 5, done. Counting objects: 20% (1/5) Counting objects: 40% (2/5) Counting objects: 60% (3/5) Counting objects: 80% (4/5) Counting objects: 100% (5/5) Counting objects: 100% (5/5), done. Delta compression using up to 4 threads Compressing objects: 50% (1/2) Compressing objects: 100% (2/2) Compressing objects: 100% (2/2), done. Writing objects: 33% (1/3) Writing objects: 66% (2/3) Writing objects: 100% (3/3) Writing objects: 100% (3/3), 314 bytes | 314.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) To ssh://remote.mygit.com/git-server/repos/paris.git 26d005b..8a33541 master -> master
As expected, your commit tree is now:
If you have a sneak peek on the server commit tree:
Mission accomplished. A nice linear history of commits. End of the civilized world averted.