Как изменить указанную фиксацию в git?


Я обычно представляю список коммитов для просмотра. Если у меня есть:

  • HEAD
  • Commit3
  • Commit2
  • Commit1

Я знаю, что могу изменить фиксацию головы с помощью git commit --amend, но как я могу изменить Commit1, учитывая, что это не HEAD совершал?

12 1744

12 ответов:

вы можете использовать git rebase, например, если вы хотите изменить back to commit bbc643cd, используя

$ git rebase --interactive 'bbc643cd^'

в Редакторе по умолчанию, измените pick до edit в строке, фиксацию которой вы хотите изменить. Внесите изменения, а затем зафиксируйте их с тем же сообщением, что и раньше:

$ git commit --all --amend --no-edit

изменить фиксацию, а после этого

$ git rebase --continue

чтобы вернуться к предыдущей фиксации головы.

предупреждение: обратите внимание, что это будет измените SHA-1 этого коммита как и все дети -- другими словами, это переписывает историю с этой точки вперед. вы можете разбить репозитории, делая это если вы нажмете с помощью команды git push --force

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

git rebase -i @~9   # Show the last 9 commits in a text editor

найти фиксацию вы хотите, изменить pick до e (edit), а также сохранить и закрыть файл. Git будет перемотать к этой фиксации, что позволит вам либо:

  • использовать git commit --amend внести изменения, или
  • использовать git reset @~ чтобы отменить последнюю фиксацию, но не изменения в файлах (т. е. привести вас к точке, в которой Вы были, когда вы редактировали файлы, но не совершали еще.)

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

затем запустите git rebase --continue, и Git будет воспроизводить последующие изменения поверх вашего измененного коммита. Вас могут попросить исправить некоторые конфликты слияния.

Примечание: @ является сокращением для HEAD и ~ - это совершить до указанной фиксации.

подробнее о переписывание истории в Git docs.


не бойтесь перебазировать

подсказка™: не бойтесь экспериментировать с "опасных" команд, которые переписывают историю* - Git не удалять свои совершает в течение 90 дней по умолчанию; вы можете найти их в reflog:

$ git reset @~3   # go back 3 commits
$ git reflog
c4f708b HEAD@{0}: reset: moving to @~3
2c52489 HEAD@{1}: commit: more changes
4a5246d HEAD@{2}: commit: make important changes
e8571e4 HEAD@{3}: commit: make some changes
... earlier commits ...
$ git reset 2c52489
... and you're back where you started

*следите за такими параметрами, как --hard и --force хотя - они могут отбрасывать данные.
* кроме того, не переписывайте историю на каких-либо ветках вы сотрудничаете.



на многих системах, git rebase -i откроет Vim по умолчанию. Vim не работает как большинство современных текстовых редакторов, так что взгляните на как перебазировать с помощью Vim. Если вы предпочитаете использовать другой редактор, измените его с помощью git config --global core.editor your-favorite-text-editor.

Interactive rebase С --autosquash это то, что я часто использую, когда мне нужно исправить предыдущие фиксации глубже в истории. Это существенно ускоряет процесс, который иллюстрирует ответ ZelluX, и особенно удобно, когда у вас есть несколько фиксаций, которые вам нужно отредактировать.

из документации:

--autosquash

когда сообщение журнала фиксации начинается с "сквош! ..." (или "исправить! ... "), и есть фиксация название которого начинается с того же..., автоматически изменяет список задач rebase-i так, чтобы фиксация, отмеченная для раздавливания, приходила сразу после фиксации, которая должна быть изменена

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

$ git log --graph --oneline
* b42d293 Commit3
* e8adec4 Commit2
* faaf19f Commit1

и у вас есть изменения, которые вы хотите изменить, чтобы Commit2 затем зафиксировать изменения с помощью

$ git commit -m "fixup! Commit2"

в качестве альтернативы вы можете использовать commit-sha вместо сообщения commit, так что "fixup! e8adec4 или даже просто префикс сообщение фиксации.

затем инициируйте интерактивную перебазировку на фиксацию перед

$ git rebase e8adec4^ -i --autosquash

ваш редактор откроется с уже правильно упорядоченными коммитами

pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3

все, что вам нужно сделать, это сохранить и выйти

Run:

$ git rebase --interactive commit_hash^

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

используя Vim вы меняете слова pick до reword для коммитов, которые вы хотите изменить, сохранить и выйти(:wq). Тогда git будет предложено для каждого коммита, который вы пометили как перефразировать, так что вы можете изменить сообщение коммита.

каждое сообщение фиксации вы должны сохранить и выйти(:wq) для перехода к следующему сообщению фиксации

