Как изменить указанную фиксацию в git?
Я обычно представляю список коммитов для просмотра. Если у меня есть:
-
HEAD
-
Commit3
-
Commit2
Commit1
Я знаю, что могу изменить фиксацию головы с помощью git commit --amend
, но как я могу изменить Commit1
, учитывая, что это не HEAD
совершал?
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
docsGIT_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
после перестановки вам нужно заменить
p
pick
Сf
(исправления объединится без сообщения фиксации) илиs
(сквош слияние с сообщением фиксации может измениться во время выполнения)а затем сохраните свое дерево.
теперь слияние сделано с существующей фиксацией.
Примечание: его не предпочтительный метод, если вы не поддерживаете самостоятельно. если у вас есть большой размер команды его не приемлемый метод переписать ГИТ дерево будет в конечном итоге в конфликтах, которые вы знаете, другие привычки. если вы хотите чтобы поддерживать вас дерево чистым с меньшим количеством коммитов можно попробовать это, и если его небольшая команда, в противном случае его не желательно.....
автоматизированные интерактивные перебазирования редактирование последующей фиксации вновь готов попробовать снова
я обнаружил, что исправляю прошлую фиксацию достаточно часто, чтобы я написал сценарий для нее.
вот рабочий процесс:
git commit-edit <commit-hash>
это приведет вас к фиксации, которую вы хотите изменить.
исправить и этап фиксации, как вы хотите, чтобы это было в первую очередь.
(вы можете использовать
git stash save
сохранить любые файлы, которые вы не совершали)повторить фиксацию с
--amend
, например:git commit --amend
полное смещение:
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