Как изменить описание коммита gitlab

Запись в разделе: Компьютер - Программирование - Системы контроля версий (VCS) - Git. Тема: GIT: Как исправить ошибочный комментарий к коммиту или файлы в коммите

Как исправить описание только последнего коммита

Если коммит только что сделан, и он не отправлен в origin-репозитарий командой git push, то изменить описание коммита можно такой командой:

git commit —amend -m «Новое описание»

Или можно дать команду:

git commit —amend

При выполнении этой команды будет открыт текстовый редактор по-умолчанию, в котором можно будет изменить сообщение коммита. После выхода из редактора, описание коммита будет изменено. При этом будет изменена SHA-1 сумма коммита.

Кстати, исправить можно не только описание, но и файлы и их состояние в коммите. Для этого нужно просто изменить или добавить или удалить файлы в рабочей директории, зафиксировать эти изменения в индексе (командами git add или git rm и проч.), после чего можно дать команду:

git commit —amend

в результате которой GIT возьмёт текущий индекс и сделает его снимком состояния нового коммита. Опять же, при этом будет изменена SHA-1 сумма коммита.

Как исправить описание любого коммита в истории

Описанные ниже действия возможны, если коммиты еще не отправлены на удаленный репозитарий, или если программист является единственным пользователем удаленного репозитария. Описанные ниже действия являются переписыванием истории. Поэтому, если есть другие программисты, то они будут вынуждены вытянуть себе заново всю копию репозитария, чтобы у них была такая же история, что и у автора правок.

Начиная с git 1.6.6 можно использовать механизм GIT, называющийся reword. Он используется в рамках команды rebase.

Для начала находим ID коммита с неправильным комментарием. Для этого в директории с нужным git-репозиторием набираем

git log —pretty=format:»%h — %ar : %s»

На выходе получаем список коммитов с хеш-идентификаторами. Например, такой

39ba64e — 7 hours ago : redirect to order status after booking

c4d25d6 — 8 hours ago : order status

Предполагается, что надо изменить коммит c4d25d6. В дальнейших командах, контрольная сумма коммита обозначает глубину, на которую будет изменяться история.

Чтобы исправить неправильный комментарий у коммита c4d25d6, вводим следующую команду (обратите внимание на символ ^ после ID, это значит, что начинать rebase надо от его родителя. То есть, в команде rebase указывается родительский коммит того коммита, который надо отредактировать)

git rebase —interactive c4d25d6^

git откроет редактор по умолчанию, в котором вы увидите что-то типа:

pick c4d25d6 order status

pick 39ba64e redirect to order status after booking

Это сценарий действий, которые будут выполнены для изменения истории.

Меняем слово pick на слово reword, то есть нам нужно лишь изменить комментарий.

reword c4d25d6 order status

pick 39ba64e redirect to order status after booking

GIT предложит сохранить текстовый файл, что и нужно сделать. Фактически, в интерактивном режиме, это список команд для него. Он снова откроет текстовый редактор и предложит изменить комментарий для нужного коммита. После сохранения файлика git повторит весь комит, поменяет его ID и короткий хеш. Все.

Кстати, помимо reword есть еще и команда edit. После сохранения файла сценария, сценарий будет выполняться до команды edit, после чего GIT завершит работу и вернется в консоль. Будут примерно такие сообщения:

$ git rebase —interactive c4d25d6^

Stopped at c4d25d6… updated the gemspec to hopefully work better

You can amend the commit now, with

git commit —amend

Once you’re satisfied with your changes, run

git rebase —continue

В этот момент рабочая директория будет находиться в состоянии коммита c4d25d6. Можно изменить это состояние (изменить или добавить или удалить файлы), и дать команду:

git commit —amend

Откроется редактор, в котором можно будет изменить описание коммита. После закрытия редактора, отредактированный коммит будет изменен в истории. И можно дать следующую команду:

git rebase —continue

Эта команда будет дальше искать коммиты, которые нужно изменить, и будет снова останавливаться чтобы позволить внести изменения, до самого последнем коммите.

Чтобы запушить изменения, сделанные таким способом, нужно выполнить команду:

git push —force

После нее история на origin-сервере будет перезаписана. И если с репозитарием работают другие программисты, они должны будут принять всю измененную историю. Как это сделать — надо искать отдельно. Радикальный способ — это сделать git clone в отдельной директории и продолжить работу в ней. Можно пинять и отдельные изменения, но это тема для отдельной статьи.

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!)

