Rebasing is often useful when you’re pulling commits into your current branch. You almost certainly don’t want to create a merge commit just because you’ve made com-mits on your current branch and want to fetch new comcom-mits from upstream. A merge commit will be created, however, if you’ve committed on this branch and pull in new commits. Instead of creating a merge conflict, you can use git pull --rebase.
To test git pull --rebase, let’s create another clone of the same repository, make a new commit, and git push it. This will let you download new changes with git pull --rebase on the original remote repository.
Here are the steps to create another cloned, local repository and push a commit from it:
1 Change to the directory where you want the new GitInPracticeRedux reposi-tory to be created: for example, cd /Users/mike/ to create the new local repos-itory in /Users/mike/GitInPracticeReduxPullTest.
Figure 6.7 Interactively rebased inspiration branch
2 Run git clone https://github.com/GitInPractice/GitInPracticeRedux .git GitInPracticeReduxPullTest to clone into the GitInPracticeRedux-PullTest directory.
3 Change directory to the new Git repository: in my case, cd /Users/mike/Git-InPracticeReduxPullTest/.
4 Modify the 00-Preface.asciidoc file.
5 Run git commit --message 'Preface: Sequel not prequel.' 00-Preface.asciidoc.
6 Run git push.
Now let’s create a commit in your main, local repository:
1 Change to the directory containing your repository: for example, cd /Users/mike/GitInPracticeRedux/.
2 Run git checkout master.
3 Edit 02-AdvancedGitInPractice.asciidoc, and make a change to the file.
4 Run git commit --message="Chapter 2: only one chapter." 02-Advanced-GitInPractice.asciidoc. The output should resemble the following.
# git commit --message="Chapter 2: only one chapter."
02-AdvancedGitInPractice.asciidoc
[master 357d7db] Chapter 2: only one chapter.
1 file changed, 1 insertion(+), 1 deletion(-)
Figure 6.8 shows the state of the master branch before the git pull --rebase opera-tion. Now let’s perform a pull with a rebase.
Problem
You want to pull commits from origin/master and rebase your current commits in master on top of the upstream changes.
Solution
1 Change to the directory containing your repository: for example, cd /Users/mike/GitInPracticeRedux/.
2 Run git pull --rebase. The output should resemble the following.
Listing 6.12 Output: commit to be reset
Figure 6.8 Commit before pull rebase
119 TECHNIQUE 46 Rewriting history on a remote branch: git push --force
# git pull --rebase
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done.
From https://github.com/GitInPractice/GitInPracticeRedux 4455fa9..ae54679 master -> origin/master
First, rewinding head to replay your work on top of it...
Applying: Chapter 2: only one chapter.
Recall that git pull is equivalent to running git fetch && git merge, and git pull --rebase is equivalent to running git fetch && git rebase.
B
shows the output of the fetch operation. This is the same as if you’d run git fetch.C
shows the output of the successful rebase operation. The one commit that had already been made on your local master branch is rebased on top of the latest commit in the origin/master remote branch. This is the same as if you had run git rebase origin/master after git fetch.Figure 6.9 shows the state of the master branch after the git pull --rebase opera-tion. You can see that there’s a new commit from origin/master (ae54679) and that the previous top commit on the local master branch has been rebased on top of this and has a new SHA-1 (27f2d8b). This works identically if there are multiple commits that need to be rebased.
You’ve pulled with a rebase. Now git push to send these commits upstream.
Discussion
git pull --rebase is sometimes recommended as a sensible default to use instead of git pull. You’ll rarely want to create a merge commit on a git pull operation, so using git pull --rebase guarantees that this won’t happen. This means when you do push this branch, it will have a simpler, cleaner history. Once you understand how to rebase and solve conflicts, I recommend using git pull --rebase by default.
Technique 46 Rewriting history on a remote branch: git push - -force
If you modify history on a branch and then try to perform a git push operation, it will fail. This is to stop you from accidentally writing remote history that other users are relying on. It’s possible to do this, but you need to be more explicit in your syntax to indicate that you’re aware you’re performing a dangerous operation.
Listing 6.13 Output: rebase pull
Fetch output
B
Rebase output
C
Figure 6.9 Commit after pull rebase
Let’s try to rebase the inspiration branch again and push it:
1 Change to the directory containing your repository: for example, cd /Users/mike/GitInPracticeRedux/.
2 Run git checkout inspiration.
3 Run git push to ensure that all the changes are up to date.
4 Run git rebase v0.1-release.
5 Run git push again. The output should resemble the following.
# git push
To https://github.com/GitInPractice/GitInPracticeRedux.git
! [rejected] inspiration -> inspiration (non-fast-forward) error: failed to push some refs to
'https://github.com/GitInPractice/GitInPracticeRedux.git' hint: Updates were rejected because the tip of your current branch
is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
B
shows the local inspiration branch that you attempted to push to the remote inspiration branch. Unfortunately, this request was rejected because it was a non-fast-forward—it wouldn’t be advancing the current history but instead rewriting it.C
shows the error message from git push. It fails because the branch you’re pushing lacks changes from the branches you’re pushing to. This is because it’s not easily possible for the remote repository to know whether you have commits on that branch that you need to fetch before pushing or whether you’ve modified the existing history of a branch.Instead, let’s learn how to force this push operation to rewrite the history on this remote branch.
Problem
You wish to rewrite the history on the remote origin/inspiration branch based on the contents of the local inspiration branch.
Solution
1 Change to the directory containing your repository: for example, cd /Users/mike/GitInPracticeRedux/.
2 Run git checkout inspiration.
3 Run git pull --rebase.
4 Run git push origin +inspiration again. The output should resemble the following.
Listing 6.14 Output: rewritten history push failure
C
Local/remote branches
Push failure
B
121 TECHNIQUE 47 Rewriting the entire history of a branch: git filter-branch
# git push origin +inspiration Counting objects: 1, done.
Writing objects: 100% (1/1), 204 bytes | 0 bytes/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To https://github.com/GitInPractice/GitInPracticeRedux.git + 0109344...ca74d2b inspiration -> inspiration (forced update)
B
shows the git push output as usual but with a (forced update) indicating that it was forced to allow non-fast-forwards on the remote.You’ve rewritten the history on a remote branch.
Discussion
You can also use git push --force instead of specifying the remote branch name pre-fixed with +, but this is not advised because it’s less safe; depending on your Git config-uration, you could accidentally force-push multiple branches at once. By default in some Git versions (which I tell you how to change later in section 7.1.2), a push will push all branches with matching local and remote branch names, so these will all be force-pushed if git push --force is run without parameters.
Remember that the reflog isn’t pushed remotely, so if you unintentionally rewrite history on the remote branch, there’s no way to recover commits you didn’t have locally without direct access to the Git repository on the server. For this reason, you should be careful when rewriting remote branches. A good rule of thumb is to only ever do it on branches that nobody else is using. Avoid doing it on shared branches, and never do it on the master branch. Also, do a git pull immediately before any forced push to try to ensure that you aren’t rewriting commits on the remote branch that you don’t have locally. Of course, it’s still possible for someone to push a commit just after you do a git pull, which you overwrite (which is why git push --force is a dangerous operation).