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»
Перевод проекта First Aid Git от команды HTML Academy.
Поговорим о решении проблем с Git.
Восстановление накопленных изменений
В том случае, если изменения, внесённые пользователем, находятся в режиме накопления, применить их к ветке можно с помощью команды git stash apply. Также можно запустить git diff — эта команда поможет выявить различия. Для того, чтобы затем избавиться от накопленных данных, нужно запустить команду:
Если существует более одного накопления, найти нужное можно с помощью команды:
git stash list
затем можно применить его, воспользовавшись его индексом:
Необходимо учитывать, что отсчёт индексов ведётся от нуля.
Восстановление удалённого тега
В том случае, если необходимо восстановить случайно удалённый тег, начать можно с его поиска:
git fsck --unreachable | grep tag
После того, как нужный тег найден, его следует восстановить:
git update-ref refs/tags/название-тега
Восстановление удалённого файла
Если вы случайно удалили файл, его можно быстро восстановить:
Если требуется восстановить файл из конкретной временной точки истории коммитов, следует узнать хеш нужного коммита и запустить команду:
git checkout $commit~1 myfile.txt
Восстановление удалённой ветки
С помощью комманды git reflog можно узнать хеш (SHA1) последнего коммита в удалённой ветке. Скопируйте этот хеш и используйте в команде:
После этого восстановить удалённую ветку можно будет вот такой командой:
git checkout -b <название-ветки>
Изменение сообщения коммита перед его отправкой
Изменить сообщение коммита можно с помощью команды git commit --amend
, она откроет редактор, в котором можно будет внести необходимые поправки в последнее сообщение.
Сообщение можно изменить и напрямую с помощью команды
git commit --amend -m "Новое прекрасное сообщение"
Изменение сообщения коммита после его отправки
В данном случае процесс занимает два шага. Сначала нужно изменить сообщение с помощью комманды git commit --amend
, а затем перезаписать историю коммитов локальной ветки: git push <remote> <branch> --force
Предупреждение: подобная насильная перезапись может привести к потери коммитов из внешней ветки, если с ней давно не было синхронизации, соблюдайте осторожность.
Использование алиасов команд в командной строке
Устали каждый раз печатать git status? Этой команде можно присвоить простой алиас, который проще и быстрее вбивать в git.
git config --global alias.st status
— теперь нужно писать только git st
Можно пойти дальше и присвоить алиасы более сложным командам:
git config --global alias.logme 'log -p --author=Rob'
Теперь алиас git logme будет выводить все наши коммиты.
Коммит в неправильную ветку
Нужно переключиться на новую ветку, которую вы забыли предварительно создать:
git checkout -b название-новой-ветки.
А затем переключиться к оригинальной ветке:
git checkout название-оригинальной-ветки
…и «откатиться» до последнего коммита, который нужно сохранить.
Чтобы это сделать, можно воспользоваться командой git log и сохранить хеш (SHA1) последнего коммита, который нужно оставить.. Например, это a31a45c.
Теперь его нужно сбросить: git reset --hard a31a45c
и отправить получившийся результат.
Предупреждение: Убедитесь в том, что никто не отправлял коммиты в оригинальную ветку во время выполнения вышеописанных процедур, в противном случае эти изменения будут потеряны!
Обновление конкретного подмодуля
Чтобы обновить конкретный подмодуль в репозитории, нужно добавить путь к подмодулю:
git submodule update --remote --merge <path>
Откат к конкретному коммиту в истории
Если вас не очень беспокоят изменения в локальном репозитории, то можно «откатиться» к конкретному коммиту в истории с помощью команды:
Эта команда установит HEAD на конкретный коммит. Также можно воспользоваться хешем коммита.
Отмена коммита до публикации изменений
Если вы сделали коммит, который впоследствии понадобилось отредактировать или полностью стереть, поможет команда git reset.
git reset HEAD~1 # отменить последний коммит, сохранить изменения
git reset --hard HEAD~1 # отменить последний коммит, стереть изменения
Будьте осторожны используя второй вариант, поскольку изменения ваших локальных файлов будут потеряны.
Чтобы сохранить сообщение коммита, наберите: :
Отмена коммита после отправки его в master-репозиторий
Рассмотрим процедуру возврата одного или нескольких коммитов, которые нужно стереть из удалённой ветки. Обозначить конкретный коммит можно с помощью его хеша:
Отмена только коммита, который является вторым после последнего:
Простая отмена последнего коммита:
Отмена локальных изменений файлов
Простейшим способом избавиться от нежелательных изменений для файлов и папок является восстановление состояния последнего коммита. Сделать это можно с помощью специальной команды:
Кроме того, можно восстановить конкретный путь к файлу:
git checkout -- путь-до-файла
Отображение всех коммитов одного файла
Если вы хотите просмотреть все коммиты с изменениями конкретного файла, воспользуйтесь командой git log --follow -p -- myfile
Аргумент —follow позволяет вывести все изменения над файлом, даже если в процессе работы он был переименован.
Если опустить опцию -p, то система выведет только сообщения коммитов, но не их содержимое.
Отображения числа коммитов от каждого участника
Хотите узнать, сколько коммитов сделал каждый участник команды?
Эта команда выведет список, отсортированный в порядке убывания количества коммитов: git shortlog -s -n
Отобразить коммиты, содержащие удалённые файлы
Узнать, в каких коммитах содержатся удалённые файлы, можно с помощью команды:
git log --diff-filter=D --summary
Она покажет список коммитов, в которых удалялись файлы.
Отсортировать коммиты по автору
Чтобы вывести список коммитов, отфильтрованных по автору, следует воспользоваться следующей командой:
git log --author="Имя автора"
Очистка всех скрытых состояний
Очистить все скрытые состояния можно следующей командой:
Переименование локальной и удалённой ветки
Предложим, у вас есть ветка «fix-bug25», которую вы захотели переименовать в «hotfix-users». Прежде всего, нужно будет изменить локальную ветку:
git branch -m fix-bug25 hotfix-users
А затем — удалённую ветку: переименовать её напрямую нельзя, поэтому нужно будет её удалить, и затем опубликовать заново уже с новым именем. Прежде чем приступать к этим процедурам, следует убедиться, что никто из членов команды не работает с этой веткой! Удаляем ветку: git push origin :fix-bug25
А теперь заново публикуем её с новым именем: git push origin hotfix-users
Переименование тега
Чтобы переименовать существующий тег:
git tag новое-название-тега старое-название-тега
git tag -d старое-название-тега
git push origin :refs/tags/старое-название-тега
git push --tags
Перестать отслеживать существующие файлы
Если вы хотите перестать отслеживать файлы, которые уже есть в репозитории, но при этом желаете сохранить его локально, осуществите коммит изменений и запустите команду:
Она удалит изменённые файлы из зоны подготовленных файлов (staging area). Затем нужно запустить команду:
и отправить изменения.
Подготовка удалённых файлов
Чтобы подготовить к коммиту файлы и папки, которые были удалены локально, можно использовать специальную команду:
Если требуется подготовить только используемый в данный момент путь, воспользуйтесь командой
Поиск конкретного сообщения во всех коммитах
Чтобы найти конкретный текст сообщения коммита, соответствующий регулярному выражению, нужно воспользоваться командой
Пометить конфликтующий файл, как разрешённый
Чтобы пометить один или несколько конфликтующих файлов, как разрешённые, чтобы их можно было нормально изменять, воспользуйтесь командой:
Затем можно запустить git commit, чтобы разрешить конфликты и опубликовать изменения.
Просмотр всех неотправленных коммитов
Чтобы просмотреть все коммиты, которые ещё не были отправлены в соответствующие ветки, воспользуйтесь следующей командой:
git log --branches --not --remotes
Кроме того, можно использовать:
git log origin/master..HEAD
Просмотр старой ревизии файла
Существует возможность просмотреть содержимое файла в конкретный момент времени в прошлом. Для этого нужно использовать команду:
git show commitHash:myfile.txt
Публикация локальной ветки для удалённого редактирования
Если вы создали локальную ветку, и хотите, чтобы другие пользователи могли с ней работать, воспользуйтесь командой:
git push -u origin название-моей-новой-ветки
Теперь они тоже смогут вносить изменения в эту ветку.
Сброс локальной ветки до состояния удалённой
В том случае, если отсутствуют изменения, которые необходимо сохранить, сбросить локальную ветку до состояния удалённой можно с помощью двух простых команд.
Прежде всего нужно получить свежие обновления из удалённой ветки:
git fetch название-удалённой-ветки.
А затем нужно сообщить git, что локальную ветку следует «откатить» до состояния удалённой:
git reset --hard origin/название-локальной-ветки.
При наличии коммита, который нужно сохранить, перед сбросом нужно создать новую ветку и произвести коммит: git commit -m «Обновление»
git branch название-новой-ветки
Синхронизировать ветку с master-репозиторием
Чтобы синхронизировать последние изменения в репозитории master (или с любой другой веткой, с которой вы работали) необходимо «перебазировать» локальную ветку. Предположим, вы работаете над веткой foobar:
А затем осуществляете «перебазирование»:
После этого будут применены коммиты origin из master. После разрешения конфликтов процесс можно продолжить с помощью команды git rebase —continue. Теперь можно продолжать работу над своей веткой или осуществить её слияние (merge) с главным репозиторием.
Слияние локальных изменений с другой веткой
Это можно сделать прямо в процессе стандартного слияния (merge). Вам стоит сохранить историю слияний используя флаг —no-ff, что означает no fast forward.
Перейдите в ветку, в которую будут вливаться изменения, убедитесь в её актуальности и запустите процесс:
git merge <другая-ветка> --no-ff
Затем появится сообщение о коммите merge X into Y branch, после чего вы можете смело запушить ваше слияние.>
Совмещение двух и более коммитов
Здесь нам понадобится произвести интерактивное перебазирование. Если перебазирование происходит относительно master-ветки, то начать следует с команды git rebase -i master. Однако, если перебазирование происходит не относительно ветки, то нужно будет перебазироваться относительно HEAD.
Если есть необходимость в совмещении двух последних коммитов, можно использовать команду
После её ввода появятся инструкции по выбору коммитов. В том случае, если необходимо совместить все коммиты с первым старейшим коммитом, то в первой строке нужно написать pick, а для всех остальных коммитов изменить букву на f. Подробнее здесь
Совмещение коммитов по конкретной функции для добавления в ветку релиза
Если вы решите совместить и опубликовать коммиты, то возникнет новый коммит в ветке релиза, поэтому история ветки конкретной функции останется неизменной.
Ниже представлен пример того, как достичь подобного эффекта:
git fetch origin
git checkout [release-branch]
git rebase origin/[release-branch]
git merge —squash —no-commit [feature-branch]
git commit -m 'Merge X into Y'
В конечном итоге останется только один коммит в ветке релиза, а история изменений в ветке разработки конкретной функции останется нетронутой.
Подробнее о ветках функций
Создание новой ветки с изменениями текущей
Часто возникает ситуация, при которой пользователи начинают изменять файлы в ветке, чтобы что-то исправить, и лишь позднее вспоминают, что предварительно не создали новую ветку. К счастью, есть способ сделать это уже в процессе:
git checkout -b название-моей-новой-ветки
Эта команда перенесёт файлы из текущей ветки в новую, которую потом уже можно «закоммитить».
Убрать файл из буфера
Чтобы убрать добавленный по ошибке файл из буфера, нужно воспользоваться простой командой:
git reset HEAD unlovedfile.txt
Удаление внешней ветки
Если вы хотите удалить ветку, введите команду:
git push origin --delete название-ветки
Удаление неотслеживаемых файлов и папок
Чтобы удалить неотслеживаемые файлы и папки из рабочей копии наберите следующую команду:
Чтобы в принципе удалить их:
Подсказка: чтобы увидеть, какие файлы являются лишними, перед их непосредственным удалением, наберите:
Удаление старых веток, стёртых из внешнего репозитория
Если ветка удалена из внешнего репозитория, её также можно стереть из локального репозитория с помощью команды
git-remote prune название-удалённой-ветки.
Она удалит старую ветку под названием название-удалённой-ветки, которая уже была стёрта из внешнего репозитория, но всё ещё доступна локально в remotes/название-удалённой-ветки.
Удаление файла из git с сохранением его локальной копии
Для того, чтобы удалить файл из git, но сохранить его локально нужно использовать следующую команду:
git rm --cached myfile.txt
Больше статей о Git
- Полезные команды для работы с Git
- Как работать с GitHub в большой команде
- Как бесплатно залить сайт на хостинг GitHub Pages
Changing the latest Git commit message
If the message to be changed is for the latest commit to the repository, then the following commands are to be executed:
-
git commit --amend -m "New message"
-
git push --force repository-name branch-name
Note that using
--force
is not recommended unless you are absolutely sure that no one else has cloned your repository after the latest commit.
A safer alternative is to use:
git push --force-with-lease repository-name branch-name
Unlike --force
, which will destroy any changes someone else has pushed to the branch, --force-with-lease
will abort if there was an upstream change to the repository.
Changing older commit messages
If the message needs to be amended for an older commit, then the interactive rebase tool can be used:
- Navigate to the repository that contains the commit you want to amend and open a terminal window.
- Use the
git rebase -i HEAD~n
command to display a list of the last nn commits in your default text editor. For example, the following command would display a list of the last three commits in your current branch:git rebase -i HEAD~3
The list would be similiar to
this:
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 that needs to be changed: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.
- Force push the amended commits using
git push --force
.
Перезапись истории
Неоднократно при работе с 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 каждого коммита в вашей истории, а не только тех, которые соответствовали адресам электронной почты.
Введение
В данном обучающем материале описаны различные способы перезаписи и изменения истории в 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 commit
уже выполнена, не отчаивайтесь — все можно поправить. Рассмотрим два варианта:
Как изменить сообщение последнего коммита?
Данный пункт подразумевает, что вы еще не выполняли команду git push
и все изменения хранятся в Вашем локальном репозитории. Выполните команду:
git commit --amend
после выполнения данной команды откроется редактор, установленный по умолчанию, который позволит вам изменить сообщение последнего коммита.
Если комментарий маленький — более удобно изменить комментарий последнего коммита в командной строке, с помощью команды:
Code language: JavaScript (javascript)
git commit --amend -m "новое соошение"
Предупреждение: Перед коммитом убедитесь, что у вас нет внесенных изменений в рабочую копию, иначе они тоже будут закоммичены. (Unstaged изменения не коммитятся)
Как изменить сообщения коммита, который вы уже отправили на сервер?
Если вы уже отправили коммит на сервер с помощью команды git push
, то — после внесения изменений в коммит локально (как описано выше) — вам также потребуется выполнить команду push
из ключем --force
:
Code language: HTML, XML (xml)
git push <название ветка на сервере> --force
или
Code language: HTML, XML (xml)
git push <название ветка на сервере> -f
Предупреждение: при использовании ключа --force
, если в удаленной ветке есть коммиты, которых нет в вашей локальной ветке, вы потеряете эти коммиты.
Будьте осторожны при внесении изменений в коммиты, особенно те, которые вы уже отправили на сервер. Внесение изменений в коммиты по существу переписывает их, чтобы они имели другие идентификаторы SHA, что создает проблему, если у других людей есть копии старого коммита, который вы переписали. Любой, у кого есть копия старого коммита, должен будет синхронизировать свою работу с вашим недавно переписанным коммитом, что иногда может быть сложно, поэтому убедитесь, что вы координируете свои действия с другими при попытке переписать общую историю коммитов, или просто избегайте редактирования коммитов.