если вы хотите выйти без сохранения изменений, нажмите клавишу :q!

EDIT: для навигации в vim вы используете j идти k идти вниз, h идти налево, и l идти направо( все это в NORMAL режим нажмите ESC перейти к NORMAL режим ). Для редактирования текста нажмите i так что вы вводите INSERT режим, в котором вы вставляете текст. Нажмите ESC to возвращайся к NORMAL режим :)

обновление: вот отличная ссылка из списка github как отменить (почти) что-нибудь с git

если по какой-то причине вам не нравятся интерактивные Редакторы, вы можете использовать git rebase --onto.

скажем, вы хотите изменить Commit1. Во-первых, ответвление от доCommit1:

git checkout -b amending [commit before Commit1]

во-вторых, захватите Commit1 С cherry-pick:

git cherry-pick Commit1

теперь внесите изменения, создав Commit1':

git add ...
git commit --amend -m "new message for Commit1"

и, наконец, после того, как вы спрятали любые другие изменения, пересадите остальные ваши коммиты до master на верхней части новый совершают:

git rebase --onto amending Commit1 master

читать: "rebase, на ветку amending, все коммиты между Commit1 (не включительно) и master (включительно)". То есть, Commit2 и Commit3, полностью вырезая старый Commit1. Вы могли бы просто выбрать вишню, но этот способ проще.

не забудьте очистить ваши ветви!

git branch -d amending

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

примечание: Я представляю этот подход для иллюстрации того, что вы можете сделать, а не повседневный вариант. Поскольку он имеет много шагов (и, возможно, некоторые предостережения.)

скажем, вы хотите изменить commit 0 и вы в настоящее время на feature-branch

some-commit---0---1---2---(feature-branch)HEAD

оформить заказ на эту фиксацию и создать quick-branch. Вы также можете клонировать свою ветвь функций в качестве точки восстановления (перед запуском).

?(git checkout -b feature-branch-backup)
git checkout 0
git checkout -b quick-branch

теперь у вас будет что-то вроде этого:

0(quick-branch)HEAD---1---2---(feature-branch)

смена сцены, припрятать все остальное.

git add ./example.txt
git stash

зафиксировать изменения и оформить заказ обратно в feature-branch

git commit --amend
git checkout feature-branch

теперь у вас будет что-то вроде этого:

some-commit---0---1---2---(feature-branch)HEAD
           \
             ---0'(quick-branch)

Rebase feature-branch на quick-branch (разрешить любые конфликты на этом пути). Применить тайник и удалить quick-branch.

git rebase quick-branch
git stash pop
git branch -D quick-branch

и вы в конечном итоге с:

some-commit---0'---1'---2'---HEAD(feature-branch)

Git не будет дублировать (хотя я не могу сказать, в какой степени) фиксацию 0 при перезагрузке.

Примечание: все хэши фиксации изменяются, начиная с фиксации, которую мы первоначально намеревались изменить.

полностью неинтерактивная команда(1)

я просто подумал, что поделюсь псевдонимом, который я использую для этого. Он основан на неинтерактивном интерактивные перебазирования. Чтобы добавить его в свой git, выполните эту команду (объяснение приведено ниже):

git config --global alias.amend-to '!f() { SHA=`git rev-parse ""`; git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"; }; f'

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


(1)учитывая, что нет никаких конфликтов во время перебазирования, of конечно

использование

git amend-to <REV> # e.g.
git amend-to HEAD~1
git amend-to aaaa1111

имя amend-to кажется подходящим ИМХО. Сравните поток с --amend:

git add . && git commit --amend --no-edit
# vs
git add . && git amend-to <REV>

объяснение

  • git config --global alias.<NAME> '!<COMMAND>' - создает глобальный псевдоним git с именем <NAME> который будет выполнять команду non-git <COMMAND>
  • f() { <BODY> }; f - "анонимная" функция bash.
  • SHA=`git rev-parse ""`; - преобразует аргумент в редакцию git и присваивает результат переменной SHA
  • git commit --fixup "$SHA" - исправление фиксации для SHA. Смотрите git-commit docs
  • GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
    • git rebase --interactive "$SHA^" часть была покрыта другими ответами.
    • --autosquash это то, что используется в сочетании с git commit --fixup см. git-rebase docs дополнительная информация
    • GIT_SEQUENCE_EDITOR=true это то, что делает все это не интерактивный. Этот хак я узнал из этого блога пост.

чтобы получить неинтерактивную команду, поместите скрипт с этим контентом в свой путь:

#!/bin/sh
#
# git-fixup
# Use staged changes to modify a specified commit
set -e
cmt=$(git rev-parse )
git commit --fixup="$cmt"
GIT_EDITOR=true git rebase -i --autosquash "$cmt~1"

