Как отменить несколько «git commit --amend» или получить разницу для каждого изменения коммита? - PullRequest
0 голосов
/ 24 сентября 2018

Я сделал по ошибке более 100 изменений.Как я могу преобразовать их в обычные коммиты?Или, по крайней мере, получить git log с разницей для каждого изменения коммита?Я вижу разницу только для всех изменений, если я запускаю gitk или git log -p сейчас.Я могу видеть все поправки фиксирует хэши, но только с reflog.Я также вижу разницу с git diff amend_hash1 amend_hash2, но не с gitk или git log -p.Они перепрыгивают через эти компенсации, хотя они правильно связаны в .git/logs/refs/heads/master и .git/logs/HEAD

. Я просто запустил git commit --amend 100 раз, по одному разу для каждого из 100 изменений.Затем я получил один большой коммит на все 100 изменений, когда я запустил git commit без изменений.

Я нашел, как отменить только один коммит с изменениями ...

Ответы [ 3 ]

0 голосов
/ 24 сентября 2018

Эти коммиты отображаются как один коммит в gitk - l0pan

Я подозреваю, что на самом деле произошло, вы раздавили 100 коммитоввместе, возможно, с git merge --squash.Вам повезло, это гораздо проще изменить, чем фактически внести 100 коммитов (что было бы очень необычно).Нам нужно найти исходный заголовок ветви.

Если вам повезет, ORIG_HEAD по-прежнему будет установлен на исходный заголовок ветви.Проверьте с помощью git log ORIG_HEAD.

В противном случае используйте git reflog, чтобы найти верхушку ветви до того, как вы произвели слияние в сквош.Вы ищете что-то вроде этого ...

a83ad6b (master) HEAD@{1}: commit: Squashed commit of the following:
78c06ed HEAD@{2}: checkout: moving from feature to master

a83ad6b - это новый сдавленный коммит.78c06ed - это исходный, несокрушенный наконечник ветви.

Как только вы найдете исходный коммит, верните к нему свою ветку.

git branch -f <commit id>

Если вы действительно исправили100 коммитов, вот как вы их найдете.

Вы бы использовали git reflog, чтобы найти оригинальные без изменений коммиты.Вы ищете что-то вроде этого ...

6493a4c HEAD@{2}: commit (amend): The log message
a2a99ea HEAD@{3}: commit: The log message

a2a99ea - это исходный, без изменений коммит.6493a4c - это новый, исправленный коммит.

Как правило, вы можете использовать grep для commit (amend) и использовать предыдущий reflog с тем же сообщением коммита.Однако это не гарантировано.Например, между фиксацией и поправкой могут быть проверки и извлечения.

0742a3a HEAD@{111}: commit (amend): chore: Add a simple monitoring process.
b71620d HEAD@{112}: pull: checkout e29402b1fda83664cf17782b1a34e2bb4a77d44f: returning to refs/heads/master
b71620d HEAD@{113}: pull: checkout e29402b1fda83664cf17782b1a34e2bb4a77d44f: chore: Add a simple monitoring process.
e29402b HEAD@{114}: pull: checkout e29402b1fda83664cf17782b1a34e2bb4a77d44f
fab188c HEAD@{115}: commit: chore: Add a simple monitoring process.

Или поправка может изменить сообщение о фиксации.

6493a4c HEAD@{2}: commit (amend): A different log message
a2a99ea HEAD@{3}: commit: The log message
0 голосов
/ 24 сентября 2018

Я думаю, что это более объяснимо с помощью картинок, хотя моя способность к ASCII-искусству заканчивается через несколько --amends.Хитрость заключается в том, чтобы понять, что git commit --amend вместо git commit без --amend изменяет родительский хеш , сохраненный в новом коммите.

Нормальный git commitзамораживает содержимое индекса в дереве и делает новый коммит, используя новое дерево, вас как автора и коммиттера, ваше сообщение журнала и текущий коммит в качестве родителя нового коммита:

...--F--G   <-- [you were here]
         \
          H   <-- branch (HEAD) [you are here now]

Затем мы делаем новый коммит I, после выпрямления чертежа:

...--F--G--H--I   <-- branch (HEAD)

и нового коммита J:

...--F--G--H--I--J   <-- branch (HEAD)

и т. Д.

Используя git commit --amend, мы делаем новый коммит H как обычно , за исключением , в котором родительский из H равен F вместо G:

       G   [you were here]
      /
