On this question there are a lot of answers, but none of them explains in super detail how to change older commit messages using Vim. I was stuck trying to do this myself, so here I’ll write down in detail how I did this especially for people who have no experience in Vim!
I wanted to change my five latest commits that I already pushed to the server. This is quite ‘dangerous’ because if someone else already pulled from this, you can mess things up by changing the commit messages. However, when you’re working on your own little branch and are sure no one pulled it you can change it like this:
Let’s say you want to change your five latest commits, and then you type this in the terminal:
git rebase -i HEAD~5
*Where 5 is the number of commit messages you want to change (so if you want to change the 10th to last commit, you type in 10).
This command will get you into Vim there you can ‘edit’ your commit history. You’ll see your last five commits at the top like this:
pick <commit hash> commit message
Instead of pick
you need to write reword
. You can do this in Vim by typing in i
. That makes you go in to insert mode. (You see that you’re in insert mode by the word INSERT at the bottom.) For the commits you want to change, type in reword
instead of pick
.
Then you need to save and quit this screen. You do that by first going in to ‘command-mode’ by pressing the Escbutton (you can check that you’re in command-mode if the word INSERT at the bottom has disappeared). Then you can type in a command by typing :
. The command to save and quit is wq
. So if you type in :wq
you’re on the right track.
Then Vim will go over every commit message you want to reword, and here you can actually change the commit messages. You’ll do this by going into insert mode, changing the commit message, going into the command-mode, and save and quit. Do this five times and you’re out of Vim!
Then, if you already pushed your wrong commits, you need to git push --force
to overwrite them. Remember that git push --force
is quite a dangerous thing to do, so make sure that no one pulled from the server since you pushed your wrong commits!
Now you have changed your commit messages!
(As you see, I’m not that experienced in Vim, so if I used the wrong ‘lingo’ to explain what’s happening, feel free to correct me!)
If a commit message contains unclear, incorrect, or sensitive information, you can amend it locally and push a new commit with a new message to GitHub. You can also change a commit message to add missing information.
Rewriting the most recent commit message
You can change the most recent commit message using the git commit --amend
command.
In Git, the text of the commit message is part of the commit. Changing the commit message will change the commit ID—i.e., the SHA1 checksum that names the commit. Effectively, you are creating a new commit that replaces the old one.
Commit has not been pushed online
If the commit only exists in your local repository and has not been pushed to GitHub.com, you can amend the commit message with the git commit --amend
command.
-
On the command line, navigate to the repository that contains the commit you want to amend.
-
Type
git commit --amend
and press Enter. -
In your text editor, edit the commit message, and save the commit.
-
You can add a co-author by adding a trailer to the commit. For more information, see «Creating a commit with multiple authors.»
-
You can create commits on behalf of your organization by adding a trailer to the commit. For more information, see «Creating a commit on behalf of an organization»
-
The new commit and message will appear on GitHub.com the next time you push.
You can change the default text editor for Git by changing the core.editor
setting. For more information, see «Basic Client Configuration» in the Git manual.
Amending older or multiple commit messages
If you have already pushed the commit to GitHub.com, you will have to force push a commit with an amended message.
We strongly discourage force pushing, since this changes the history of your repository. If you force push, people who have already cloned your repository will have to manually fix their local history. For more information, see «Recovering from upstream rebase» in the Git manual.
Changing the message of the most recently pushed commit
- Follow the steps above to amend the commit message.
- Use the
push --force-with-lease
command to force push over the old commit.$ git push --force-with-lease origin EXAMPLE-BRANCH
Changing the message of older or multiple commit messages
If you need to amend the message for multiple commits or an older commit, you can use interactive rebase, then force push to change the commit history.
-
On the command line, navigate to the repository that contains the commit you want to amend.
-
Use the
git rebase -i HEAD~n
command to display a list of the lastn
commits in your default text editor.# Displays a list of the last 3 commits on the current branch $ git rebase -i HEAD~3
The list will look similar to the following:
pick e499d89 Delete CNAME pick 0c39034 Better README pick f7fde4a Change the commit message but push the same commit. # Rebase 9fdb3bd..f7fde4a onto 9fdb3bd # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
-
Replace
pick
withreword
before each commit message you want to change.pick e499d89 Delete CNAME reword 0c39034 Better README reword f7fde4a Change the commit message but push the same commit.
-
Save and close the commit list file.
-
In each resulting commit file, type the new commit message, save the file, and close it.
-
When you’re ready to push your changes to GitHub, use the push —force command to force push over the old commit.
$ git push --force origin EXAMPLE-BRANCH
For more information on interactive rebase, see «Interactive mode» in the Git manual.
As before, amending the commit message will result in a new commit with a new ID. However, in this case, every commit that follows the amended commit will also get a new ID because each commit also contains the id of its parent.
If you have included sensitive information in a commit message, force pushing a commit with an amended commit may not remove the original commit from GitHub. The old commit will not be a part of a subsequent clone; however, it may still be cached on GitHub and accessible via the commit ID. You must contact GitHub Support with the old commit ID to have it purged from the remote repository.
Further reading
- «Signing commits»
On this question there are a lot of answers, but none of them explains in super detail how to change older commit messages using Vim. I was stuck trying to do this myself, so here I’ll write down in detail how I did this especially for people who have no experience in Vim!
I wanted to change my five latest commits that I already pushed to the server. This is quite ‘dangerous’ because if someone else already pulled from this, you can mess things up by changing the commit messages. However, when you’re working on your own little branch and are sure no one pulled it you can change it like this:
Let’s say you want to change your five latest commits, and then you type this in the terminal:
git rebase -i HEAD~5
*Where 5 is the number of commit messages you want to change (so if you want to change the 10th to last commit, you type in 10).
This command will get you into Vim there you can ‘edit’ your commit history. You’ll see your last five commits at the top like this:
pick <commit hash> commit message
Instead of pick
you need to write reword
. You can do this in Vim by typing in i
. That makes you go in to insert mode. (You see that you’re in insert mode by the word INSERT at the bottom.) For the commits you want to change, type in reword
instead of pick
.
Then you need to save and quit this screen. You do that by first going in to ‘command-mode’ by pressing the Escbutton (you can check that you’re in command-mode if the word INSERT at the bottom has disappeared). Then you can type in a command by typing :
. The command to save and quit is wq
. So if you type in :wq
you’re on the right track.
Then Vim will go over every commit message you want to reword, and here you can actually change the commit messages. You’ll do this by going into insert mode, changing the commit message, going into the command-mode, and save and quit. Do this five times and you’re out of Vim!
Then, if you already pushed your wrong commits, you need to git push --force
to overwrite them. Remember that git push --force
is quite a dangerous thing to do, so make sure that no one pulled from the server since you pushed your wrong commits!
Now you have changed your commit messages!
(As you see, I’m not that experienced in Vim, so if I used the wrong ‘lingo’ to explain what’s happening, feel free to correct me!)
Many times, when working with Git, you may want to revise your local commit history.
One of the great things about Git is that it allows you to make decisions at the last possible moment.
You can decide what files go into which commits right before you commit with the staging area, you can decide that you didn’t mean to be working on something yet with git stash
, and you can rewrite commits that already happened so they look like they happened in a different way.
This can involve changing the order of the commits, changing messages or modifying files in a commit, squashing together or splitting apart commits, or removing commits entirely — all before you share your work with others.
In this section, you’ll see how to accomplish these tasks so that you can make your commit history look the way you want before you share it with others.
Note |
Don’t push your work until you’re happy with it One of the cardinal rules of Git is that, since so much work is local within your clone, you have a great deal of freedom to rewrite your history locally. |
Changing the Last Commit
Changing your most recent commit is probably the most common rewriting of history that you’ll do.
You’ll often want to do two basic things to your last commit: simply change the commit message, or change the actual content of the commit by adding, removing and modifying files.
If you simply want to modify your last commit message, that’s easy:
The command above loads the previous commit message into an editor session, where you can make changes to the message, save those changes and exit.
When you save and close the editor, the editor writes a new commit containing that updated commit message and makes it your new last commit.
If, on the other hand, you want to change the actual content of your last commit, the process works basically the same way — first make the changes you think you forgot, stage those changes, and the subsequent git commit --amend
replaces that last commit with your new, improved commit.
You need to be careful with this technique because amending changes the SHA-1 of the commit.
It’s like a very small rebase — don’t amend your last commit if you’ve already pushed it.
Tip |
An amended commit may (or may not) need an amended commit message When you amend a commit, you have the opportunity to change both the commit message and the content of the commit. On the other hand, if your amendments are suitably trivial (fixing a silly typo or adding a file you forgot to stage) such that the earlier commit message is just fine, you can simply make the changes, stage them, and avoid the unnecessary editor session entirely with:
|
Changing Multiple Commit Messages
To modify a commit that is farther back in your history, you must move to more complex tools.
Git doesn’t have a modify-history tool, but you can use the rebase tool to rebase a series of commits onto the HEAD that they were originally based on instead of moving them to another one.
With the interactive rebase tool, you can then stop after each commit you want to modify and change the message, add files, or do whatever you wish.
You can run rebase interactively by adding the -i
option to git rebase
.
You must indicate how far back you want to rewrite commits by telling the command which commit to rebase onto.
For example, if you want to change the last three commit messages, or any of the commit messages in that group, you supply as an argument to git rebase -i
the parent of the last commit you want to edit, which is HEAD~2^
or HEAD~3
.
It may be easier to remember the ~3
because you’re trying to edit the last three commits, but keep in mind that you’re actually designating four commits ago, the parent of the last commit you want to edit:
Remember again that this is a rebasing command — every commit in the range HEAD~3..HEAD
with a changed message and all of its descendants will be rewritten.
Don’t include any commit you’ve already pushed to a central server — doing so will confuse other developers by providing an alternate version of the same change.
Running this command gives you a list of commits in your text editor that looks something like this:
pick f7f3f6d Change my name a bit
pick 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
It’s important to note that these commits are listed in the opposite order than you normally see them using the log
command.
If you run a log
, you see something like this:
$ git log --pretty=format:"%h %s" HEAD~3..HEAD
a5f4a0d Add cat-file
310154e Update README formatting and add blame
f7f3f6d Change my name a bit
Notice the reverse order.
The interactive rebase gives you a script that it’s going to run.
It will start at the commit you specify on the command line (HEAD~3
) and replay the changes introduced in each of these commits from top to bottom.
It lists the oldest at the top, rather than the newest, because that’s the first one it will replay.
You need to edit the script so that it stops at the commit you want to edit.
To do so, change the word “pick” to the word “edit” for each of the commits you want the script to stop after.
For example, to modify only the third commit message, you change the file to look like this:
edit f7f3f6d Change my name a bit
pick 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
When you save and exit the editor, Git rewinds you back to the last commit in that list and drops you on the command line with the following message:
$ git rebase -i HEAD~3
Stopped at f7f3f6d... Change my name a bit
You can amend the commit now, with
git commit --amend
Once you're satisfied with your changes, run
git rebase --continue
These instructions tell you exactly what to do.
Type:
Change the commit message, and exit the editor.
Then, run:
This command will apply the other two commits automatically, and then you’re done.
If you change pick to edit on more lines, you can repeat these steps for each commit you change to edit.
Each time, Git will stop, let you amend the commit, and continue when you’re finished.
Reordering Commits
You can also use interactive rebases to reorder or remove commits entirely.
If you want to remove the “Add cat-file” commit and change the order in which the other two commits are introduced, you can change the rebase script from this:
pick f7f3f6d Change my name a bit
pick 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
to this:
pick 310154e Update README formatting and add blame
pick f7f3f6d Change my name a bit
When you save and exit the editor, Git rewinds your branch to the parent of these commits, applies 310154e
and then f7f3f6d
, and then stops.
You effectively change the order of those commits and remove the “Add cat-file” commit completely.
Squashing Commits
It’s also possible to take a series of commits and squash them down into a single commit with the interactive rebasing tool.
The script puts helpful instructions in the rebase message:
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
If, instead of “pick” or “edit”, you specify “squash”, Git applies both that change and the change directly before it and makes you merge the commit messages together.
So, if you want to make a single commit from these three commits, you make the script look like this:
pick f7f3f6d Change my name a bit
squash 310154e Update README formatting and add blame
squash a5f4a0d Add cat-file
When you save and exit the editor, Git applies all three changes and then puts you back into the editor to merge the three commit messages:
# This is a combination of 3 commits.
# The first commit's message is:
Change my name a bit
# This is the 2nd commit message:
Update README formatting and add blame
# This is the 3rd commit message:
Add cat-file
When you save that, you have a single commit that introduces the changes of all three previous commits.
Splitting a Commit
Splitting a commit undoes a commit and then partially stages and commits as many times as commits you want to end up with.
For example, suppose you want to split the middle commit of your three commits.
Instead of “Update README formatting and add blame”, you want to split it into two commits: “Update README formatting” for the first, and “Add blame” for the second.
You can do that in the rebase -i
script by changing the instruction on the commit you want to split to “edit”:
pick f7f3f6d Change my name a bit
edit 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
Then, when the script drops you to the command line, you reset that commit, take the changes that have been reset, and create multiple commits out of them.
When you save and exit the editor, Git rewinds to the parent of the first commit in your list, applies the first commit (f7f3f6d
), applies the second (310154e
), and drops you to the console.
There, you can do a mixed reset of that commit with git reset HEAD^
, which effectively undoes that commit and leaves the modified files unstaged.
Now you can stage and commit files until you have several commits, and run git rebase --continue
when you’re done:
$ git reset HEAD^
$ git add README
$ git commit -m 'Update README formatting'
$ git add lib/simplegit.rb
$ git commit -m 'Add blame'
$ git rebase --continue
Git applies the last commit (a5f4a0d
) in the script, and your history looks like this:
$ git log -4 --pretty=format:"%h %s"
1c002dd Add cat-file
9b29157 Add blame
35cfb2b Update README formatting
f7f3f6d Change my name a bit
This changes the SHA-1s of the three most recent commits in your list, so make sure no changed commit shows up in that list that you’ve already pushed to a shared repository.
Notice that the last commit (f7f3f6d
) in the list is unchanged.
Despite this commit being shown in the script, because it was marked as “pick” and was applied prior to any rebase changes, Git leaves the commit unmodified.
Deleting a commit
If you want to get rid of a commit, you can delete it using the rebase -i
script.
In the list of commits, put the word “drop” before the commit you want to delete (or just delete that line from the rebase script):
pick 461cb2a This commit is OK
drop 5aecc10 This commit is broken
Because of the way Git builds commit objects, deleting or altering a commit will cause the rewriting of all the commits that follow it.
The further back in your repo’s history you go, the more commits will need to be recreated.
This can cause lots of merge conflicts if you have many commits later in the sequence that depend on the one you just deleted.
If you get partway through a rebase like this and decide it’s not a good idea, you can always stop.
Type git rebase --abort
, and your repo will be returned to the state it was in before you started the rebase.
If you finish a rebase and decide it’s not what you want, you can use git reflog
to recover an earlier version of your branch.
See Data Recovery for more information on the reflog
command.
Note |
Drew DeVault made a practical hands-on guide with exercises to learn how to use |
The Nuclear Option: filter-branch
There is another history-rewriting option that you can use if you need to rewrite a larger number of commits in some scriptable way — for instance, changing your email address globally or removing a file from every commit.
The command is filter-branch
, and it can rewrite huge swaths of your history, so you probably shouldn’t use it unless your project isn’t yet public and other people haven’t based work off the commits you’re about to rewrite.
However, it can be very useful.
You’ll learn a few of the common uses so you can get an idea of some of the things it’s capable of.
Caution |
|
Removing a File from Every Commit
This occurs fairly commonly.
Someone accidentally commits a huge binary file with a thoughtless git add .
, and you want to remove it everywhere.
Perhaps you accidentally committed a file that contained a password, and you want to make your project open source.
filter-branch
is the tool you probably want to use to scrub your entire history.
To remove a file named passwords.txt
from your entire history, you can use the --tree-filter
option to filter-branch
:
$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
Ref 'refs/heads/master' was rewritten
The --tree-filter
option runs the specified command after each checkout of the project and then recommits the results.
In this case, you remove a file called passwords.txt
from every snapshot, whether it exists or not.
If you want to remove all accidentally committed editor backup files, you can run something like git filter-branch --tree-filter 'rm -f *~' HEAD
.
You’ll be able to watch Git rewriting trees and commits and then move the branch pointer at the end.
It’s generally a good idea to do this in a testing branch and then hard-reset your master
branch after you’ve determined the outcome is what you really want.
To run filter-branch
on all your branches, you can pass --all
to the command.
Making a Subdirectory the New Root
Suppose you’ve done an import from another source control system and have subdirectories that make no sense (trunk
, tags
, and so on).
If you want to make the trunk
subdirectory be the new project root for every commit, filter-branch
can help you do that, too:
$ git filter-branch --subdirectory-filter trunk HEAD
Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12)
Ref 'refs/heads/master' was rewritten
Now your new project root is what was in the trunk
subdirectory each time.
Git will also automatically remove commits that did not affect the subdirectory.
Changing Email Addresses Globally
Another common case is that you forgot to run git config
to set your name and email address before you started working, or perhaps you want to open-source a project at work and change all your work email addresses to your personal address.
In any case, you can change email addresses in multiple commits in a batch with filter-branch
as well.
You need to be careful to change only the email addresses that are yours, so you use --commit-filter
:
$ git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
then
GIT_AUTHOR_NAME="Scott Chacon";
GIT_AUTHOR_EMAIL="schacon@example.com";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD
This goes through and rewrites every commit to have your new address.
Because commits contain the SHA-1 values of their parents, this command changes every commit SHA-1 in your history, not just those that have the matching email address.
Введение
В данном обучающем материале описаны различные способы перезаписи и изменения истории в Git. В Git используются несколько способов регистрации изменений. Мы обсудим плюсы и минусы различных способов и покажем примеры работы с ними. В данном обучающем материале описаны некоторые типовые причины перезаписи состояний кода и разъясняется, как избегать ошибок при таких операциях.
Основная задача Git — гарантировать, что вы не потеряете внесенные изменений. Но эта система также предназначена для предоставления вам полного контроля над процессом разработки. В числе прочего вы сами определяете то, как выглядит история вашего проекта. Такая свобода создает и вероятность потери коммитов. Git предоставляет команды для перезаписи истории, но предупреждает, что использование таких команд может привести к потере данных.
В Git существует несколько механизмов хранения истории и сохранения изменений. Вот эти механизмы: commit --amend
, git rebase
и git reflog
. Это мощные инструменты для настройки рабочего процесса. По окончании этого обучающего материала вы будете знать команды, которые позволят реструктурировать коммиты Git, и сможете избегать проблем, с которыми приходится сталкиваться при перезаписи истории.
Изменение последнего коммита: git commit --amend
Команда git commit --amend
— это удобный способ изменить последний коммит. Она позволяет объединить проиндексированные изменения с предыдущим коммитом без создания нового коммита. Ее можно использовать для редактирования комментария к предыдущему коммиту без изменения состояния кода в нем. Но такое изменение не только редактирует последний коммит, но и полностью его заменяет. То есть измененный коммит станет новой сущностью с отдельной ссылкой. Для Git он будет выглядеть как новый коммит, который отмечен звездочкой (*) на схеме внизу. Существует несколько распространенных сценариев использования команды git commit --amend
. В следующих разделах мы расскажем о примерах ее использования.
Изменение комментария к последнему коммиту Git
Допустим, при выполнении коммита вы допустили ошибку в комментарии к нему. Выполнение этой команды в отсутствие проиндексированных файлов позволяет отредактировать комментарий к предыдущему коммиту без изменения состояния кода.
В процессе разработки регулярно случаются преждевременные коммиты. Очень просто забыть проиндексировать файл или использовать неправильный формат комментария к коммиту. Флаг --amend
позволяет удобно исправить эти небольшие ошибки.
git commit --amend -m "an updated commit message"
Добавление аргумента -m
позволяет передать новый комментарий из командной строки, не открывая текстовый редактор.
Изменение файлов после коммита
В следующем примере показан распространенный сценарий разработки с использованием Git. Допустим, вы отредактировали несколько файлов и хотите сделать коммит за одну операцию. Но потом выясняется, что вы забыли добавить один из файлов в самом начале. Для того чтобы исправить эту ошибку, достаточно проиндексировать другой файл и выполнить коммит с флагом --amend
:
# Edit hello.py and main.py
git add hello.py
git commit
# Realize you forgot to add the changes from main.py
git add main.py
git commit --amend --no-edit
Флаг --no-edit
позволит внести изменения в коммит без изменения комментария к нему. Итоговый коммит заменит неполный коммит. При этом все будет выглядеть так, словно изменения в файлах hello.py
и main.py
были сделаны за один коммит.
Не используйте amend для публичных коммитов
Измененные коммиты по сути являются новыми коммитами. При этом предыдущий коммит не останется в текущей ветке. Последствия этой операции аналогичны сбросу (reset) публичного состояния кода. Не изменяйте коммит, после которого уже начали работу другие разработчики. Эта ситуация только запутает разработчиков, и разрешить ее будет непросто.
Обзор
Если коротко, команда git commit --amend
позволяет добавить новые проиндексированные изменения в последний коммит. С помощью коммита --amend
можно добавлять изменения в индекс Git или удалять таковые из него. Если никаких изменений не проиндексировано, при использовании флага --amend
вам все равно будет предложено изменить комментарий к последнему коммиту. Будьте осторожны при использовании флага --amend
с коммитами, доступными другим членам команды. Изменение коммита, доступного другому пользователю, может привести к путанице и длительным устранениям конфликтов при слиянии.
Изменение старых или нескольких коммитов
Для изменения старых или нескольких коммитов используйте команду git rebase
для объединения нескольких коммитов в новый базовый коммит. В стандартном режиме команда git rebase
позволяет в буквальном смысле перезаписать историю: она автоматически применяет коммиты в текущей рабочей ветке к указателю head переданной ветки. Поскольку новые коммиты заменяют старые, команду git rebase
запрещено применять к коммитам, которые стали доступны публично. Иначе история проекта исчезнет.
В таких или подобных случаях, когда важно сохранить чистую историю проекта, добавление флага -i
к команде git rebase
позволяет выполнять интерактивную операцию rebase
. Это дает возможность изменять отдельные коммиты в процессе, а не перемещать все коммиты. Подробную информацию об интерактивной операции rebase и дополнительных командах перемещения см. на странице git rebase.
Изменение файлов после коммита
Во время операции rebase команда редактирования (e
) остановит процесс на указанном коммите и позволит вам внести дополнительные изменения с помощью команды git commit --amend
. Git прервет работу и выведет следующее сообщение:
Stopped at 5d025d1... formatting
You can amend the commit now, with git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Несколько комментариев
Каждый стандартный коммит в Git будет содержать комментарий, поясняющий, что было изменено в коммите. Комментарии дают наглядное представление об истории проекта. Во время операции rebase можно выполнить несколько команд для изменения комментариев к коммитам.
- Изменение комментария (r) приведет к остановке операции rebase и позволит вам переписать комментарий к отдельному коммиту.
- При склеивании (s) во время rebase ко всем коммитам, отмеченным символом
s
, можно будет ввести один общий комментарий вместо нескольких отдельных. Подробности см. в разделе о склеивании коммитов ниже. - Эффект исправления (f) аналогичен склеиванию. В отличие от склеивания, исправление не прерывает rebase с открытием редактора для объединения комментариев к коммитам. В коммитах, отмеченных символом f, комментарии будут сброшены и заменены на комментарий предыдущего коммита.
Склеивайте коммиты для поддержания чистой истории
Команда склеивания (s
) позволяет в полной мере понять смысл rebase. Склеивание позволяет указать коммиты, которые нужно объединить в предыдущие коммиты. Таким образом создается «чистая история». Во время перемещения Git будет исполнять указанную команду rebase для каждого коммита. В случае склеенных коммитов Git откроет выбранный текстовый редактор и предложит объединить комментарии к указанным коммитам. Этот процесс можно показать следующим образом:
Обратите внимание, что идентификаторы коммитов, измененных с помощью команды rebase, отличаются от идентификаторов каждого из начальных коммитов. Коммиты с маркером pick получат новый идентификатор, если предыдущие коммиты были переписаны.
Современные решения для хостинга Git (например, Bitbucket) предлагают возможности «автосклеивания» при слиянии. Эти возможности позволяют автоматически выполнять rebase и склеивать коммиты ветки при использовании интерфейса решений для хостинга. Дополнительную информацию см. в разделе «Склеивание коммитов при слиянии ветки Git в Bitbucket».
Обзор
Команда git rebase позволяет изменять историю, а интерактивное выполнение rebase «подчищает» за вами следы. Теперь вы можете совершать и исправлять ошибки, оттачивая свою работу и сохраняя чистую, линейную историю проекта.
Страховка: git reflog
Справочные журналы (reflog) — это механизм, который используется в Git для регистрации обновлений, применяемых к концам веток и другим ссылкам на коммиты. Reflog позволяет вернуться к коммитам, даже если на них нет ссылок из какой-либо ветки или метки. После перезаписи истории reflog содержит информацию о старом состоянии веток и позволяет при необходимости вернуться к этому состоянию. Каждый раз при обновлении конца ветки любым способом (переключение веток, загрузка новых изменений, перезапись истории или просто добавление новых коммитов) в reflog добавляется новая запись. В данном разделе мы рассматриваем команду git reflog
и стандартные примеры ее использования.
Использование
Отображается reflog локального репозитория.
git reflog --relative-date
Отображается reflog с относительными датами (например, 2 недели назад).
Пример
Чтобы понять команду git reflog
, давайте разберем пример.
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
В приведенной выше команде reflog показан переход из главной ветки в ветку 2.2 и обратно. Отсюда можно выполнить жесткий сброс к старому коммиту. Последнее действие указано в верхней строчке с пометкой HEAD@{0}
.
Если вы случайно переместитесь назад, reflog будет содержать главный коммит, указывающий на (0254ea7)
до случайного удаления вами 2 коммитов.
git reset --hard 0254ea7
При использовании команды git reset можно вернуть главную ветку к более раннему коммиту. Это страховка на случай непреднамеренного изменения истории.
Необходимо отметить, что reflog только предоставляет страховку на тот случай, когда изменения попали в коммит в локальном репозитории, и что в нем отслеживаются только перемещения концов веток репозитория. Кроме того, записи reflog имеют определенный срок хранения. По умолчанию этот срок составляет 90 дней.
Дополнительную информацию см. на странице git reflog
.
Резюме
В данной статье мы рассмотрели несколько способов изменения истории Git и отмены изменений в Git. Мы вкратце рассмотрели процесс git rebase. Вот основные заключения.
- Существует несколько способов переписать историю в Git.
- Используйте команду
git commit --amend
для изменения последнего комментария. - Используйте команду
git commit --amend
, чтобы внести изменения в последний коммит. - Используйте команду
git rebase
для объединения коммитов и изменения истории ветки. - Команда
git rebase -i
дает более точный контроль над изменениями истории, чем обычный вариант git rebase.
Узнайте больше об описанных командах на соответствующих страницах:
- git rebase
- git reflog
Рабочий процесс в системе контроля версий git сводится к тому, что разработчик создает от ветки develop новую ветку для реализации конкретной задачи. Принято, что в ветке каждому коммиту соответствует более-менее логически законченная единица работы или же изменения, которые в будущем может потребоваться отменить.
Однако порой в спешке под конец рабочего дня разработчик может закоммититься с подписью вроде такой «WIP» или «TMP». В статье рассмотрим, как в git переименовать коммит. При этом возможны два основных случая — коммит является последним или коммит не является последним.
Переименование последнего коммита
На следующий день разработчик приходит на работу, просматривает изменения в ветке и вспоминает, что нужно изменить комментарий к коммиту. Для этого достаточно использовать команду git commit --amend -m "Новое название коммита"
.
Изменение комментария к не последнему коммиту
Допустим разработчик забыл с утра откорректировать название коммита, и в течение дня сделал ещё несколько коммитов. Тогда встаёт вопрос, как обновить комментарий, например для четвёртого коммита вглубь истории.
Итак, чтобы в git исправить комментарий такого коммита потребуется выполнить rebase в интерактивном режиме.
1. Сначала нужно посмотреть, на сколько коммитов назад находится тот коммит, которые нужно отредактировать. Для этого можно использоваться либо git log --oneline
, либо консольный GUI для Git, например, tig.
Видим, что изменить комментарий нужно к коммиту «tmp», находящийся позади на четыре коммита относительно HEAD.
2. Теперь запускаем rebase в интерактивной режиме:
git rebase -i HEAD~5
3. В появившемся редакторе следует в строках коммитов изменить команду pick
на reword
.
Затем сохраняем файл и выходим.
4. Автоматически начнется ребейз и откроется файл редактирования коммита. Здесь необходимо ввести новую подпись к коммиту.
5. Остаётся сохранить и выйти. Теперь название коммита изменено.
Важно. Оба описанных способа приводят к изменению коммитов, а значит коммиты на сервере и в локальной ветке будут теперь отличаться. Поэтому запушить ветку так просто не получится — нужно использовать флаг --force
.
git push --force
Однако, если работа в ветке ведётся несколькими разработчиками одновременно, тогда такой подход неприемлем.
Метки: Метки: git
Перезапись истории
Неоднократно при работе с Git, вам может потребоваться по какой-то причине внести исправления в историю коммитов.
Одно из преимуществ Git заключается в том, что он позволяет вам отложить принятие решений на самый последний момент.
Область индексирования позволяет вам решить, какие файлы попадут в коммит непосредственно перед его выполнением; благодаря команде git stash
вы можете решить, что не хотите продолжать работу над какими-то изменениями; также можете внести изменения в сделанные коммиты так, чтобы они выглядели как будто они произошли по-другому.
В частности, можно изменить порядок коммитов, сообщения или изменённые в коммитах файлы, объединить вместе или разбить на части, полностью удалить коммит — но только до того, как вы поделитесь своими наработками с другими.
В этом разделе вы познакомитесь со способами решения всех этих задач и научитесь перед публикацией данных приводить историю коммитов в нужный вам вид.
Примечание |
Не отправляйте свои наработки, пока вы ими не довольны Одно из основных правил Git заключается в том, что, так как большую часть работы вы делаете в своём локальном репозитории, то вы вольны переписывать свою историю локально. |
Изменение последнего коммита
Изменение вашего последнего коммита, наверное, наиболее частое исправление истории, которое вы будете выполнять.
Наиболее часто с вашим последним коммитом вам будет нужно сделать две основные операции: изменить сообщение коммита или изменить только что сделанный снимок, добавив, изменив или удалив файлы.
Если вы хотите изменить только сообщение вашего последнего коммита, это очень просто:
Эта команда откроет в вашем текстовом редакторе сообщение вашего последнего коммита, для того, чтобы вы могли его исправить.
Когда вы сохраните его и закроете редактор, будет создан новый коммит, содержащий это сообщение, который теперь и будет вашим последним коммитом.
Если вы создали коммит и затем хотите изменить зафиксированный снимок, добавив или изменив файлы (возможно, вы забыли добавить вновь созданный файл, когда совершали изначальный коммит), то процесс выглядит в основном так же.
Вы добавляете в индекс необходимые изменения, редактируя файл и выполняя для него git add
или git rm
для отслеживаемого файла, а последующая команда git commit --amend
берет вашу текущую область подготовленных изменений и делает её снимок для нового коммита.
Вы должны быть осторожными, используя этот приём, так как при этом изменяется SHA-1 коммита.
Поэтому, как и с операцией rebase
— не изменяйте ваш последний коммит, если вы уже отправили его в общий репозиторий.
Подсказка |
Изменённый коммит может потребовать изменения сообщения коммита При изменении коммита существует возможность изменить как его содержимое, так и сообщение коммита. С другой стороны, если изменения незначительны (исправление опечаток, добавление в коммит забытого файла), то текущее сообщение вполне можно оставить; чтобы лишний раз не вызывать редактор, просто добавьте измененные файлы в индекс и выполните команду:
|
Изменение сообщений нескольких коммитов
Для изменения коммита, расположенного раньше в вашей истории, вам нужно обратиться к более сложным инструментам.
В Git отсутствуют инструменты для переписывания истории, но вы можете использовать команду rebase
, чтобы перебазировать группу коммитов туда же на HEAD, где они были изначально, вместо перемещения их в другое место.
С помощью интерактивного режима команды rebase
, вы можете останавливаться после каждого нужного вам коммита и изменять сообщения, добавлять файлы или делать что-то другое, что вам нужно.
Вы можете запустить rebase
в интерактивном режиме, добавив опцию -i
к git rebase
.
Вы должны указать, какие коммиты вы хотите изменить, передав команде коммит, на который нужно выполнить перебазирование.
Например, если вы хотите изменить сообщения последних трёх коммитов, или сообщение какого-то одного коммита этой группы, то передайте как аргумент команде git rebase -i
родителя последнего коммита, который вы хотите изменить — HEAD~2^
или HEAD~3
.
Может быть, проще будет запомнить ~3
, так как вы хотите изменить последние три коммита; но не забывайте, что вы, в действительности, указываете четвертый коммит с конца — родителя последнего коммита, который вы хотите изменить:
Напомним, что это команда перебазирования — каждый коммит, входящий в диапазон HEAD~3..HEAD
, будет изменён вне зависимости от того, изменили вы сообщение или нет.
Не включайте в такой диапазон коммит, который уже был отправлен на центральный сервер: сделав это, вы можете запутать других разработчиков, предоставив вторую версию одних и тех же изменений.
Выполнение этой команды отобразит в вашем текстовом редакторе список коммитов, в нашем случае, например, следующее:
pick f7f3f6d Change my name a bit
pick 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
Важно отметить, что коммиты перечислены в порядке, противоположном порядку, который вы обычно видите при использовании команды log
.
Если вы выполните log
, то увидите следующее:
$ git log --pretty=format:"%h %s" HEAD~3..HEAD
a5f4a0d Add cat-file
310154e Update README formatting and add blame
f7f3f6d Change my name a bit
Обратите внимание на обратный порядок.
Команда rebase
в интерактивном режиме предоставит вам скрипт, который она будет выполнять.
Она начнет с коммита, который вы указали в командной строке (HEAD~3
) и повторит изменения, внесённые каждым из коммитов, сверху вниз.
Наверху отображается самый старый коммит, а не самый новый, потому что он будет повторен первым.
Вам необходимо изменить скрипт так, чтобы он остановился на коммите, который вы хотите изменить.
Для этого измените слово pick
на слово edit
напротив каждого из коммитов, после которых скрипт должен остановиться.
Например, для изменения сообщения только третьего коммита, измените файл следующим образом:
edit f7f3f6d Change my name a bit
pick 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
Когда вы сохраните сообщение и выйдете из редактора, Git переместит вас к самому раннему коммиту из списка и вернёт вас в командную строку со следующим сообщением:
$ git rebase -i HEAD~3
Stopped at f7f3f6d... Change my name a bit
You can amend the commit now, with
git commit --amend
Once you're satisfied with your changes, run
git rebase --continue
Эти инструкции говорят вам в точности то, что нужно сделать.
Выполните:
Измените сообщение коммита и выйдите из редактора.
Затем выполните:
Эта команда автоматически применит два оставшиеся коммита и завершится.
Если вы измените pick
на edit
в других строках, то можете повторить эти шаги для соответствующих коммитов.
Каждый раз Git будет останавливаться, позволяя вам исправить коммит, и продолжит, когда вы закончите.
Упорядочивание коммитов
Вы также можете использовать интерактивное перебазирование для изменения порядка или полного удаления коммитов.
Если вы хотите удалить коммит «Add cat-file» и изменить порядок, в котором были внесены два оставшихся, то вы можете изменить скрипт перебазирования с такого:
pick f7f3f6d Change my name a bit
pick 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
на такой:
pick 310154e Update README formatting and add blame
pick f7f3f6d Change my name a bit
Когда вы сохраните скрипт и выйдете из редактора, Git переместит вашу ветку на родителя этих коммитов, применит 310154e
, затем f7f3f6d
и после этого остановится.
Вы, фактически, изменили порядок этих коммитов и полностью удалили коммит «Add cat-file».
Объединение коммитов
С помощью интерактивного режима команды rebase
также можно объединить несколько коммитов в один.
Git добавляет полезные инструкции в сообщение скрипта перебазирования:
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
Если вместо «pick» или «edit» вы укажете «squash», Git применит изменения из текущего и предыдущего коммитов и предложит вам объединить их сообщения.
Таким образом, если вы хотите из этих трёх коммитов сделать один, вы должны изменить скрипт следующим образом:
pick f7f3f6d Change my name a bit
squash 310154e Update README formatting and add blame
squash a5f4a0d Add cat-file
Когда вы сохраните скрипт и выйдете из редактора, Git применит изменения всех трёх коммитов и затем вернёт вас обратно в редактор, чтобы вы могли объединить сообщения коммитов:
# This is a combination of 3 commits.
# The first commit's message is:
Change my name a bit
# This is the 2nd commit message:
Update README formatting and add blame
# This is the 3rd commit message:
Add cat-file
После сохранения сообщения, вы получите один коммит, содержащий изменения всех трёх коммитов, существовавших ранее.
Разбиение коммита
Разбиение коммита отменяет его и позволяет затем по частям индексировать и фиксировать изменения, создавая таким образом столько коммитов, сколько вам нужно.
Например, предположим, что вы хотите разбить средний коммит на два.
Вместо одного коммита «Update README formatting and add blame» вы хотите получить два разных: первый — «Update README formatting», и второй — «Add blame».
Вы можете добиться этого, изменив в скрипте rebase -i
инструкцию для разбиваемого коммита на «edit»:
pick f7f3f6d Change my name a bit
edit 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
Затем, когда скрипт вернёт вас в командную строку, вам нужно будет отменить индексацию изменений этого коммита, и создать несколько коммитов на основе этих изменений.
Когда вы сохраните скрипт и выйдете из редактора, Git переместится на родителя первого коммита в вашем списке, применит первый коммит (f7f3f6d
), применит второй (310154e
), и вернёт вас в консоль.
Здесь вы можете отменить коммит с помощью команды git reset HEAD^
, которая, фактически, отменит этот коммит и удалит из индекса изменённые файлы.
Теперь вы можете добавлять в индекс и фиксировать файлы, пока не создадите требуемые коммиты, а после этого выполнить команду git rebase --continue
:
$ git reset HEAD^
$ git add README
$ git commit -m 'Update README formatting'
$ git add lib/simplegit.rb
$ git commit -m 'Add blame'
$ git rebase --continue
Git применит последний коммит (a5f4a0d
) из скрипта, и ваша история примет следующий вид:
$ git log -4 --pretty=format:"%h %s"
1c002dd Add cat-file
9b29157 Add blame
35cfb2b Update README formatting
f7f3f6d Change my name a bit
И снова, при этом изменились SHA-1 хеши всех коммитов в вашем списке, поэтому убедитесь, что ни один коммит из этого списка ранее не был отправлен в общий репозиторий.
Обратите внимание, что последний коммит в списке (f7f3f6d
) не изменился.
Несмотря на то, что коммит был в списке перебазирования, он был отмечен как «pick» и применён до применения перебазирования, поэтому Git оставил его нетронутым.
Удаление коммита
Если вы хотите избавиться от какого-либо коммита, то удалить его можно во время интерактивного перебазирования rebase -i
.
Напишите слово «drop» перед коммитом, который хотите удалить, или просто удалите его из списка:
pick 461cb2a This commit is OK
drop 5aecc10 This commit is broken
Из-за того, как Git создаёт объекты коммитов, удаление или изменение коммита влечёт за собой перезапись всех последующих коммитов.
Чем дальше вы вернётесь в историю ваших коммитов, тем больше коммитов потребуется переделать.
Это может вызвать множество конфликтов слияния, особенно если у вас много последующих коммитов, которые зависят от удалённого.
Если во время подобного перебазирования вы поняли, что это была не очень хорошая идея, то всегда можно остановиться.
Просто выполните команду git rebase --abort
и ваш репозиторий вернётся в то состояние, в котором он был до начала перебазирования.
Если вы завершили перебазирование, а затем решили, что полученный результат это не то, что вам нужно — воспользуйтесь командой git reflog
, чтобы восстановить предыдущую версию вашей ветки.
Дополнительную информацию по команде reflog
можно найти в разделе Восстановление данных главы 10.
Примечание |
Дрю Дево создал практическое руководство с упражнениями по использованию |
Продвинутый инструмент: filter-branch
Существует ещё один способ переписывания истории, который вы можете использовать при необходимости изменить большое количество коммитов каким-то программируемым способом — например, изменить глобально ваш адрес электронной почты или удалить файл из всех коммитов.
Для этого существует команда filter-branch
, и она может изменять большие периоды вашей истории, поэтому вы, возможно, не должны её использовать кроме тех случаев, когда ваш проект ещё не стал публичным и другие люди ещё не имеют наработок, основанных на коммитах, которые вы собираетесь изменить.
Однако, эта команда может быть очень полезной.
Далее вы ознакомитесь с несколькими обычными вариантами использованиями этой команды, таким образом, вы сможете получить представление о том, на что она способна.
Внимание |
Команда |
Удаление файла из каждого коммита
Такое случается довольно часто.
Кто-нибудь случайно зафиксировал огромный бинарный файл, неосмотрительно выполнив git add .
, и вы хотите отовсюду его удалить.
Возможно, вы случайно зафиксировали файл, содержащий пароль, а теперь хотите сделать ваш проект общедоступным.
В общем, утилиту filter-branch
вы, вероятно, захотите использовать, чтобы привести к нужному виду всю вашу историю.
Для удаления файла passwords.txt
из всей вашей истории вы можете использовать опцию --tree-filter
команды filter-branch
:
$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
Ref 'refs/heads/master' was rewritten
Опция --tree-filter
выполняет указанную команду после переключения на каждый коммит и затем повторно фиксирует результаты.
В данном примере, вы удаляете файл passwords.txt
из каждого снимка вне зависимости от того, существует он или нет.
Если вы хотите удалить все случайно зафиксированные резервные копии файлов, созданные текстовым редактором, то вы можете выполнить нечто подобное git filter-branch --tree-filter 'rm -f *~' HEAD
.
Вы можете посмотреть, как Git изменит деревья и коммиты, а затем уже переместить указатель ветки.
Как правило, хорошим подходом будет выполнение всех этих действий в тестовой ветке и, после проверки полученных результатов, установка на неё указателя основной ветки.
Для выполнения filter-branch
на всех ваших ветках, вы можете передать команде опцию --all
.
Установка подкаталога как корневого каталога проекта
Предположим, вы выполнили импорт из другой системы контроля версий и получили в результате подкаталоги, которые не имеют никакого смысла (trunk
, tags
и так далее).
Если вы хотите сделать подкаталог trunk
корневым для каждого коммита, команда filter-branch
может помочь вам в этом:
$ git filter-branch --subdirectory-filter trunk HEAD
Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12)
Ref 'refs/heads/master' was rewritten
Теперь вашим новым корневым каталогом проекта будет являться подкаталог trunk
.
Git также автоматически удалит коммиты, которые не затрагивали этот подкаталог.
Глобальное изменение адреса электронной почты
Ещё один типичный случай возникает, когда вы забыли выполнить git config
для настройки своего имени и адреса электронной почты перед началом работы, или, возможно, хотите открыть исходные коды вашего рабочего проекта и изменить везде адрес вашей рабочей электронной почты на персональный.
В любом случае вы можете изменить адрес электронный почты сразу в нескольких коммитах с помощью команды filter-branch
.
Вы должны быть осторожны, чтобы изменить только свои адреса электронной почты, для этого используйте опцию --commit-filter
:
$ git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
then
GIT_AUTHOR_NAME="Scott Chacon";
GIT_AUTHOR_EMAIL="schacon@example.com";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD
Эта команда пройдёт по всем коммитам и установит в них ваш новый адрес.
Так как коммиты содержат значения SHA-1-хешей их родителей, эта команда изменяет хеш SHA-1 каждого коммита в вашей истории, а не только тех, которые соответствовали адресам электронной почты.
Many programmers underestimate the role of the commit message, while it is very important for managing the work. It helps other developers working on your project to understand the changes that you have made. So it must be as concrete, well-structured and clear as possible.
In this snippet, we will show you how to change your most recent commit message, as well as how to change any number of commit messages in the history.
Read on to see the options.
You can use --amend flag with the git commit command to commit again for changing the latest commit:
git commit --amend -m "New commit message"
Running this will overwrite not only your recent commit message but, also, the hash of the commit. Note, that it won’t change the date of the commit.
It will also allow you to add other changes that you forget to make using the git add command:
git add more/changed/w3docs.txt
git commit --amend -m "message"
The -m option allows writing the new message without opening the Editor.
If you want to change the message of the commit that is already pushed to the server, you should force push it using the git push command with —force flag, otherwise, your push will be rejected.
Check out Force Pushing Local Changes to Remote for more details on how to force push your changes.
In this section, we will show you the steps to follow if you want to change multiple commit messages in the history.
Let’s assume that we want to take the 10 latest commit messages, starting from the HEAD.
Run Git Rebase in Interactive Mode
Firstly, you should run the git rebase in the interactive mode:
Type «Reword»
After the first step, the editor window will show up the 10 most recent commits. It will offer you to input the command for each commit. All you need to do is typing «reword» at the beginning of each commit you want to change and save the file. After saving, a window will open for each selected commit for changing the commit message.
Enter a New Commit Message
After the second step, an editor will open for each commit. Type a new commit message and save the file.
Check out Force Pushing Local Changes to Remote for more details on how to force push your changes.
It is not recommended to change a commit that is already pushed because it may cause problems for people who worked on that repository.
If you change the message of the pushed commit, you should force push it using the git push command with --force flag (suppose, the name of remote is origin, which is by default):
git commit --amend -m "New commit message."
git push --force origin HEAD
—force overwrites the remote branch on the basis of your local branch. It destroys all the pushed changes made by other developers. It refers to the changes that you don’t have in your local branch.
Here is an alternative and safer way to amend the last commit:
git push --force-with-lease origin HEAD
—force-with-lease is considered a safer option that will not overwrite the work done on the remote branch in case more commits were attached to it (for instance, by another developer). Moreover, it helps you to avoid overwriting another developer’s work by force pushing.
The git add command is used for adding changes in the working directory to the staging area. It instructs Git to add updates to a certain file in the next commit. But for recording changes the git commit command should also be run. The git add and git commitcommands are the basis of Git workflow and used for recording project versions into the history of the repository. In combination with these two commands, the git status command is also needed to check the state of the working directory and the staging area.
The git push command is used to upload the content of the local repository to the remote repository. After making changes in the local repository, you can push to share the modification with other members of the team.
Git refuses your push request if the history of the central repository does not match the local one. In this scenario, you should pull the remote branch and merge it into the local repository then push again. The --force flag matches the remote repository’s branch and the local one cleaning the upstream changes from the very last pull. Use force push when the shared commits are not right and are fixed with git commit --amend or an interactive rebase. The interactive rebase is also a safe way to clean up the commits before sharing. The git commit --amend option updates the previous commit.
Quick cheat sheet to change commit message in git
You just did a commit then realized the message is not the intended one, or you want to git change commit message on multiple commits. Here is what you should do.
To change the last commit, use the --amend
flag this way
git commit --amend -m "<message>"
OR
git commit --amend -c <commit hash>
You can also git change commit messages on the HEAD using a soft reset.
git reset --soft HEAD~1
git commit -m "<message>"
OR
Use the longer route by doing a soft reset followed by a mixed reset. Then commit the changes with a new message.
git reset --soft HEAD~1 git reset HEAD git add <file> git commit -m "<message>"
To edit multiple commit messages or specific commit message that is NOT the HEAD, interactively rebase the commit using any of these commands:
ALSO READ: git HEAD~ vs HEAD^ vs HEAD@{} Explained with Examples
Specify the commit hash for one change.
git rebase -i <commit hash>
Your default text editor opens up. You can then reword
the message.
OR
Edit multiple commit messages by selecting a bunch of commits from the HEAD to the target commit hash.
git rebase -i HEAD~N
where N
is the number of commits, from the top excluding the parent commit hash, whose message you want to edit.
As shown in the next sections of this tutorial, it would help to find a detailed explanation of the git commit message and practice git change commit message using relatable examples.
What is a git commit message? Do’s and Don’ts
A git commit message explains why a change occurred in a repository. It plays a significant role in marking software releases and bug fixes.
You will mostly apply git commit messages when collaborating on a project, enabling your teammates to figure out why you made a change on the branch you are working on. Editing commit messages lets you put more descriptive information or entirely overwrite the message.
Despite the excellent side of the git change commit message, you should use it cautiously. For instance, the change could be unconducive for pushed files because changing a commit message alters the SHA id.
Another reason to avoid git change commit message is that the operation automatically commits staged changes even if you had not decided to commit them.
ALSO READ: Let’s decode «git restore» for you [Practical Examples]
Now that you know what can lead to editing a commit message and the drawbacks of git change commit message, let us see them in action.
Lab setup to practice git change commit message
Create a new repo
I am creating a remote repo called change_commit_message
on GitHub.
After creating the repo, copy its URL and clone it on your preferred terminal or command line.
Configure the text editor
Next, we will configure our default text editor to ease git change commit message. If you have installed a friendlier text editor other than git’s default one, vim
, let us configure it as follows:
git config --global core.editor "[your preferred editor] -w"
I am changing my git’s default text editor to Visual Studio Code.
git config --global core.editor "code -w"
where the -w
flag informs git to wait for us to change the commit message before recording the input.
Build a commit history
Let’s cd
into the new repo and create a few commits in readiness for git change commit message examples.
cd change_commit_message
Second commit
touch file2 git stage file2 git commit -m "Add second commit"
Third commit
touch file3 git stage file3 git commit -m "Add the third commit"
Fourth commit
touch file4 git stage file4 git commit -m "Add fourth commit"
Check the commit history
git log
We have four commits.
ALSO READ: Remove untracked files with «git clean» [Practical Examples]
We have some commits to play with. Let’s git change commit messages using relatable examples.
Scenario-1: Editing the last commit message
Git change commit message is quite simple to do on the commit HEAD using either the --amend
flag or git reset soft.
Example-1: Using the amend flag
Assume we want to introduce the article in the fourth commit message. We can achieve that by applying the --amend
flag as follows:
git commit --amend -m "Add the fourth commit"
Let’s recheck the history.
git log
The commit message got edited!
Example-2: Doing a soft reset
The reset command for doing git change commit message is
git reset --soft HEAD~1
By writing ~1
, we are telling git to edit one commit from the HEAD. Alternatively, we can replace ~1
with the caret symbol ^
to refer to the last commit, as shown below.
git reset --soft HEAD^
A soft reset undoes the commit HEAD changes. We can then rewrite the message.
git commit -m "Edit the fourth commit"
Our last commit message changed from Add the fourth commit to Edit the fourth commit.
We can also go the longer route by unstaging the file.
Do a soft reset.
git reset --soft HEAD~1
Unstage the last indexed file.
git reset HEAD
Then restage and recommit it using a new commit message
git add . git commit -m "Change the fourth commit message entirely"
Recheck the history.
git log
The last commit message changed from Edit the fourth commit to Change the fourth commit message entirely.
ALSO READ: Git rebase explained in detail with examples
Scenario-2: Git change commit messages on specific file(s)
Want to edit a specific commit message other than the HEAD’s? Combine the --amend
and -c
flags or rebase the commit hash interactively.
Example-3: Combine the --amend
and -c
flags
Running the command
git commit --amend -c fc87791e0565c40d5fbdb38fead250e646fdddcd
asks for a commit message to change the commit before the specified hash by opening our text editor.
Let’s edit the commit message to Edit the third commit. That changes the commit message before the commit hash fc87791e0565c40d5fbdb38fead250e646fdddcd
Example-4: Interactively rebase a commit hash
Interactive rebase is one of the most familiar ways to git change commit messages. We can use it to edit one commit by rebasing our third last commit as follows
git rebase -i 22e2c5c
The text editor opens up. Change the last commit’s command from pick
to reword
, then close the text editor.
The text editor reopens, asking us for a new commit message for the target commit hash. Let’s edit the message from Edit the third commit to Change to fourth commit, close the text editor then check the history.
git log
We can also use git rebase -i <commit hash>
to edit multiple commit messages. Better yet, specify N
during the interactive rebase if you know the number of commits from the head to your target base commit hash, as illustrated below.
ALSO READ: git undo commit before push [Practical Examples]
Scenario-3: Changing multiple commit messages
Example-5: Interactively rebase the HEAD
Replacing N
with 3 in the command
git rebase -i HEAD~N
git rebase -i HEAD~3
opens our default text editor. This time around, let’s reword
every commit message, close the editor and see what happens.
The editor reopens three times, asking for respective commit messages. I am replacing each Add or Edit with Track then logging the history to check the result of git change commit messages.
git log
And that’s the fundamentals of git change commit message using the rebase commit.
Conclusion
You just learned how to git change commit messages using the --amend
flag, git reset command, and interactive rebase. Now is the time to enjoy your software tracking missions by practicing the commands further and applying them in various projects.