используйте его, разместив свои изменения (с git add), а затем запустить git fixup <commit-to-modify>. Конечно, он все равно будет интерактивным, если вы получаете конфликты.

на основе документация

внесение изменений в сообщение более старых или нескольких сообщений фиксации

git rebase -i HEAD~3 

выше отображается список последних 3 фиксаций на текущей ветке, измените 3 на что-то еще, если вы хотите больше. Список будет выглядеть следующим образом:

pick e499d89 Delete CNAME
pick 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.

заменить pick С изменить перед каждым сообщением фиксации вы хотите изменить. Допустим, вы измените второй коммит в списке, ваш файл будет выглядеть следующим образом:

pick e499d89 Delete CNAME
reword 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.

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

Finaly Force-нажмите исправленные коммиты.

git push --force

для меня это было для удаления некоторых учетных данных из репозитория. Я попытался перебазировать и столкнулся с тонной, казалось бы, несвязанных конфликтов на этом пути, пытаясь перебазировать --продолжить. Не пытайтесь перебазировать себя, используйте инструмент под названием BFG (brew install bfg) на mac.

я решил эту,

1) путем создания новой фиксации с изменениями, которые я хочу..

r8gs4r commit 0

2) я знаю, какой коммит мне нужно слить с ним. который совершал 3.

и git rebase -i HEAD~4 # 4 представляет последние 4 фиксации (здесь фиксация 3 находится на 4-м месте)

3) в интерактивной ребазе последние фиксации будут расположены внизу. это будет похоже,

pick q6ade6 commit 3
pick vr43de commit 2
pick ac123d commit 1
pick r8gs4r commit 0

4) здесь нам нужно изменить фиксацию, если вы хотите слиться с конкретным. так и должно быть мол,

parent
|_child

pick q6ade6 commit 3
f r8gs4r commit 0
pick vr43de commit 2
pick ac123d commit 1

после перестановки вам нужно заменить ppick С f (исправления объединится без сообщения фиксации) или s (сквош слияние с сообщением фиксации может измениться во время выполнения)

а затем сохраните свое дерево.

теперь слияние сделано с существующей фиксацией.

Примечание: его не предпочтительный метод, если вы не поддерживаете самостоятельно. если у вас есть большой размер команды его не приемлемый метод переписать ГИТ дерево будет в конечном итоге в конфликтах, которые вы знаете, другие привычки. если вы хотите чтобы поддерживать вас дерево чистым с меньшим количеством коммитов можно попробовать это, и если его небольшая команда, в противном случае его не желательно.....

автоматизированные интерактивные перебазирования редактирование последующей фиксации вновь готов попробовать снова

я обнаружил, что исправляю прошлую фиксацию достаточно часто, чтобы я написал сценарий для нее.

вот рабочий процесс:

  1. git commit-edit <commit-hash>
    

    это приведет вас к фиксации, которую вы хотите изменить.

  2. исправить и этап фиксации, как вы хотите, чтобы это было в первую очередь.

    (вы можете использовать git stash save сохранить любые файлы, которые вы не совершали)

  3. повторить фиксацию с --amend, например:

    git commit --amend
    
  4. полное смещение:

    git rebase --continue
    

чтобы вышеперечисленное работало, поместите приведенный ниже скрипт в исполняемый файл с именем git-commit-edit где-то в $PATH:

#!/bin/bash

set -euo pipefail

script_name=${0##*/}

warn () { printf '%s: %s\n' "$script_name" "$*" >&2; }
die () { warn "$@"; exit 1; }

[[ $# -ge 2 ]] && die "Expected single commit to edit. Defaults to HEAD~"

# Default to editing the parent of the most recent commit
# The most recent commit can be edited with `git commit --amend`
commit=$(git rev-parse --short "${1:-HEAD~}")
message=$(git log -1 --format='%h %s' "$commit")

if [[ $OSTYPE =~ ^darwin ]]; then
  sed_inplace=(sed -Ei "")
else
  sed_inplace=(sed -Ei)
fi

export GIT_SEQUENCE_EDITOR="${sed_inplace[*]} "' "s/^pick ('"$commit"' .*)/edit \1/"'
git rebase --quiet --interactive --autostash --autosquash "$commit"~
git reset --quiet @~ "$(git rev-parse --show-toplevel)"  # Reset the cache of the toplevel directory to the previous commit
git commit --quiet --amend --no-edit --allow-empty  #  Commit an empty commit so that that cache diffs are un-reversed

echo
echo "Editing commit: $message" >&2
echo