私は通常、確認のためにコミットのリストを送信します。次のコミットがある場合:
HEAD
Commit3
Commit2
Commit1
...でヘッドコミットを変更できることはわかっていますgit commit --amend
。しかしCommit1
、それがHEAD
コミットではない場合、どのように変更できますか?
私は通常、確認のためにコミットのリストを送信します。次のコミットがある場合:
HEAD
Commit3
Commit2
Commit1
...でヘッドコミットを変更できることはわかっていますgit commit --amend
。しかしCommit1
、それがHEAD
コミットではない場合、どのように変更できますか?
回答:
git rebaseを使用できます。たとえば、commitを変更するbbc643cd
場合は、次を実行します。
$ git rebase --interactive 'bbc643cd^'
^
コマンドの最後のキャレットに注意してください。変更したいコミットの前に、実際にコミットにリベースする必要があるからです。
デフォルトのエディターで、'bbc643cd'を示す行pick
をedit
に変更します。
ファイルを保存して終了:gitはファイル内のコマンドを解釈して自動的に実行します。コミットを作成したばかりの前の状況にいることがわかりますbbc643cd
。
この時点で、bbc643cd
は最後のコミットであり、簡単に修正できます。変更を加えてから、次のコマンドでコミットします。
$ git commit --all --amend --no-edit
その後、次のように入力します。
$ git rebase --continue
前のHEADコミットに戻ります。
警告:これにより、そのコミットのSHA-1とすべての子が変更されることに注意してください。つまり、これにより、その時点以降の履歴が書き換えられます。コマンドを使用してプッシュすると、これを行うリポジトリを壊すことができますgit push --force
reword
、git rebase -i
代わりにアクションを使用できますedit
、これはの使用を不要にする、(それが自動的にエディタを開き、リベースの残りのステップを続行git commit --ammend
し、git rebase --continue
あなただけのコミットメッセージではなく内容を変更する必要がある場合)。
git stash
前に、git rebase
とgit stash pop
あなたは保留中の変更がある場合は、その後。
git commit --all --amend --no-edit
ここで盲目的に使用するのではなく、プロンプトの指示に従うほうが賢明です。すべて私が後にしなければならなかったgit rebase -i ...
ことだったgit commit --amend
通常その後git rebase --continue
。
git rebase -i @~9 # Show the last 9 commits in a text editor
必要なコミットを見つけて()に変更pick
し、ファイルを保存して閉じます。Gitはそのコミットに巻き戻され、次のいずれかを実行できます。e
edit
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
。
@
省略形として使用できることを知りませんでしたHEAD
。これを投稿してくれてありがとう。
git reset @~
でコミットを選択した後、まさに私がやりたかったことgit rebase ...
。あなたは私のヒーローです)
対話型リベースと--autosquash
以前のコミットを履歴の中でより深く修正する必要があるときによく使用します。ZelluXの答えが示すプロセスを本質的に高速化し、編集が必要なコミットが複数ある場合に特に便利です。
ドキュメントから:
--autosquash
コミットログメッセージが「squash!…」(または「fixup!…」)で始まり、タイトルが同じ…で始まるコミットがある場合、rebase -iのtodoリストを自動的に変更して、コミットスカッシュ対象としてマークされているのは、変更するコミットの直後です
次のような履歴があるとします。
$ 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
保存して終了するだけです
git commit --fixup=@~
代わりに使用することもできますgit commit -m "fixup! Commit2"
。これは、コミットメッセージが長く、全体を入力するのが面倒な場合に特に便利です。
実行:
$ git rebase --interactive commit_hash^
それぞれ^
は、編集するコミットの数を示します。それが1つだけの場合(指定したコミットハッシュ)、1つ追加するだけ^
です。
Vimを使って、あなたは言葉を変更pick
するreword
(変更したいコミットのために保存して終了します:wq
)。次に、gitは、リワードとしてマークしたコミットごとにプロンプトを表示するので、コミットメッセージを変更できます。
:wq
次のコミットメッセージに移動するには、保存して終了する必要がある各コミットメッセージ
変更を適用せずに終了する場合は、 :q!
編集:ナビゲートvim
するj
には、上k
に移動、下h
に移動、左l
に移動、右に移動します(このNORMAL
モードではすべて、を押しESC
てNORMAL
モードに移動します)。テキストを編集するには、を押しi
て、テキストINSERT
を挿入するモードに入ります。を押しESC
てNORMAL
モードに戻ります:)
更新:githubのリストからの素晴らしいリンクを次に示します。gitで(ほとんど)何でも元に戻す方法
git push --force
ますか?
git push --force
行いは、あなたの地元のコミットとリモコンのコミットを上書きしています。これはこのトピックの場合ではありません:)
何らかの理由でインタラクティブなエディターが気に入らない場合は、を使用できます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
読み取り:「ブランチにリベースして、(非包括的)と(包括的)のamending
間のすべてのコミット」。つまり、Commit2とCommit3は、古いCommit1を完全に削除します。あなたはそれらをチェリーピックすることができますが、この方法は簡単です。Commit1
master
ブランチをクリーンアップすることを忘れないでください!
git branch -d amending
git checkout -b amending Commit1~1
前のコミットを取得するために使用できます
git checkout -b amending Commit1
?
ドキュメントに基づく
古いまたは複数のコミットメッセージのメッセージを修正する
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をrewordに置き換えます。リストの2番目のコミットを変更すると、ファイルは次のようになります。
pick e499d89 Delete CNAME
reword 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.
コミットリストファイルを保存して閉じます。これにより、コミットメッセージを変更し、コミットメッセージを変更して保存するための新しいエディターがポップアップ表示されます。
最後に、修正したコミットをForce-pushします。
git push --force
これに使用しているエイリアスを共有したいと思っただけです。これは、非インタラクティブなインタラクティブリベースに基づいています。それを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)もちろん、リベース中に競合は発生しません
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>
以外のコマンドを実行するという名前のグローバルgitエイリアスを作成します<COMMAND>
f() { <BODY> }; f
-「匿名」のbash関数。SHA=`git rev-parse "$1"`;
-引数をgitリビジョンに変換し、結果を変数に割り当てます SHA
git commit --fixup "$SHA"
-fixup-commit for SHA
。ドキュメントを見るgit-commit
GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
git rebase --interactive "$SHA^"
一部は他の回答でカバーされています。--autosquash
と組み合わせて使用されます。詳細git commit --fixup
については、git-rebase
ドキュメントを参照してくださいGIT_SEQUENCE_EDITOR=true
全体が非インタラクティブになるのです。このハックは、このブログ投稿から学びました。amend-to
git config --global alias.amend-to '!f() { SHA=
; git stash -k && git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^" && git stash pop; }; f'
私は自分が過去のコミットを頻繁に修正しているので、そのためのスクリプトを作成しました。
ワークフローは次のとおりです。
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
このアプローチにたどり着きました(そしておそらくインタラクティブリベースを使用するのとまったく同じです)が、私にとっては単純なものです。
注:このアプローチは、日常的な代替手段ではなく、何ができるかを説明するために示しています。これには多くのステップがあるため(そしておそらくいくつかの注意点があります。)
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)
にリベースし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コミットを複製しません(実際にはどの程度かはわかりませんが)。
注:すべてのコミットハッシュは、当初変更しようとしていたコミットから変更されます。
非インタラクティブなコマンドを取得するには、次の内容のスクリプトを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>
ます。もちろん、衝突が発生した場合でもインタラクティブです。
git stash
+ rebase
自動化
Gerritレビューのために古いコミットを何度も変更する必要があるときのために、私は次のようにしています:
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"
)
使用法:
git add
すでにリポジトリにある場合は必要ありませんgit-amend-old $old_sha
--autosquash
他の無関係なフィックスアップを押しつぶさないので、私はこれが好きです。
git amend
、現在のスタッシュを使用して特定のコミットに変更を適用するためのデフォルトのオプションです。非常に賢いです!
私はこれを解決しました、
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ツリーを書き換える方法として受け入れられない場合、他の人にはわからないような競合が発生します。少ないコミットでツリーをクリーンに維持したい場合は、これを試すことができます。また、小さなチームの場合は、それ以外の場合は好ましくありません。
最良のオプションは、「インタラクティブなリベースコマンド」を使用することです。
git rebase
コマンドは非常に強力です。コミットメッセージの編集、 コミットの結合、並べ替えなどができます。コミットをリベースするたびに、内容が変更されるかどうかに関係なく、コミットごとに新しいSHAが作成されます。特に他の開発者と共同で作業している場合は、このコマンドを使用するときに注意が必要です。あなたがいくつかをリベースしている間、彼らはあなたのコミットで働き始めるかもしれません。強制的にコミットをプッシュすると、コミットが同期されなくなり、後で厄介な状況に気付く場合があります。ので注意してください!
backup
リベースする前にブランチを作成することをお勧めします。そうすれば、制御不能なものを見つけたときはいつでも前の状態に戻ることができます。
git rebase -i <base>
-i
「インタラクティブ」の略。非インタラクティブモードでリベースを実行できることに注意してください。例:
#interactivly rebase the n commits from the current position, n is a given number(2,3 ...etc)
git rebase -i HEAD~n
HEAD
現在の場所を示します(ブランチ名またはコミットSHAにすることもできます)。~n
その手段「のnbeforeé、HEAD~n
あなたが現在いる1前のn 『コミットのリストになります』。
git rebase
次のような異なるコマンドがあります:
p
またはpick
そのままコミットを続けます。r
またはreword
:コミットの内容は保持しますが、コミットメッセージを変更します。s
またはsquash
:このコミットの変更を前のコミット(リスト内でその上のコミット)に結合します。...など
注:Gitをコードエディターと連携させて、物事を簡単にすることをお勧めします。たとえば、ビジュアルコードを使用する場合は、次のように追加できますgit config --global core.editor "code --wait"
。または、コードエディターをGITに関連付ける方法をGoogleで検索できます。
git rebase
私が行った最後の2つのコミットを変更したかったので、次のように処理します。
#This to show all the commits on one line
$git log --oneline
4f3d0c8 (HEAD -> documentation) docs: Add project description and included files"
4d95e08 docs: Add created date and project title"
eaf7978 (origin/master , origin/HEAD, master) Inital commit
46a5819 Create README.md
ここでgit rebase
、最後の2つのコミットメッセージを変更するために使用します。$git rebase -i HEAD~2
コードエディターが開き、これが表示さ
れます。
pick 4d95e08 docs: Add created date and project title
pick 4f3d0c8 docs: Add project description and included files
# Rebase eaf7978..4f3d0c8 onto eaf7978 (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
...
この2つのコミットのコミットメッセージを変更したいので。ので、r
またはのreword
代わりに入力しpick
ます。次に、ファイルを保存してタブを閉じます。注rebase
次のステップは、メッセージを更新することであるので、多段階プロセスで実行されます。また、コミットは時系列の逆順に表示されるため、最後のコミットはそのコミットで表示され、最初のコミットは最初の行で表示されることなどにも注意してください。
メッセージを更新:最初のメッセージを更新:
docs: Add created date and project title to the documentation "README.md"
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
...
保存して閉じる2番目のメッセージを編集
docs: Add project description and included files to the documentation "README.md"
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
...
保存して閉じます。
リベースの終わりまでに、このようなメッセージが表示されSuccessfully rebased and updated refs/heads/documentation
ます。つまり、成功したことを意味します。変更を表示できます。
5dff827 (HEAD -> documentation) docs: Add project description and included files to the documentation "README.md"
4585c68 docs: Add created date and project title to the documentation "README.md"
eaf7978 (origin/master, origin/HEAD, master) Inital commit
46a5819 Create README.md
私はそれが新しいユーザーを助けることを望みます:)。
まだコミットをプッシュしていない場合は、次のコマンドを使用して前のコミットに戻ることができます 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が追加されます。
まあ、このソリューションは非常にばかげているように聞こえるかもしれませんが、特定の状況であなたを救うことができます。
私の友人が偶然、非常にいくつかの巨大なファイル(それぞれ3GBから5GBの範囲で自動生成された4つのファイル)をコミットし、その上にいくつかの追加のコードコミットを実行してから、git push
機能しなくなった問題に気付きました!
ファイルはにリストされていました.gitignore
が、コンテナーフォルダーの名前を変更した後、公開されてコミットされました!そして、その上にさらにいくつかのコードのコミットがありましたが、push
永久に実行され(GBのデータをアップロードしようとしました!)、Githubのファイルサイズ制限のために最終的に失敗しました。
インタラクティブなリベースまたはそれに類似したものの問題は、これらの巨大なファイルをいじり回すことに対処し、何をするにも永久に時間がかかることでした。それにもかかわらず、CLIで約1時間費やした後、ファイル(およびデルタ)が実際に履歴から削除されているのか、それとも単に現在のコミットに含まれていないのかはわかりませんでした。プッシュも機能しておらず、私の友人は本当に行き詰っていました。
だから、私が思いついた解決策は:
~/Project-old
。~/Project
)。 cp -r
のファイルを手動で。 ~/Project-old
~/Project
mv
編集され、.gitignore
適切に含まれていることを確認してください。 .git
、最近複製したフォルダ内のフォルダを上書きしないようにしてください~/Project
れた古い。ここに問題のある履歴のログがあります。push
。編集するのは良いことです。このソリューションの最大の問題は、一部のファイルを手動でコピーすることと、最近のすべてのコミットを1つにマージすることです(明らかに新しいコミットハッシュを使用します)。B
大きな利点は、すべてのステップで非常に明確であり、巨大なファイル(および機密ファイル)に最適であり、履歴に痕跡を残さないことです。