Как изменить указанный коммит?
Обычно я представляю список коммитов для проверки. Если у меня есть следующие коммиты:
-
HEAD
-
Commit3
-
Commit2
-
Commit1
... Я знаю, что могу модифицировать head commit с помощью git commit --amend
. Но как я могу изменить Commit1
, учитывая, что это не HEAD
commit?
Ответы
Ответ 1
Вы можете использовать git rebase. Например, если вы хотите изменить коммит bbc643cd
, запустите
$ git rebase --interactive 'bbc643cd^'
Обратите внимание на каретку ^
в конце команды, потому что на самом деле вам нужно вернуть обратно коммит перед тем, который вы хотите изменить.
В редакторе по умолчанию измените pick
на edit
в строке с упоминанием "bbc643cd".
Сохраните файл и выйдите: git будет интерпретировать и автоматически выполнять команды в файле. Вы окажетесь в предыдущей ситуации, в которой вы только что создали коммит bbc643cd
.
На этом этапе bbc643cd
является вашим последним коммитом, и вы можете легко изменить его: внести изменения, а затем зафиксировать их командой:
$ git commit --all --amend --no-edit
После этого введите:
$ git rebase --continue
чтобы вернуться к предыдущему коммиту HEAD.
ПРЕДУПРЕЖДЕНИЕ: обратите внимание, что это изменит SHA-1 этого коммита , а также всех дочерних элементов - другими словами, это переписывает историю с этого момента. Вы можете разорвать репо, выполнив это, если нажмете с помощью команды git push --force
Ответ 2
Используйте потрясающую интерактивную rebase:
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.
Не бойтесь переустанавливать
ProTip ™: не бойтесь экспериментировать с "опасными" командами, которые переписывают историю * - Git не удаляет ваши фиксации в течение 90 дней по умолчанию; вы можете найти их в reflog:
$ git reset @~3 # go back 3 commits
$ git reflog
c4f708b [email protected]{0}: reset: moving to @~3
2c52489 [email protected]{1}: commit: more changes
4a5246d [email protected]{2}: commit: make important changes
e8571e4 [email protected]{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
.
Ответ 3
Интерактивный rebase с --autosquash
- это то, что я часто использую, когда мне нужно исправлять предыдущие, фиксирует глубже в истории. Это существенно ускоряет процесс, который иллюстрирует ответ ZelluX, и особенно удобен, когда у вас есть несколько коммитов, которые нужно редактировать.
Из документации:
--autosquash
Когда сообщение журнала фиксации начинается с "squash!..." (или "fixup!..." ), и есть коммит, чей заголовок начинается с того же..., автоматически изменяет список todo reba -i так что фиксация, помеченная для раздачи, появляется сразу после модификации фиксации
Предположим, что у вас есть история, которая выглядит так:
$ git log --graph --oneline
* b42d293 Commit3
* e8adec4 Commit2
* faaf19f Commit1
и у вас есть изменения, которые вы хотите внести в Commit2, затем выполните свои изменения, используя
$ git commit -m "fixup! Commit2"
альтернативно вы можете использовать commit-sha вместо сообщения фиксации, поэтому "fixup! e8adec4
или даже просто префикс сообщения фиксации.
Затем инициируйте интерактивную перезагрузку для фиксации до
$ git rebase e8adec4^ -i --autosquash
ваш редактор откроется с правильно заказанными коммитами
pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3
все, что вам нужно сделать, это сохранить и выйти
Ответ 4
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
, чтобы вернуться в режим NORMAL
:)
UPDATE. Здесь отличная ссылка из списка github Как отменить (почти) что-нибудь с git
Ответ 5
Если по какой-то причине вам не нравятся интерактивные редакторы, вы можете использовать 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
поверх вашего
new commit:
git rebase --onto amending Commit1 master
Читайте: "rebase, на ветку amending
, все совершает транзакции между Commit1
(не включительно) и master
(включительно)". То есть Commit2 и Commit3 полностью разрезают старый Commit1. Вы могли бы просто выбрать вишню, но это проще.
Не забудьте очистить ветки!
git branch -d amending
Ответ 6
Полностью неинтерактивная команда (1)
Я просто подумал, что у меня есть псевдоним, который я использую для этого. Он основан на неинтерактивной интерактивной перестановке. Чтобы добавить его в свой git, запустите эту команду (пояснение ниже):
git config --global alias.amend-to '!f() { SHA='git rev-parse "$1"'; git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"; }; f'
Самым большим преимуществом этой команды является то, что она не-vim.
(1), учитывая, что во время rebase конфликтов нет, конечно
использование
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
f() { <BODY> }; f
- "анонимная" функция bash. -
SHA='git rev-parse "$1"';
- преобразует аргумент в 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
для получения дополнительной информации -
GIT_SEQUENCE_EDITOR=true
- это то, что делает все это не интерактивным. Этот взлом я узнал из этого сообщения в блоге.
Ответ 7
Основываясь на документации
Изменение сообщения старых или нескольких сообщений фиксации
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 e499d89 Delete CNAME
reword 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.
Сохраните и закройте файл списка фиксации, появится новый редактор для изменения вашего сообщения фиксации, изменения сообщения фиксации и сохранения.
Завершить принудительное принудительное изменение.
git push --force
Ответ 8
Пришел к этому подходу (и это, вероятно, точно так же, как с использованием интерактивной rebase), но для меня это довольно просто.
Примечание. Я представляю этот подход для иллюстрации того, что вы можете сделать, а не повседневной альтернативы. Поскольку он имеет много шагов (и, возможно, некоторые оговорки.)
Предположим, вы хотите изменить 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
(разрешить любые конфликты на этом пути). Примените stash и удалите quick-branch
.
git rebase quick-branch
git stash pop
git branch -D quick-branch
И вы получите:
some-commit---0'---1'---2'---HEAD(feature-branch)
Git не будет дублировать (хотя я не могу сказать, в какой степени) 0 совершает при перезагрузке.
Примечание: все хеши фиксации изменены, начиная с фиксации, которую мы изначально планировали изменить.
Ответ 9
Автоматическое редактирование интерактивной ребазы, за которым следует фиксация готовности, готовая к выполнению
Я обнаружил, что фиксирую прошлую фиксацию достаточно часто, чтобы написать для нее сценарий.
Здесь рабочий процесс:
-
git commit-edit <commit-hash>
Это выведет вас на фиксацию, которую вы хотите отредактировать.
-
Исправьте и выполните фиксацию, как вы хотели, в первую очередь.
(Вы можете использовать git stash save
чтобы сохранить файлы, которые вы не совершаете)
-
Повторите фиксацию с помощью --amend
, например:
git commit --amend
-
Заполните rebase:
git rebase --continue
Чтобы выше работали, поместите приведенный ниже сценарий в исполняемый файл с именем git-commit-edit
где git-commit-edit
нибудь в вашем $PATH
:
#!/bin/bash
set -euo pipefail
script_name=${0##*/}
warn () { printf '%s: %s\n' "$script_name" "$*" >&2; }
die () { warn "[email protected]"; 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
Ответ 10
Чтобы получить неинтерактивную команду, поместите скрипт с этим контентом в свой PATH:
#!/bin/sh
#
# git-fixup
# Use staged changes to modify a specified commit
set -e
cmt=$(git rev-parse $1)
git commit --fixup="$cmt"
GIT_EDITOR=true git rebase -i --autosquash "$cmt~1"
Используйте его, разместив свои изменения (с git add
), а затем запустите git fixup <commit-to-modify>
. Конечно, он будет интерактивным, если вы столкнетесь с конфликтами.
Ответ 11
Я решил это,
1) путем создания новой фиксации с изменениями, которые я хочу.
r8gs4r commit 0
2) Я знаю, какой фиксации мне нужно объединить с ним. который является фиксацией 3.
поэтому, git rebase -i HEAD~4
# 4 представляет недавний 4 фиксации (здесь commit 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
(fixup будет сливаться без сообщения фиксации) или s
(слияние squash с сообщением фиксации может измениться во время выполнения)
а затем сохраните свое дерево.
теперь слияние выполняется с существующей фиксацией.
Примечание. Это не предпочтительный метод, если вы не поддерживаете его самостоятельно. если у вас большой размер команды, его не приемлемый метод для перезаписи дерева git закончится конфликтами, которые, как вы знаете, будут другими. если вы хотите сохранить свое дерево чистым с меньшим количеством коммитов, можете попробовать это, и если его небольшая команда в противном случае не будет предпочтительнее.....
Ответ 12
git stash
+ rebase
автоматизация
Когда мне нужно много раз модифицировать старый коммит для обзоров Геррита, я делаю:
git-amend-old() (
# Stash, apply to past commit, and rebase the current branch on to of the result.
current_branch="$(git rev-parse --abbrev-ref HEAD)"
apply_to="$1"
git stash
git checkout "$apply_to"
git stash apply
git add -u
git commit --amend --no-edit
new_sha="$(git log --format="%H" -n 1)"
git checkout "$current_branch"
git rebase --onto "$new_sha" "$apply_to"
)
GitHub вверх по течению.
Использование:
- изменить файл
-
git-amend-old $old_sha
Мне нравится это над --autosquash
поскольку оно не раздавливает другие несвязанные исправления.
Ответ 13
Для меня это было для удаления некоторых учетных данных из репо.
Я попытался переустановить и столкнулся с тонкой, казалось бы, несвязанными конфликтами на пути, когда пытался переустановить, - продолжайте.
Не утруждайте себя попыткой перезагрузить себя, используйте инструмент под названием BFG (brew install bfg) на mac.
Ответ 14
Если вы еще не выдвинули коммиты, вы можете вернуться к предыдущему коммиту, используя git reset HEAD^[1,2,3,4...]
Например
git commit <file1> -m "Updated files 1 and 2"
git commit <file3> -m "Updated file 3"
Ой, забыл добавить file2 в первый коммит...
git reset HEAD^1 // because I only need to go back 1 commit
git add <file2>
Это добавит file2 к первому коммиту.
Ответ 15
Ну, это решение может показаться очень глупым, но может спасти вас в определенных условиях.
Мой друг только что натолкнулся на случайную фиксацию очень больших файлов (четыре автоматически сгенерированных файла размером от 3 до 5 ГБ каждый), а затем сделал еще несколько коммитов кода, прежде чем понял, что git push
не работает !
Файлы были перечислены в .gitignore
но после переименования папки контейнера они были открыты и зафиксированы! И теперь было еще несколько коммитов кода поверх этого, но push
работал вечно (пытаясь загрузить ГБ данных!) И в конечном итоге потерпел неудачу из-за ограничений размера файла Github.
Проблема с интерактивным перебазированием или чем-то подобным заключалась в том, что им приходилось ковыряться в этих огромных файлах, и им потребовалось бы целую вечность, чтобы что-то сделать. Тем не менее, потратив почти час в CLI, мы не были уверены, действительно ли файлы (и дельты) удалены из истории или просто не включены в текущие коммиты. Толчок тоже не работал, и мой друг действительно застрял.
Итак, решение, которое я придумал, было:
- Переименуйте текущую папку git в
~/Project-old
. - Снова клонируйте папку git из github (в
~/Project
). - Оформить заказ на ту же ветку.
- Вручную
cp -r
файлы из ~/Project-old
folder в ~/Project
. - Убедитесь в том, что массивные файлы, которые не должны были быть проверены в это
mv
- е изд, а также включены в .gitignore
правильно. - Также убедитесь, что вы не перезаписываете папку
.git
в недавно клонированном ~/Project
старой. Это где журналы проблемной истории живет! - Теперь просмотрите изменения. Это должен быть союз всех последних коммитов, исключая проблемные файлы.
- Наконец, внесите изменения, и это хорошо, когда вас
push
.
Самая большая проблема этого решения заключается в том, что оно имеет дело с ручным копированием некоторых файлов, а также объединяет все последние коммиты в один (очевидно, с новым хешем коммитов.) B
Большим преимуществом является то, что он очень понятен на каждом этапе, отлично работает с огромными файлами (а также с чувствительными) и не оставляет следов в истории!