Вопрос: Как изменить указанный коммит в git?


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

  • HEAD
  • Commit3
  • Commit2
  • Commit1

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


1670


источник


Ответы:


Вы можете использовать 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


2191



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


298



интерактивный перебазировать с --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 вместо сообщения commit, поэтому "fixup! e8adec4или даже просто префикс сообщения фиксации.

Затем инициируйте интерактивную перезагрузку

$ git rebase e8adec4^ -i --autosquash

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

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

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


54



Бег:

$ git rebase --interactive commit_hash^

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

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

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

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

РЕДАКТИРОВАТЬ : для навигации по vimты используешь jподняться, kспускаться, hидти влево и lидти вправо (все это в NORMALрежиме, нажмите ESCидти NORMALРежим ). Чтобы отредактировать текст, нажмите iтак что вы вводите INSERTрежим, в который вы вставляете текст. Нажмите ESCвернуться к NORMALРежим :)

ОБНОВИТЬ : Вот отличная ссылка из списка github Как отменить (почти) что угодно с git


30



Если по какой-то причине вам не нравятся интерактивные редакторы, вы можете использовать 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

11



Came to this approach (and it is probably exactly the same as using interactive rebase) but for me it's kind of straightforward.

Note: I present this approach for the sake of illustration of what you can do rather than an everyday alternative. Since it has many steps (and possibly some caveats.)

Say you want to change commit 0 and you are currently on feature-branch

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

Checkout to this commit and create a quick-branch. You can also clone your feature branch as a recovery point (before starting).

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

You will now have something like this:

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

Stage changes, stash everything else.

git add ./example.txt
git stash

Commit changes and checkout back to feature-branch

git commit --amend
git checkout feature-branch

You will now have something like this:

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

Rebase feature-branch onto quick-branch (resolve any conflicts along the way). Apply stash and remove quick-branch.

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

And you end up with:

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

Git will not duplicate (although I can't really say to what extent) the 0 commit when rebasing.

Note: all commit hashes are changed starting from the commit we originally intended to change.


6



Completely non-interactive command(1)

I just thought I'd share an alias that I'm using for this. It's based on non-interactive interactive rebase. To add it to your git, run this command (explanation given below):

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'

The biggest advantage of this command is the fact that it's no-vim.


(1)given that there are no conflicts during rebase, of course

Usage

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

The name amend-to seems appropriate IMHO. Compare the flow with --amend:

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

Explanation

  • git config --global alias.<NAME> '!<COMMAND>' - creates a global git alias named <NAME> that will execute non-git command <COMMAND>
  • f() { <BODY> }; f - an "anonymous" bash function.
  • SHA=`git rev-parse "$1"`; - converts the argument to git revision, and assigns the result to variable SHA
  • git commit --fixup "$SHA" - fixup-commit for SHA. See git-commit docs
  • GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
    • git rebase --interactive "$SHA^" part has been covered by other answers.
    • --autosquash is what's used in conjunction with git commit --fixup, see git-rebase docs for more info
    • GIT_SEQUENCE_EDITOR=true is what makes the whole thing non-interactive. This hack I learned from this blog post.

3



To get a non-interactive command, put a script with this content in your 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"

Use it by staging your changes (with git add) and then run git fixup <commit-to-modify>. Of course, it will still be interactive if you get conflicts.


2



For me it was for removing some credentials from a repo. I tried rebasing and ran into a ton of seemingly unrelated conflicts along the way when trying to rebase --continue. Don't bother attempting to rebase yourself, use the tool called BFG (brew install bfg) on mac.


1



I solved this,

1) by creating new commit with changes i want..

r8gs4r commit 0

2) i know which commit i need to merge with it. which is commit 3.

so, git rebase -i HEAD~4 # 4 represents recent 4 commit (here commit 3 is in 4th place)

3) in interactive rebase recent commit will located at bottom. it will looks alike,

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

4) here we need to rearrange commit if you want to merge with specific one. it should be like,

parent
|_child

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

after rearrange you need to replace p pick with f (fixup will merge without commit message) or s (squash merge with commit message can change in run time)

and then save your tree.

now merge done with existing commit.

Note: Its not preferable method unless you're maintain on your own. if you have big team size its not a acceptable method to rewrite git tree will end up in conflicts which you know other wont. if you want to maintain you tree clean with less commits can try this and if its small team otherwise its not preferable.....


1