私は通常レビューのためにコミットのリストを提出します。以下のコミットがあるとします。
HEAD
Commit3
Commit2
Commit1
...ヘッドコミットをgit commit --amend
で修正できることを私は知っています。しかし、それがHEAD
コミットではない場合、どうすればCommit1
を変更できますか?
例えば、git rebaseを使ってcommit bbc643cd
に戻したい場合は、
$ git rebase --interactive 'bbc643cd^'
デフォルトのエディタで、コミットを変更したい行のpick
をedit
に変更します。変更を加えてから、以前と同じメッセージでそれらをコミットします。
$ git commit --all --amend --no-edit
コミットを変更し、その後
$ git rebase --continue
直前のヘッドコミットに戻ります。
WARNING:これはそのコミットの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のドキュメントを参照してください。
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
で変更してください。
対話的な リベース と--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 rebase --interactive commit_hash^
それぞれの^
は編集したいコミットの数を示し、それが1つだけの場合(指定したコミットハッシュ)、^
を1つだけ追加します。
Vimを使って、変更したいコミットをpick
からreword
に変更し、保存して終了します(:wq
)。それからgitはあなたがrewordとしてマークした各コミットであなたを促しますので、コミットメッセージを変更することができます。
次のコミットメッセージに進むには、コミットメッセージを保存して終了する必要があります(:wq
)。
変更を適用せずに終了したい場合は:q!
を押してください。
EDIT:vim
内を移動するにはj
を使用し、上に移動するにはk
を使用し、h
を使用する左に移動し、l
モードに移動するにはNORMAL
モードに移動し、ESC
モードに移動するにはNORMAL
を押します。テキストを編集するには、i
を押してテキストを挿入するINSERT
モードに入ります。 ESC
モードに戻るにはNORMAL
を押してください:)
UPDATE:これは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
読んでください: "amending
へのリベース、すべてのCommit1
(包括的)とmaster
(包括的)の間のコミット"つまり、Commit2とCommit3で、古いCommit1を完全に削除します。あなたはそれらをチェリーピックすることができますが、この方法はより簡単です。
あなたの枝を片付けるのを忘れないでください!
git branch -d amending
私はこれに使用しているエイリアスを共有すると思いました。それは非対話型対話型リベースに基づいています。それをあなたの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'
このコマンドの最大の利点は、no-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 revisionに変換し、結果を変数SHA
に代入しますgit commit --fixup "$SHA"
- SHA
に対するfixup-commit git-commit
docs を参照してください。GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
git rebase --interactive "$SHA^"
の部分は他の答えでカバーされています。--autosquash
はgit commit --fixup
と一緒に使われるものです。詳細は git-rebase
docsGIT_SEQUENCE_EDITOR=true
は全体を非インタラクティブにするものです。このハックは私がこのブログ投稿から学んだ です。私は自分自身のためにスクリプトを書いたのに十分な頻度で過去のコミットを直していました。
これがワークフローです。
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コミットを複製することはありません(実際にはどの程度まで言えないか)。
注意:すべてのコミットハッシュは、最初に変更しようとしていたコミットから変更されます。
古いコミットメッセージまたは複数のコミットメッセージのメッセージを修正する
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.
コミットリストファイルを保存して閉じると、コミットメッセージを変更し、コミットメッセージを変更して保存するための新しいエディタがポップアップ表示されます。
Finaly Force - 修正されたコミットを押します。
git Push --force
非対話型のコマンドを取得するには、この内容のスクリプトを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>
を実行します。もちろん、あなたが衝突を起こしたとしてもそれは依然として対話型です。
私はこれを解決しました、
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
(fixupはコミットメッセージなしでマージされます)またはs
(squashコミットメッセージとマージすると実行時に変更される可能性があります)
それからあなたの木を保存してください。
今マージは既存のコミットで行われました。
注:自分で保守しない限り、その方法は好ましくありません。チームサイズが大きい場合、gitツリーを書き換える方法として受け入れられないので、他の人にはわかりませんが競合が発生します。あなたがより少ないコミットであなたの木をきれいにしておきたいならこれを試みることができます、そして、その小さいチームがそうでなければそれは好まれません.....
git stash
+ rebase
自動化
Gerritレビューのために古いコミットを何度も修正する必要があるときは、次のようにしています。
git-amend-to() (
# 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-amend-to $old_sha
私は--autosquash
よりもこれが好きです。それは他の無関係な修正を潰さないからです。
私にとっては、リポジトリから資格情報を削除するためのものでした。リベースしようとすると、リベースしようとすると途中で無関係の衝突が大量に発生しました - 続行します。自分でリベースしようとしなくても、MacではBFG(brew install bfg)というツールを使用してください。
まだコミットをプッシュしていない場合は、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
edであり、.gitignore
に正しく含まれていることを確認してください。.git
の~/Project
フォルダーを古いもので上書きしないようにしてください。それが問題のある歴史の記録です。Push
されることは良いことです。この解決方法の最大の問題は、手動でファイルをコピーすることと、最近行われたすべてのコミットを1つにマージすることです(明らかに新しいコミットハッシュで)。
大きな利点は、すべてのステップで非常に明確であることです。巨大なファイル(および機密性の高いファイル)にも最適です。 !