Sometimes, after several pieces of committing, you realized that some of that commits need to be modified. Following is how to change the history of git repository.
Change specific commit
Change the last commit
1
git commit --amend
The above command will modify the previous commit.
Split apart the last commit
In order to split apart the most recently commited commit, use following command:
1
git reset HEAD~
As illustrated in notes on git part 1, the above
command will reset the staged area as the commit ahead of the last commit. Now use git add/rm
to stage files,
and make commits.
Use the old commit message
Sometimes you need to reuse the commit message from some commits, i.e. keep the Change-ID. Then use following command:
1
2
3
4
5
6
7
git commit --reuse-message=HEAD@{1} # reuse the commit message of HEAD@{1} directly
git commit -C <commit> # short version of above command
git commit --reedit-message=<commit> # reuse and edit the commit message of <commit>
git commit -c <commit> # short version of above command
git commit --reset-author -c <commit> # change author only
NOTICE:
HEAD@{}
is a notation for capturing the history of HEAD
movement, and HEAD@{1}
references
the place from where you jumped to the new commit location. SEE notes on git part 1.
In order to reuse the original commit message for a certain commit, as in the process splitting apart commits, use following command:
1
git commit -c ORIG_HEAD
Modify specific commits
What if the commit I want to rework is not the last commit? Use git rebase -i
as:
1
git rebase -i HEAD~n
where, n
denotes how many commits back it is. And in the prompt, reorder the commits and select proper flags.
For example, if you want to modify the third last commit, use git rebase -i HEAD~3
, pick edit
option on
that commit.
And if you want to split apart that commit, do git reset HEAD~
, and make new commits as you want.
And if you want to test what you’re committing, use git stash
to hide away the part that hasn’t been
committed (or git stash --keep-index
before committing it), test, then git stash pop
to return
the rest to the work tree.
After you managed each history commits that need a rework, having a clean work tree on each commit point, use the following command to proceed rebasing:
1
git rebase --continue
After you managed all the to-be-edit commits, the history has been rewrote.
NOTICE:
explanation on HEAD~n
and HEAD^n
.
HEAD^
means the first parent of the tip of the current branch, as git commits can have more than one
parent.
HEAD~
is for moving back through generations, favoring the first parent in cases of ambiguity.
These specifiers can be chained arbitrarily , e.g., topic~3^2.
So, ~
is fuzzy while ^
is precise.
Change a branch of commits in a common way
This part is mostly copied from https://git-scm.com
Remove a file from every commit
1
git filter-branch --tree-filter 'rm -f <to-be-removed-file>' HEAD
To run filter-branch
on all branches, pass --all
to the command.
Make a subdirectory as the new Root
1
git filter-branch --subdirectory-filter <sub-directory> HEAD
Now the new project root is what was in the <sub-directory>
each time. Git will also automatically
remove commits that did not affect the <sub-directory>
.
Change Email address globally
1
git filter-branch --commit-filter 'if [ "$GIT_AUTHOR_EMAIL" = "<old-email-addr>" ]; then GIT_AUTHOR_NAME="<author-name>"; GIT_AUTHOR_EMAIL="<email-addr>"; git commit-tree "$@"; else git commit-tree "$@"; fi' HEAD
This goes through and rewrites every commit to have the new <email-addr>
. Because commits contain
the SHA-1 values of their parents, this command changes every commit SHA-1 in history, not just
those that have the matching email address.