Рабочий процесс в системе контроля версий 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 commit --amend --no-edit

Изменение сообщений нескольких коммитов

Для изменения коммита, расположенного раньше в вашей истории, вам нужно обратиться к более сложным инструментам.
В 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.

Примечание

Дрю Дево создал практическое руководство с упражнениями по использованию git rebase.
Найти его можно здесь: https://git-rebase.io/

Продвинутый инструмент: filter-branch

Существует ещё один способ переписывания истории, который вы можете использовать при необходимости изменить большое количество коммитов каким-то программируемым способом — например, изменить глобально ваш адрес электронной почты или удалить файл из всех коммитов.
Для этого существует команда filter-branch, и она может изменять большие периоды вашей истории, поэтому вы, возможно, не должны её использовать кроме тех случаев, когда ваш проект ещё не стал публичным и другие люди ещё не имеют наработок, основанных на коммитах, которые вы собираетесь изменить.
Однако, эта команда может быть очень полезной.
Далее вы ознакомитесь с несколькими обычными вариантами использованиями этой команды, таким образом, вы сможете получить представление о том, на что она способна.

Внимание

Команда git filter-branch таит в себе много подводных камней и более не является рекомендуемым способом изменения истории.
Вместо этого, рассмотрите возможность использования Python скрипта git-filter-repo, который лучше подходит для большинства ситуаций, в которых вы обычно используете filter-branch.
С документаций и исходным кодом скрипта можно ознакомиться здесь: https://github.com/newren/git-filter-repo.

Удаление файла из каждого коммита

Такое случается довольно часто.
Кто-нибудь случайно зафиксировал огромный бинарный файл, неосмотрительно выполнив 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 просто не могут возникнуть. Любая информация должна соответствовать вашему текущему уровню.

1. Самое простое решение многих проблем

Вы «натворили делов» со своим репозиторием, и вам кажется, что все сломалось и ничего не работает. В качестве максимально быстрого решения этой проблемы можно удалить локальный репозиторий (скрытую папку .git в корне вашего проекта) и репозиторий на GitHub, а затем выполнить git init. При этом ваши файлы удалять не нужно.

Когда я изучал Git, то десятки раз создавал и удалял свои репозитории, сталкиваясь с неразрешимыми для меня на тот момент проблемами.

Этот вариант имеет право на существование для начинающих разработчиков, т. к. они не обладают еще нужным набором знаний для применения более технологичных решений. Но, как вы понимаете, данный способ хорош до поры до времени.

2. Как изменить URL удаленного репозитория?

Вы неверно назвали свой репозиторий, например, вместо StartJava почему-то написали Lesson1 или JavaOps. На что наставник вам сказал, что его нужно переименовать.

Или вы удалили свой репозиторий на GitHub, а у себя на компьютере — оставили. После этого создали новый удаленный репозиторий (УР), изменив его первоначальное имя.

Например, URL репозитория выглядел так:


https://github.com/ichimax/lesson1.git

А после изменений ссылка стала следующей:


https://github.com/ichimax/startjava.git

При этом в настройках локального репозитория (ЛР) адрес старого репозитория никуда не делся — его нужно обновить.

Свяжем ЛР с новым адресом УР:


> git remote set-url origin новый_url.git
> git remote -v

Когда вам в дальнейшем потребуется выполнить push, то необходимо будет написать полную версию команды git push -u origin master, чтобы заново связать ЛР с новым УР, а затем снова можете использовать сокращенный вариант.

3. Как удалить файл из репозитория?

3.1. Удаление файла из индекса

Представим, что вы забыли занести в .gitignore правило, которое позволяло бы игнорировать class-файлы. В итоге применив команду git add, добавили их случайно в индекс.

Это очень частая ошибка у начинающих, которая влечет за собой попадание мусорных файлов в УР.

Скомпилируем исходник, создав class-файл:

Внесем изменения в класс из предыдущих статей:


import java.util.Scanner;