...--F--H   <-- branch (HEAD)

Затем, делая I, мы делаем как обычно за исключением , что родительский из I равен F вместо H:

       G   [you were here]
      /
...--F--H   [you were here too]
      \
       I   <-- branch (HEAD)

и т. Д.

Если представить, что в этот момент выполняется git log с коммитом I, указывающим назад на коммит F - вы увидите коммит I, затем коммит F, затем E и так далее.Коммиты G и H будут невидимы для gitk или git log (но будут отображаться в выводе git reflog, поскольку это не следует родительским цепочкам).

После 100 таких операцийУ меня закончились письма о коммитах, и я не могу привлечь безумного поклонника коммитов, указывающих на F, но вы можете их себе представить;или я могу нарисовать всего 9 таких коммитов, от G до O:

     GH
     | I
     |/ J
...--F==K
     |\ L
     | M
     ON

Каждый из этих коммитов имеет дерево и сообщение , котороеты хочешь.Что не так с каждым из этих коммитов, за исключением самого G, так это то, что у него неправильный parent: вы хотите, чтобы G имел F в качестве родителя (что он и делает), но тогда вы бы хотели, чтобы H имел G в качестве родителя (чего у него нет).

Это означает, что вы должны скопировать неправильных фиксаций.Давайте начнем с копирования H в H', у которого G в качестве родителя, но в противном случае используется то же дерево и , сообщение и другие метаданные как H:

      H'
     /
     GH
     | I
     |/ J
...--F==K
     |\ L
     | M
     ON

Теперь нам нужно скопировать I в новый коммит I'.Родитель I' не G, а H':

      H'-I'
     /
     GH
     | I
     |/ J
...--F==K
     |\ L
     | M
     ON

Мы повторяем для J до J', используя I' в качестве родителя для J', ии так до тех пор, пока мы не скопируем каждый «неправильный» коммит в «правильный».Затем мы можем установить имя ветви, указывающее на последний такой скопированный коммит:

      H'-I'-J'-K'-L'-M'-N'-O'   <-- repaired
     /
     GH
     | I
     |/ J
...--F==K
     |\ L
     | M
     ON

Запуск git log при repaired или gitk --all теперь будет показывать коммит N', ведущий кM' возвращение к L' и так далее.Помните, что git loggitk) следуют за родительскими связями в обратном направлении, вообще не просматривая reflog.

Если вы хотите разрешить использование некоторых метаданных (имя автора и коммиттера, электронная почта,и метка времени), каждый из этих коммитов легко сделать с помощью цикла сценария оболочки, используя git commit-tree.если вы хотите сохранить эти метаданные, это сложнее: вам нужно установить серию переменных среды Git перед каждым вызовом git commit-tree, установив:

  • GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE: для автора
  • GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL, GIT_COMMITTER_DATE: аналогично, но для коммиттера

Сообщение журнала можно скопировать непосредственно из некорректных фиксаций, используяsed или аналогичный, чтобы отрубить все до первой пустой строки включительно (обратите внимание, что это отбрасывает любые данные кодирования i18n, и sed может плохо работать с неопределенными окончательными строками текста в сообщениях фиксации, но это может быть допустимо, еслинет, извлеките соответствующий код из git filter-branch).

Используйте git reflog для получения хэш-идентификаторов всех коммитов, которые должны быть скопированы, в правильном порядке (сначала самый старый для копирования, последний для того, чтобыкопия = последняя последняя)Поместите их в файл, по одной записи в строке.Тогда, вероятно, будет достаточно следующего непроверенного сценария оболочки:

parent=$hash  # hash ID of commit G, onto which new chain will be built
while read tocopy; do
    tree=$(git rev-parse $tocopy^{tree})
    parent=$(git cat-file -p $tocopy | sed '1,/^$/d' |
        git commit-tree -p $parent -t $tree)
done < $file_of_commits
git branch repaired $parent

При этом создается новое имя ветви repaired для хранения только что созданной цепочки.

0 голосов
/ 24 сентября 2018

Все они находятся в рефлоге репо, который внес изменения, и они совершенно обычные коммиты, так что вы можете, например, git show их увидеть, какие изменения они сделали, и git cherry-pick их, чтобы получить эти изменения и т.д.на.См. git reflog документы и посмотрите, например, .git/logs/refs/heads/master, чтобы увидеть, в чем дело.

...