Как изменить указанный коммит?

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

  1. HEAD
  2. Commit3
  3. Commit2
  4. Commit1

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

1759
задан Sam Liao 27 июля '09 в 8:19
источник поделиться
12 ответов

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

$ git rebase --interactive 'bbc643cd^'

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

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

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

$ git rebase --continue

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

ПРЕДУПРЕЖДЕНИЕ. Обратите внимание, что это изменит SHA-1 этого комманда , а также всех детей - другими словами, это переписывает историю с этой точки вперед, Если вы нажмете с помощью команды git push --force

2309
ответ дан ZelluX 27 июля '09 в 8:28
источник поделиться

Используйте потрясающую интерактивную 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 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.

327
ответ дан Zaz 29 апр. '15 в 20:50
источник поделиться

Интерактивный 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

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

62
ответ дан thrau 29 сент. '15 в 20:59
источник поделиться

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

31
ответ дан betoharres 02 июля '15 в 22:11
источник поделиться

Если по какой-то причине вам не нравятся интерактивные редакторы, вы можете использовать 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
13
ответ дан FeepingCreature 22 окт. '16 в 15:19
источник поделиться

Полностью неинтерактивная команда (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 - это то, что делает все это не интерактивным. Этот взлом я узнал из этого сообщения в блоге.
6
ответ дан Dethariel 27 февр. '18 в 4:47
источник поделиться

Пришел к этому подходу (и это, вероятно, точно так же, как с использованием интерактивной 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 совершает при перезагрузке.

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

6
ответ дан Olga 01 июня '16 в 14:57
источник поделиться

Чтобы получить неинтерактивную команду, поместите скрипт с этим контентом в свой 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>. Конечно, он будет интерактивным, если вы столкнетесь с конфликтами.

5
ответ дан Pelle Nilsson 16 янв. '18 в 18:27
источник поделиться

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

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

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
4
ответ дан justMe 17 мая '18 в 11:38
источник поделиться

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

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

Здесь рабочий процесс:

  1. git commit-edit <commit-hash>
    

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

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

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

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

    git commit --amend
    
  4. Заполните 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 "$@"; 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
2
ответ дан Tom Hale 14 сент. '18 в 6:32
источник поделиться

Я решил это,

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

1
ответ дан Mohideen ibn Mohammed 05 янв. '18 в 21:35
источник поделиться

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

1
ответ дан Pellet 07 нояб. '17 в 10:24
источник поделиться

Другие вопросы по меткам или Задайте вопрос