Splitting up commits makes the process of reviewing often easier, since you can create several merge requests or review every commit one by one.
So when you find out that you have portions of the code that you initially didn't intend to change or when you do some refactoring along the current changes, you can use one of the following processes to split up the changes into several commits in a logical order:
#1 Splitting up the last n commits into m commits
#2 Adding changes to a previous commit
2.1 While adding new changes
2.2 Selectively reverting changes
#3 Splitting up previous commits (by moving changes or creating new commits)
3.1 Splitting up
3.2 Adding to later commits
#4 Reordering commits
#1 Splitting up the last n
commits into m
commits
If you decide to split up the n
commits after you have finished working on a feature.
- This process first reset all changes to the index and then removes changes from the index.
- See the box below if you want to add from the working directory to the index.
Step by step:
- Use
git reset --soft HEAD~n
(orgit reset --soft <commit hash>
) - Then remove file selectively from the index by using
git reset -- <file path>
.
2.1 If you need to add specific changes selectively again you can usegit add -p
- Then only commit the staged files
- Afterwards add the unstaged files to next commit
- Continue step 4 until you are finished
Only adding changes to the index instead of removing from the index
- Depending on the number of commits and the amount of changes, it might be easier to only add changes from the working directory.
- Then use
git reset HEAD~n
in step 1 and then only add the desired changes for every commit in step 2 instead of removing selectively.
#2 Adding new changes to a previous commit
2.1 While adding new changes
- In this scenario git fixup comes in handy.
- This is mostly used when you are working on a branch and make a new commits which belongs to a previous commit.
- If the previous commit is the last commit, you can also use
git commit --amend
- This is not recommended when the commit has already been pushed to a remote branch where other developers might have pulled in the meantime.
2.2 Selectively reverting changes
You can also use git fixup to selectively revert previous changes and add them to another commit
- You can also use this process reset changes from a specific commit of a file and then add them into a fixup commit by using
git reset <commit hash> <file path>
. - Note that you have to reset to a commit before the commit were the change was introduced.
- Also note that you can selectively undo commits by using
git reset --patch <commit hash> <pathspec>...
- If you use reset, you will need two fixup commits:
- one that reverts the version of the reset commit
- one which adds the changes to another commit
Let's say you have three commits a -> b -> c
and want to reset changes of commit b
and apply them to commit c
- Use
git log --oneline
to get the required<hash of commit a>
- Use git
git reset
:
git reset <hash of commit a> app/assets/stylesheets/pagination.sass
git status
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: app/assets/stylesheets/pagination.sass
Untracked files:
(use "git add <file>..." to include in what will be committed)
app/assets/stylesheets/pagination.sass
- Now use
fixup
to add the commit to delete the file tob
- Now add the untracked files (or unstaged changes) to the index with
git add
- Now use
fixup
to add the the changes to commitc
git rebase -i --autosquash master
#3 Splitting up previous commits (by moving changes or creating new commits)
Even though the process in #1 could be used for this it can become quite tedious if you have a large commit and might have already done this and just want to refine some changes afterwards.
- Then this process is useful if you want to go through several commits and might want to apply changes from a previous commit to a later commit or ad d a new commit
- Note that you could also use this to reorder the commits at the same time with #4.
3.1 Splitting up
If you just want to split up the last commit into several commits just follow the process in "How to split up a git commit"
3.2 Adding to later commits
- Use
git rebase -i
- Mark all commits you want to edit with the flag
e
- Follow the process in #1
- Redo the commit with the same commit message as before
- Add the unstaged files now to the stash with
git stash
- Use
git rebase --continue
to edit the next commit - Follow #1 again
7.1 Optionally pop the stashed changes to the commit where you want to apply them withgit stash pop
7.2 You can also do this at the end if you want to add them into a new commit
#4 Reordering commits
If you are finished with splitting up, reordering the commits may give them a more logical appearance, especially if the changes depend on each other. Then:
Use git rebase -i HEAD~n
for n
being the number of commits to reorder.
- This will open the editor of your choice with the oldest commits being at the top and the newest at the bottom
- You can now reorder the lines as you wish, save and exit the editor.
Vim shortcuts to reorder
If you use vim these shortcuts will come in handy
ddp
will move current line downdd
, moving the cursor,p
will paste the line at the current cursor position