public class MyFirstApp {
    public static void main(String[] args) {
        System.out.println("У какого языка программирования следующий слоган:");
        System.out.print(""Написано однажды, ");
        System.out.println("работает везде!"");

        String answer = new Scanner(System.in).next();
        if (answer.equalsIgnoreCase("Java")) {
            System.out.println("Вы угадали");
        } else System.out.println("Увы, но - это Java");
    }
}

Далее введем привычные команды:


> git add . && git status
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   MyFirstApp.class
        modified:   MyFirstApp.java

MyFirstApp.class в итоге попал в индекс Git. Необходимо его оттуда удалить, т. к. отслеживание изменений у данного вида файлов не несет никакой практической пользы.

Воспользуемся командой, подсказанной Git:


> git restore --staged MyFirstApp.class && git status
Changes to be committed:
        modified:   MyFirstApp.java

Untracked files:
        MyFirstApp.class

MyFirstApp.class снова стал неотслеживаемым, а измененный класс остался в индексе не тронутым.

Чтобы обезопасить себя, перед использованием команды git add, всегда нужно просматривать список файлов (git status), которые вы планируете добавить в индекс. Также обязательно добавьте шаблон *.class в .gitignore, чтобы больше не спотыкаться о class-файлы.

3.2. Удаление файла из коммита

Пойдем дальше и научимся удалять MyFirstApp.class из коммита.


> git status

Changes to be committed:
        new file:   MyFirstApp.class
        modified:   MyFirstApp.java

Добавим все изменения в коммит:


> git commit -m "Добавил quiz по слогану Java"

Перед тем, как запушить коммит, программист решил посмотреть, какие файлы в него входят:


> git log -1 --name-only --oneline
ead5167 (HEAD -> master) Добавил quiz по слогану Java
src/MyFirstApp.class
src/MyFirstApp.java

С командной git log мы уже сталкивались в предыдущей статье. Из нового — аргумент —name-only, который позволяет вывести только имена измененных файлов, находящихся в коммите.

Использование log позволило заметить, что MyFirstApp.class оказался в коммите. Его нужно оттуда убрать.


> git reset --soft HEAD~1

Разберем эту команду по частям:

  • reset — отменяет коммиты
  • —soft — позволяет выполнить отмену коммита без потери изменений
  • HEAD — указатель на локальную ветку в которой мы находимся. В нашем случае HEAD~1 указывает на первый коммит в дереве коммитов ветки master

Более подробно про команду reset (1, 2).

Тут следует сказать, что HEAD указывает на ветку, в которой вы находитесь в данный момент. Благодаря ему мы можем перемещаться по дереву коммитов и откатываться к любому из них.

Получается следующая картина: HEAD указывает на ветку master, а master является указателем на вершину в дереве коммитов.

Отобразим, куда указывает HEAD:

* обозначается текущая ветка.

Если команду ввести с параметром -v, то кроме названия ветки отобразится информация о коммите, на который указывает master:


> git branch -v
* master b59d871 Изменил вывод текста, отображаемого в консоль

После отмены коммита отобразим список оставшихся коммитов:


> git log --oneline
b59d871 (HEAD -> master, origin/master) Изменил вывод текста, отображаемого в консоль
39ba195 Переименовал about.txt в README.md и внес в него описание проекта
1e36e0f Инициализация проекта

Видим, что благодаря reset последний коммит из него был исключен.

При этом log отображает ту же самую информацию, о которой мы говорили выше: HEAD указывает на master, а он — на вершину дерева коммитов (коммит b59d871).

Стоит отметить, что команду reset необходимо использовать только для отмены коммитов, которые находятся исключительно на вашем компьютере и еще не попали на УР. Т. к. эта команда меняет историю коммитов, то это может принести множество проблем для других людей, которые совместно с вами работают в одном репозитории, если вы отмените с ее помощью коммит, а затем запушите изменения на УР.

Посмотрим, в каком состоянии теперь находится ЛР:


> git status
Changes to be committed:
        new file:   MyFirstApp.class
        modified:   MyFirstApp.java

Что делать дальше, вы уже знаете: нужно убрать из индекса class-файл, и закоммитить java-класс.

Исключаем MyFirstApp.class:


> git restore --staged MyFirstApp.class
> git status
Changes to be committed:
        modified:   MyFirstApp.java

Untracked files:
        MyFirstApp.class

И тут вы понимаете, что забыли, как у вас назывался отмененный коммит: изменения остались все те же, значит, придумывать описание к коммиту нет смысла — нужно воспользоваться тем, которое было раньше.

Есть одна волшебная команда git reflog, которая отобразит все, что вы когда-либо сделали в своем репозитории с коммитами. Это своего рода лог всех ваших операций. Приведу его малую часть, которая нам необходима:


> git reflog
b59d871 (HEAD -> master, origin/master) HEAD@{0}: reset: moving to HEAD~1
ead5167 HEAD@{1}: commit: Добавил quiz по слогану Java

Из вывода видно описание отмененного коммита. Используем его для нового:


> git commit -m "Добавил quiz по слогану Java"

Больше подробностей о reflog по ссылке.

Не забудьте добавить *.class в.gitignore.

3.3. Удаление файла с GitHub

Самый неприятный случай — это когда мусорный файл все же пробрался на GitHub, т. е. был запушен:

В этом случае наставник вам может написать замечание: «на GitHub не должно быть файлов с расширением *.class, только *.java. Необходимо удалить из УР все class-файлы».

Самый простой выход из этой ситуации — это удалить в ЛР class-файлы, добавить в .gitignore маску *.class и сделать новый пуш. После этих действий на GitHub class-файл удалится. Он также автоматически удалится и у всех людей, которые работают с вами в одном репозитории. Например, когда наставник перед проверкой вашего ДЗ сделает git pull, то удаленные вами class-файлы, удалятся автоматически и у него.

Выглядеть это может примерно так:


D:JavaStartJava (master -> origin)
> git rm src*.class
rm 'src/MyFirstApp.class'


> git status
On branch master

Changes to be committed:
        new file:   .gitignore
        deleted:    src/MyFirstApp.class

> git commit -m "Добавил .gitignore с маской *.class"
[master 4bf0ddd] Добавил .gitignore с маской *.class
 2 files changed, 1 insertion(+)
 create mode 100644 .gitignore
 delete mode 100644 src/MyFirstApp.class

> git push

4. Как изменить описание коммита?

4.1. Коммит не был запушен

Вы сделали коммит, но еще его не запушили. Выясняется, что описание к нему содержит опечатку или это сообщение нужно изменить. Разберем данную ситуацию.

Внесем ряд изменений в файл MyFirstApp.java, добавив в него код, запрашивающий у участника квиза его имя, которое будет выводиться, если он ответит правильно на вопрос:


import java.util.Scanner;

public class MyFirstApp {
    public static void main(String[] args) {
        Scanner console = new Scanner(System.in, "cp866");
        System.out.print("Введите, пожалуйста, свое имя: ");
        String name = console.next();

        System.out.println("У какого языка программирования следующий слоган:");
        System.out.print(""Написано однажды, ");
        System.out.println("работает везде!"");

        String answer = console.next();
        if (answer.equalsIgnoreCase("Java")) {
            System.out.println(name + ", вы угадали!");
        } else System.out.println("Увы, но - это Java");
    }
}

> git add MyFirstApp.java
> git commit -m "Добавил ввод имени учасника"

Выясняется, что описание содержит опечатку в слове «учасника».

Для исправления воспользуемся двумя вариантами команды commit:

  • в первом случае при вводе git commit —amend, откроется редактор для изменения сообщения:

Такой способ подходит для исправления многострочных комментариев. Нам же больше подходит второй способ.

  • во втором случае команда ниже позволит внести изменение прямо из консоли:

> git commit --amend -m "Добавил ввод имени участника"
[master 96e2847] Добавил ввод имени участника

Убедимся, что описание к коммиту изменилось, а прежнее — не сохранилось, отобразив сокращенную информацию по последнему коммиту:


> git log --oneline -1
96e2847 (HEAD -> master) Добавил ввод имени участника

Следует обратить внимание на то, что данные способы позволяют внести изменение в описание только последнего коммита.

4.2.1. Принудительная перезапись истории

Что описание к коммиту нужно изменить, вы вспомнили, когда он был уже отправлен на GitHub.

Чтобы решить эту задачу, необходимо к команде из предыдущего пункта добавить git push -f, которая принудительно перезапишет коммит с ошибочным описанием — исправленным:


> git commit --amend -m "Добавил ввод имени участника"
> git push -f

Среди прочего, в консоли отобразится примерно следующая строка:


+ 5aa3d6d...ce3cf6f master -> master (forced update).

Она сообщает, что было сделано принудительное обновление репозитория.

У ваших коллег (или наставника) после ввода git pull отобразятся строки, тоже сообщающие о принудительном изменении и последующем успешном слиянии (merge):


+ 5aa3d6d...ce3cf6f master     -> origin/master  (forced update)
Merge made by the 'ort' strategy.

5. Как выполнить пуш, если Git не дает это сделать?

Представим, что на первых порах вы еще не до конца освоили Git и какое-то время вносили изменения в файлы прямо на GitHub. Но рано или поздно поняли, что для своего профессионального роста все же нужно изучить базовые команды Git, и применять их в консоли.

После этого у себя на компьютере начали писать код очередного домашнего задания, а затем решили его запушить на GitHub для проверки наставником. Но сделать пуш у вас в итоге не получилось, а в консоли отобразилась ошибка:


hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes

Возможна и другая ситуация, когда вы пушите в один репозиторий на GitHub с разных компьютеров. Например, днем, в свободное от работы время, порешали домашку, а затем запушили ее на удаленный репозиторий. Вечером, придя домой, продолжили писать код для следующего ДЗ. А про код, запушенный на GitHub, либо забыли, либо подумали, что из-за него проблем не будет. Но в действительности оказалось все иначе.

Или на GitHub случайно попали class-файлы и вы без задней мысли пошли на сайт и удалили их.

Во всех этих ситуациях пуш приведет к одной и той же ошибке, связанной с тем, что истории изменений на локальном и удаленном репозитории пошли разными путями: на GitHub есть коммит, которого нет в ЛР. В дереве коммитов на ЛР на какое-то количество коммитов меньше, чем на УР.

Научимся решать эту проблему.

5.1. Подтягивание изменений с GitHub

Зайдя в свой репозиторий на GitHub, я внес в README.md следующие мелкие изменения:

  • добавил картинку в виде шапки
  • пункт «Командная строка» заменил на конкретное название используемой программы — cmder
  • добавил иконки к каждому пункту списка

В качестве описания к коммиту указал следующий текст:

При этом README.md содержит следующий код:


# [StartJava](https://topjava.ru/startjava) -- курс на Java для начинающих

![image](https://user-images.githubusercontent.com/29703461/194078652-25a6e509-cdc6-4af4-9ab0-78b6b336c749.png)

## Используемые на курсе инструменты и технологии

:coffee: Java

:octocat: Git/GitHub

:pager: cmder

:bookmark_tabs: Sublime Text

:fire: Intellij IDEA

:gem: SQL

:elephant: PostgreSQL

:newspaper: psql

Проделанные только что изменения я благополучно забыл подтянуть на свой компьютер с помощью git pull. И продолжил работать над классом MyFirstApp.java в ЛР, добавив в него еще один квиз и немного мелких правок.

В итоге класс (у меня на компьютере, не в GitHub) стал выглядеть так:


import java.util.Scanner;

public class MyFirstApp {
    public static void main(String[] args) {
        Scanner console = new Scanner(System.in, "cp866");
        System.out.print("Введите, пожалуйста, свое имя: ");
        String name = console.nextLine();

        System.out.println("n1. У какого языка программирования следующий слоган:");
        System.out.print(""Написано однажды, ");
        System.out.println("работает везде!"");

        String answer = console.nextLine();
        if (answer.equalsIgnoreCase("Java")) {
            System.out.println(name + ", вы угадали!");
        } else System.out.println("Увы, но - это Java");

        System.out.println("n2. Какая фамилия у автора языка Java?");

        answer = console.nextLine();
        if (answer.equals("Гослинг") || answer.equals("Gosling")) {
            System.out.println(name + ", вы угадали!");
        } else System.out.println("Увы, но - это Гослинг (Gosling)");
    }
}

Добавил изменения в коммит:


> git add MyFirstApp.java
> git commit -m "Добавил quiz по автору Java"

Отобразим полученный результат в консоли:


> git log --oneline -1
7106587 (HEAD -> master) Добавил quiz по автору Java

А теперь самое интересное: попробуем сделать push:


> git push
To https://github.com/ichimax/startjava2.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'https://github.com/ichimax/startjava2.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Отобразилась ошибка, означающая, что в локальном и удаленном репозитории истории изменений пошли разными путями и не соответствуют друг другу.

Для решения этой проблемы Git предлагает выполнить команду pull, которая подтянет коммиты с УР, а затем объединит их с локальными, чтобы выровнять историю:


> git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 1.16 KiB | 62.00 KiB/s, done.
From https://github.com/ichimax/startjava2
   ce3cf6f..2bdecf7  master     -> origin/master
Merge made by the 'ort' strategy.
 README.md | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

В этом сообщении стоить обратить внимание на строку Merge made by the ‘ort’ strategy. Из нее мы можем сделать вывод, что произошло слияние (merge).

Слияние — это объединение истории коммитов из разных веток в одну. У нас ветка хоть и одна (master), но история коммитов на GitHub и в ЛР стала с какого-то момента различаться. Из-за этого и не удавалось сделать push. Когда мы выполнили git pull, то Git автоматически объединил (что бывает не всегда) последний коммит из УР с последним коммитом из ЛР, создав новый.

Не всегда Git может выполнить слияние автоматически, что вызывает конфликт слияния. Такое бывает, когда в разных ветках был изменен один и тот же фрагмент кода. Из-за этого Git не сможет определить, какую версию использовать. Для решения проблемы потребуется вмешательство пользователя. Более подробно об этом можно узнать по ссылке.

Для того, чтобы минимизировать возникновение подобных ситуаций (конфликтов), рекомендуется каждый раз перед началом работы с проектом делать git pull, чтобы синхронизировать историю коммитов ЛР с историей УР.

Отобразим 4 последних коммита:


> git log --oneline -4
1b4a7b7 (HEAD -> master) Merge branch 'master' of https://github.com/ichimax/startjava2
7106587 Добавил quiz по автору Java
2bdecf7 (origin/master) Обновил README.md
ce3cf6f Добавил ввод имени участника

Из этого списка видно, что:
2bdecf7 — был сделан на GitHub
7106587 — был сделан в ЛР
1b4a7b7 — получился автоматически после слияния двух предыдущих коммитов

Обратите внимание, что у коммита, сделанного на GitHub, ветка называется не master, а origin/master. Разница в том, что master — это имя локальной ветки, а origin/master является удаленной веткой с именем master.

Отобразим ветки, связанные с нашим репозиторием:


> git branch -a
* master
  remotes/origin/master

Ветка master является текущей (помечена *) веткой ЛР. remotes/origin/master — это ветвь с именем master и псевдонимом origin на УР.

То, что это — разные ветки, можно убедиться визуально, введя следующую команду:


D:JavaStartJavasrc (master -> origin)
> git log --pretty=format:"%h - %s" --graph
*   1b4a7b7 - Merge branch 'master' of https://github.com/ichimax/startjava2
| 
| * 2bdecf7 - Обновил README.md
* | 7106587 - Добавил quiz по автору Java
|/
* ce3cf6f - Добавил ввод имени участника
* 4bf0ddd - Добавил .gitignore с маской *.class
* 90ca67c - Добавил quiz по слогану Java
* b59d871 - Изменил вывод текста, отображаемого в консоль
* 39ba195 - Переименовал about.txt в README.md и внес в него описание проекта
* 1e36e0f - Инициализация проекта

Опция —graph позволяет вывести граф в формате ASCII, который показывает текущую ветку и историю слияний. Более подробно с разными опциями команды log можно ознакомиться по ссылке.

Давайте взглянем на различия в последних коммитах в удаленной и локальной master-ветках:


D:JavaStartJavasrc (master -> origin)
> git diff origin/master..master
diff --git a/src/MyFirstApp.java b/src/MyFirstApp.java
index 8e25560..08099ad 100644
--- a/src/MyFirstApp.java
+++ b/src/MyFirstApp.java
@@ -4,15 +4,22 @@ public class MyFirstApp {
     public static void main(String[] args) {
         Scanner console = new Scanner(System.in, "cp866");
         System.out.print("Введите, пожалуйста, свое имя: ");
-        String name = console.next();
+        String name = console.nextLine();

-        System.out.println("У какого языка программирования следующий слоган:");
+       System.out.println("n1. У какого языка программирования следующий слоган:");
         System.out.print(""Написано однажды, ");
         System.out.println("работает везде!"");

-        String answer = console.next();
+        String answer = console.nextLine();
         if (answer.equalsIgnoreCase("Java")) {
             System.out.println(name + ", вы угадали!");
         } else System.out.println("Увы, но - это Java");
+
+        System.out.println("n2. Какая фамилия у автора языка Java?");
+
+        answer = console.nextLine();
+        if (answer.equals("Гослинг") || answer.equals("Gosling")) {
+            System.out.println(name + ", вы угадали!");
+        } else System.out.println("Увы, но - это Гослинг (Gosling)");
     }
 }

После всех манипуляций отобразим состояние репозитория:


> git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)
 
nothing to commit, working tree clean

Посмотрим, какие два коммита мы должны запушить:


> git log --branches --not --remotes --oneline
1b4a7b7 (HEAD -> master) Merge branch 'master' of https://github.com/ichimax/startjava2
7106587 Добавил quiz по автору Java

Выполним push и снова введем команду, отображающую список из 4 коммитов:


> git push
> git log --oneline -4
1b4a7b7 (HEAD -> master, origin/master) Merge branch 'master' of https://github.com/ichimax/startjava2
7106587 Добавил quiz по автору Java
2bdecf7 Обновил README.md
ce3cf6f Добавил ввод имени участника

Видим, что история коммитов выровнялась: HEAD в ЛР и УР указывают на самый последний коммит 1b4a7b7.

Список решений тех или иных задач с помощью Git, разобранных в статье, с кратким описанием.

В статье мы рассмотрели самые простые, но часто возникающие проблемы, которые могут появиться при работе с Git и GitHub. Это базовый минимум, который поможет вам на первых порах не чувствовать себя растерянным, столкнувшись лицом к лицу с описанными сложностями.

Оцените статью, если она вам понравилась!

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.

  1. On the command line, navigate to the repository that contains the commit you want to amend.

  2. Type git commit --amend and press Enter.

  3. 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

  1. Follow the steps above to amend the commit message.
  2. 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.

  1. On the command line, navigate to the repository that contains the commit you want to amend.

  2. Use the git rebase -i HEAD~n command to display a list of the last n 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
  3. Replace pick with reword 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.
  4. Save and close the commit list file.

  5. In each resulting commit file, type the new commit message, save the file, and close it.

  6. 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»

Введение

В данном обучающем материале описаны различные способы перезаписи и изменения истории в Git. В Git используются несколько способов регистрации изменений. Мы обсудим плюсы и минусы различных способов и покажем примеры работы с ними. В данном обучающем материале описаны некоторые типовые причины перезаписи состояний кода и разъясняется, как избегать ошибок при таких операциях.

Основная задача Git — гарантировать, что вы не потеряете внесенные изменений. Но эта система также предназначена для предоставления вам полного контроля над процессом разработки. В числе прочего вы сами определяете то, как выглядит история вашего проекта. Такая свобода создает и вероятность потери коммитов. Git предоставляет команды для перезаписи истории, но предупреждает, что использование таких команд может привести к потере данных.

В Git существует несколько механизмов хранения истории и сохранения изменений. Вот эти механизмы: commit --amend, git rebase и git reflog. Это мощные инструменты для настройки рабочего процесса. По окончании этого обучающего материала вы будете знать команды, которые позволят реструктурировать коммиты Git, и сможете избегать проблем, с которыми приходится сталкиваться при перезаписи истории.

Изменение последнего коммита: git commit --amend

Команда git commit --amend — это удобный способ изменить последний коммит. Она позволяет объединить проиндексированные изменения с предыдущим коммитом без создания нового коммита. Ее можно использовать для редактирования комментария к предыдущему коммиту без изменения состояния кода в нем. Но такое изменение не только редактирует последний коммит, но и полностью его заменяет. То есть измененный коммит станет новой сущностью с отдельной ссылкой. Для Git он будет выглядеть как новый коммит, который отмечен звездочкой (*) на схеме внизу. Существует несколько распространенных сценариев использования команды git commit --amend. В следующих разделах мы расскажем о примерах ее использования.

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 откроет выбранный текстовый редактор и предложит объединить комментарии к указанным коммитам. Этот процесс можно показать следующим образом:

Обучающие материалы по Git: пример команды git rebase -i

Обратите внимание, что идентификаторы коммитов, измененных с помощью команды 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 вы можете столкнуться с ситуацией, когда вам нужно отредактировать сообщение коммита. Существует ряд причин, по которым вы хотите внести изменения, например, исправление опечатки, удаление конфиденциальной информации или добавление дополнительной информации.

В этой статье объясняется, как изменить сообщение о самых последних или старых коммитах в Git.

Команда git commit –amend позволяет вам изменить самое последнее сообщение о коммите.

Чтобы изменить сообщение самого последнего коммита, который не был передан в удаленный репозиторий, передайте его снова, используя флаг –amend.

  1. Перейдите в каталог хранилища в вашем терминале.
  2. Выполните следующую команду, чтобы поправить (изменить) сообщение о последнем коммите:
    git commit --amend -m "New commit message."

    Команда выполняет перезапись самого последнего коммита новым.

    Опция -m позволяет записать новое сообщение в командной строке без открытия редактора сессии.

Перед изменением сообщения о коммите вы также можете добавить другие ранее забытые изменения:

git add .git commit --amend -m "New commit message."

Измененный коммит – это новый объект с другим SHA-1. Предыдущий коммит больше не будет существовать в текущей ветке.

Как правило, вам следует избегать внесения изменений в коммит, который уже выдвинут, поскольку это может вызвать проблемы у людей, которые основывают свою работу на этом коммите. Хорошей идеей будет проконсультироваться со своими коллегами-разработчиками перед изменением принудительного коммита.

Если вы изменили сообщение о самом последнем введенном коммите, вам придется принудительно нажать его.

  1. Перейдите в хранилище.
  2. Исправьте сообщение о последнем введенном коммите:
    git commit --amend -m "New commit message."
  3. Принудительно нажмите, чтобы обновить историю удаленного хранилища:
    git push --force branch-name

Если вам нужно изменить сообщение о старых или нескольких коммитах, вы можете использовать интерактив git rebase для изменения одного или нескольких старых коммитов.

Команда rebase переписывает историю коммитов, и настоятельно не рекомендуется перебазировать коммиты, которые уже переданы в удаленный репозиторий Git .

  1. Перейдите в хранилище, содержащее сообщение фиксации, которое вы хотите изменить.
  2. Введите git rebase -i HEAD~N, где N число коммитов для выполнения перебазирования. Например, если вы хотите изменить 4-й и 5-й последние коммиты, введите:
    git rebase -i HEAD~5

    Команда отобразит последние X коммитов в текстовом редакторе по умолчанию :

    pick 43f8707f9 fix: update dependency json5 to ^2.1.1
    pick cea1fb88a fix: update dependency verdaccio to ^4.3.3
    pick aa540c364 fix: update dependency webpack-dev-server to ^3.8.2
    pick c5e078656 chore: update dependency flow-bin to ^0.109.0
    pick 11ce0ab34 fix: Fix spelling.
    
    # Rebase 7e59e8ead..11ce0ab34 onto 7e59e8ead (5 commands)
  3. Переход к линии сообщения фиксации, который вы хотите изменить, и замените pick на reword:
    reword 43f8707f9 fix: update dependency json5 to ^2.1.1
    reword cea1fb88a fix: update dependency verdaccio to ^4.3.3
    pick aa540c364 fix: update dependency webpack-dev-server to ^3.8.2
    pick c5e078656 chore: update dependency flow-bin to ^0.109.0
    pick 11ce0ab34 fix: Fix spelling.
    
    # Rebase 7e59e8ead..11ce0ab34 onto 7e59e8ead (5 commands)
  4. Сохраните изменения и закройте редактор.
  5. Для каждого выбранного коммита открывается новое окно текстового редактора. Измените сообщение о коммите, сохраните файл и закройте редактор.
    fix: update dependency json5 to ^2.1.1
  6. Принудительно отправить изменения в удаленный репозиторий:
    git push --force branch-name

Чтобы изменить самое последнее сообщение о коммите, используйте команду git commit –amend и, чтобы изменить более старое или несколько сообщений о коммите git rebase -i HEAD~N.

Не вносите изменения в принудительные коммиты, так как это может вызвать массу проблем у ваших коллег.

Если вы столкнулись с проблемой или у вас есть отзыв, оставьте комментарий ниже.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Как изменить ожирение
  • Как изменить описание коммита git
  • Как изменить ожидание экрана на компе
  • Как изменить описание дискорд сервера
  • Как изменить описание группы вконтакте

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии