This Git repository gives you examples on how you can train/experiment with Git's rebasing feature. The CLI part shows some additional options besides rebasing to train/experiment with keeping your git history clean.
You can train following things here:
Sourcetree:
- Rebase
- Interactive rebase (squashing and other options)
- Commit amend
- Pushing rebased changes
CLI:
- Rebase
- Interactive rebase (squashing and other options)
- Rebase options like continue, skip and abort.
- Git reset
- Commit amend
- Pushing and pulling rebased changes
Follow these instructions to clone the repository into a clean state where you can train:
$ cd <an-empty-dir>
$ git clone -b rebase/master https://github.com/LensArt/rebase-training.git .
$ git checkout --track origin/rebase/feature/multiply
Note: If something went wrong, you can delete the impacted branch and download it again from the remote or you can delete the whole directory and re-clone the repository to try again.
Rebasing allows you to 'rebase' your changes on top of another branch or on top of new changes pushed to the branch you branched off.
- Open the "Log / History" tab
- Checkout source branch (
rebase/feature/multiply
) - Right-click head of target branch (
rebase/master
) and select "Rebase..." - Resolve conflict via the "File Status" tab (Right-click on
Program.cs
and chose "Resolve Conflict") - In the menu choose "Action" -> "Continue Rebase"
Interactive rebasing allows you to squash, delete and reorder commits before rebasing them onto a different branch.
Unfortunately, Sourcetree contains an ancient bug; so this feature doesn't fully work.
My current workaround: First do a regular rebase, then do the interactive rebase.
- Rebase source branch (
rebase/feature/multiply
) on target branch (rebase/master
) - Open the "Log / History" tab
- Right-click the parent of oldest commit you want to rebase interactively (here
5c3d0ea
) and select "Rebase children of xxx interactively..." - Modify the commits as desired (check out this video for all the options)
Sometimes you want to add a fix to your last commit, in this case you can amend the changes to your commit.
- Stage your changes in sourcetree
- Select in the
commit options
dropdown (right upper corner above the commit message entry)Amend latest commit
. - It will show you a popup if you want to replace you commit message, if you select
Yes
it will copy the commit message from the last commit in the commit message entry box. - You can edit this message if desired and commit your changes.
To push your changes see below how to force push
Once you're done you (probably) need to push your changes back to origin
. Normally Git doesn't allow you to push these kinds of changes.
Instead you need to "force push" them; ideally by using --force-with-lease
instead of --force
. You can read about the differences between those two in this article.
To be able to do a "force push" with Sourcetree, this needs to be enabled in the options.
In the menu, go to "Tools" -> "Options" -> "Git" and check "Enable Force Push". Make sure to also check "Use Safe Force Push".
Then you can select "Force Push" in "Push" dialog. (No matter the name, this will automatically use --force-with-lease
instead of --force
when "Use Safe Force Push" is enabled in the options.)
Rebasing allows you to 'rebase' your changes on top of another branch or on top of new changes pushed to the branch you branched off.
- Checkout source branch (
git checkout rebase/feature/multiply
) - Rebase head of target branch (
git rebase rebase/master
) - You can optionally show the conflict in cli (
git diff
) enterq
to exit - Resolve conflict in your favorite text editor
- To see the beginning of the merge conflict in your file, search the file for the conflict marker
<<<<<<<
. When you open the file in your text editor, you'll see the changes from the HEAD or base branch after the line<<<<<<< HEAD
. Next, you'll see=======
, which divides your changes from the changes in the other branch, followed by>>>>>>> BRANCH-NAME
. In this tutorial, one person wrote "case Operator.Subtract:...
" in the base or HEAD branch and another person wrote "case Operator.Multiply:...
" in the compare branch orrebase/feature/multiply
. In some cases you want to keep both changes, in other cases you want to replace one of the 2 changes. If you want to keep both changes, keep an eye on the line before and after the conflict to see if you need to copy those to complete the conflict fix.
++<<<<<<< HEAD + case Operator.Subtract: + result = RunSubtract(operand1, operand2); ++======= + case Operator.Multiply: + result = RunMultiply(operand1, operand2); ++>>>>>>> 5062359 (Feature: Implemented multiply operation)
- To see the beginning of the merge conflict in your file, search the file for the conflict marker
- Add the fixed file (
git add .
) - Continue the rebase (
git rebase --continue
)- This will show a vim screen with the commit message, you can change the message if needed. You can quit and continue with
:q
. Look for basic vim explanation here.
- This will show a vim screen with the commit message, you can change the message if needed. You can quit and continue with
Interactive rebasing allows you to squash, delete and reorder commits before rebasing them onto a different branch.
- Checkout source branch (
git checkout rebase/feature/multiply
) - Rebase interactively head of target branch (
git rebase rebase/master -i
)- This will show a vim screen with the options and commit messages
- You can change the action if needed in front of the commit.
- For instance you can change an action default
pick
tof
: start edit mode in vim by pressingi
removepick
from the line and enterf
, next pressescape
to stop the edit mode in vim.
- For instance you can change an action default
- You can quit and write to continue with
:wq
. - Look for basic vim explanation here.
Sometimes you have branched off a branch which has new or amended changes to commits which you also have in your branch. If you rebase on the new changes of this branch you will see these new or amended as conflicts in the rebasing process. In that case you can skip the current commit to continue the rebase.
- Checkout source branch (
git checkout rebase/feature/refactor
) - Rebase head of target branch (
git rebase rebase/feature/multiply
) - You will see a merge conflict on a commit which has been fixed in the
rebase/feature/multiply
branch, if you would fix the merge conflict you would end up with an empty diff. This is wheregit rebase --skip
comes into play. With the skip command you will skip (basically drop) this commit from the branch you are rebasing and continue with the next commits on your branch. Since the commit is already part of the branchrebase/feature/multiply
you don't need it anymore in your branch (rebase/feature/refactor
).
In order to stop an ongoing rebase process you can use git rebase --abort
to go back to the original state.
Sometimes it can be interesting to use git reset ..
this undoes changes. It will remove the commits from history (or better stated point the HEAD and branch ref to a specified commit) and restore all changes to your local system showing them in your diff.
- Checkout source branch (
git checkout rebase/feature/multiply
) - Undo last commit by using
git reset d0b2d6f
this will show the last committed files in the diff as unstaged changes. You can do some changes to the unstaged files, stage them and commit. But for now we won't. - Undo 2nd last commit by using
git reset 20294f3
. As in our case the first commit undoes the 2nd, it will leave the diff empty and show you can pull 2 commits from the remote. You could force push to remove both commits from the history.
In real world scenarios most likely you wouldn't end up with an empty diff, but this git reset ..
option gives you the ability to change your commits or revert or edit some committed changes.
Optionally you can add the --hard
option to also remove the staged changes from the undone commits.
You can also reset a single or multiple file(s) (space separated) in a commit, doing this will leave the rest of the commit intact, and just reset the specified file(s). You can then revert the changes to this file or change the file and amend to the existing commit.
- Checkout source branch (
git checkout rebase/feature/refactor
) - Reset
Program.cs
git reset 20294f3 Calculator\Calculator\Program.cs
- You will see
Program.cs
both as staged and unstaged file. The staged part reflects the reverted changes compared to what is in the commit and the unstaged part reflects the changes which were committed. If you would stage the unstaged changes unedited you will end up with an empty diff. If you would edit the file you would see those changes as diff if you stage them.
Sometimes you want to add a fix to your last commit, in this case you can amend the changes to your commit.
- Use
git commit -a --amend
to stage and amend all your changes to your last commit. - This will show a vim screen with the option to change the commit message.
To push your changes see below how to force push
Once you're done you (probably) need to push your changes back to origin
. Normally Git doesn't allow you to push these kinds of changes.
Instead you need to "force push" them; ideally by using --force-with-lease
instead of --force
. You can read about the differences between those two in this article.
So to push the changes to origin
you can either use git push -f
to force push or better git push --force-with-lease
to force push with lease.
To pull changes which were force pushed by someone else you can use git pull --rebase
.
A quick summary of the git commands and what they do:
Git rebase
git rebase [branch_or_commit]
rebase the current branch on the branch in the command.git rebase [branch_or_commit] -i
interactively rebase the current branch on the branch in the command.git rebase --continue
commit fixes on current commit and continue to the next commit on the branch which is being rebased.git rebase --skip
drops the commit from the branch which is being rebased.git rebase --abort
stop an ongoing rebase.
Git reset
git reset [branch_or_commit]
undo commits and point the HEAD and branch ref to the specified branch/commit.git reset [branch_or_commit] --hard
undo commits and point the HEAD and branch ref to the specified branch/commit and clear all staged changes.git reset [branch_or_commit] [file_path(s)]
undo changes to a specific file and leave the rest of the commit intact.
Commit amend
git commit -a --amend
stage and amend (add) all files to the previous commit.
Pushing and pulling changes
git push --force-with-lease
force push with lease (check if remote has no new changes)git push -f
force pushgit pull --rebase
pull rebased changes from remote
possible other helpful commands
git log
view commit history of the current branch.git status
check local repo status and show modified files.git diff
shows diff of what is changed but not staged.git commit -am ""
stage and commit all files with specified commit message.
- https://www.atlassian.com/git/tutorials/using-branches/merge-conflicts
- https://www.atlassian.com/git/tutorials/merging-vs-rebasing
- https://www.atlassian.com/git/tutorials/advanced-overview
- https://www.atlassian.com/git/tutorials/undoing-changes/git-reset
- https://levelup.gitconnected.com/top-30-git-commands-you-should-know-to-master-git-cli-f04e041779bc