Gitを使用して、最後のXコミットを1つのコミットにまとめる方法を教えてください。
Gitを使用して、最後のXコミットを1つのコミットにまとめる方法を教えてください。
回答:
マニュアルでgit rebase -i <after-this-commit>
説明されているように、 2回目以降のコミットで「pick」を使用し、「squash」または「fixup」で置き換えます。
この例で<after-this-commit>
は、SHA1ハッシュまたは現在のブランチのHEADからの相対位置のいずれかであり、rebaseコマンドでコミットが分析されます。たとえば、ユーザーが過去に現在のHEADから5つのコミットを表示したい場合、コマンドはgit rebase -i HEAD~5
です。
<after-this-commit>
ですか?
<after-this-commit>
X + 1をコミットします。つまり、スカッシュしたい最も古いコミットの親です。
rebase -i
アプローチとの違いreset --soft
は、rebase -i
コミットの作成者を保持reset --soft
できる一方で、再コミットできることです。時々私はプルリクエストのコミットを押しつぶしながら、作者の情報を維持する必要があります。自分のコミットでソフトをリセットする必要がある場合があります。とにかく両方の素晴らしい答えに賛成票を投じます。
あなたはせずに、かなり簡単にこれを行うことができますgit rebase
かgit merge --squash
。この例では、最後の3つのコミットを押しつぶします。
新しいコミットメッセージを一から書きたい場合は、これで十分です。
git reset --soft HEAD~3 &&
git commit
既存のコミットメッセージを連結して新しいコミットメッセージの編集を開始する場合(つまり、pick / squash / squash /…/ squashのgit rebase -i
指示リストから開始する場合と同様)、それらのメッセージを抽出して渡す必要があります。それらにgit commit
:
git reset --soft HEAD~3 &&
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"
これらの方法はどちらも、最後の3つのコミットを同じ方法で1つの新しいコミットに押しつぶします。ソフトリセットは、スカッシュしたくない最後のコミットにHEADを再ポイントするだけです。インデックスも作業ツリーもソフトリセットの影響を受けず、インデックスは新しいコミットの望ましい状態のままになります(つまり、「破棄しようとしている」コミットからのすべての変更がすでにあります)。
git rebase --squash-recent
、あるいはgit commit --amend-many
。
branch@{upstream}
(または@{upstream}
現在のブランチのみ。どちらの場合も、最後の部分はに省略できます。gitrevisionsを@{u}
参照してください)。これは、「最後にプッシュされたコミット」とは異なる場合があります(たとえば、誰かが最新のプッシュの上に構築された何かをプッシュし、それをフェッチした場合)、それはあなたが望むものに近いようです。
push -f
ことは私に必要でしたが、そうでなければそれは素敵でした、ありがとう。
git push --force
、その後それがコミットかかること
git merge --squash
これに使用できますgit rebase -i
。これは、よりも少しエレガントです。あなたがマスターであり、最後の12のコミットを1つに潰したいとします。
警告:最初に作業をコミットしてください— git status
クリーンであることを確認してください(git reset --hard
ステージングされた変更とアンステージングされた変更が破棄されるため)
次に:
# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12
# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}
# Commit those squashed changes. The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit
のドキュメントでgit merge
は、--squash
オプションについて詳しく説明しています。
更新:この方法の唯一の本当の利点git reset --soft HEAD~12 && git commit
、Chris Johnsenが彼の回答で提案つぶしているすべてのコミットメッセージが事前に入力されたコミットメッセージが得られることです。
git rebase -i
ますが、理由を述べていません。これを暫定的に-1にすると、実際にはその逆が真実であり、これはハックであると思われます。特別に設計された機能のgit merge
1つを強制するために、必要以上のコマンドを実行していませんgit rebase
か?
git merge --squash
また、スクリプトで使用する方が簡単です。基本的に、その理由はgit rebase -i
、このための「対話性」はまったく必要ないということでした。
git merge --squash
、特にローカルブランチからマージする場合は、リベースと比較して、移動、削除、名前変更の前にマージの競合が発生する可能性が低いことです。(免責事項:唯一の経験に基づいて、これが一般的なケースでは当てはまらない場合は私を修正してください!)
HEAD@{1}
あなたのワークフローが停電などにより時間中断されたときだけ、安全側の例えば上にあるように
git reset
特にGit初心者は、可能な限り回避することをお勧めします。本当に多くのコミットに基づいてプロセスを自動化する必要がない限り、それほど珍しい方法はありません...
git merge --squash (working branch name)
git commit
コミットメッセージはスカッシュに基づいて事前に入力されます。
gitk
コード行にラベルを付け、つぶしのベースにもラベルを付けることを意味します。通常の場合、これらのラベルは両方ともすでに存在するため、ステップ(1)はスキップできます。
git branch your-feature && git reset --hard HEAD~N
、最も便利な方法を見つけました。ただし、これにはgit resetが再度含まれるため、この回答は回避しようとしました。
クリスジョンセンの答えに基づいて、
bash:(またはWindowsのGit Bash)からグローバルな「スカッシュ」エイリアスを追加します。
git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'
...またはWindowsのコマンドプロンプトを使用:
git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
あなた~/.gitconfig
は今このエイリアスを含むべきです:
[alias]
squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
使用法:
git squash N
...これは、最後のN
コミットを包括的に自動的につぶします。
注:結果のコミットメッセージは、すべての押しつぶされたコミットを順番に組み合わせたものです。これに不満がある場合は、いつでもgit commit --amend
手動で変更できます。(または、好みに合わせてエイリアスを編集します。)
git squash -m "New summary."
してN
決定したいと思います。
git commit --amend
してメッセージをさらに変更できますが、このエイリアスを使用すると、コミットメッセージの内容を適切に開始できます。
この便利なブログ投稿のおかげで、このコマンドを使用して最後の3つのコミットを押しつぶすことができることがわかりました。
git rebase -i HEAD~3
これは、追跡情報/リモートリポジトリのないローカルブランチにいる場合でも機能するので便利です。
このコマンドはインタラクティブなリベースエディターを開き、通常どおりに並べ替え、スカッシュ、リワードなどを行うことができます。
インタラクティブなリベースエディターの使用:
インタラクティブなリベースエディターは、最後の3つのコミットを表示します。この制約はHEAD~3
、コマンドの実行時に決定されましたgit rebase -i HEAD~3
。
最新のコミットはHEAD
、1行目に最初に表示されます。aで始まる行#
はコメント/ドキュメントです。
表示されるドキュメントはかなり明確です。任意の行で、コマンドをから任意のコマンドに変更できpick
ます。
fixup
上の行のコミットにコミットの変更を「押しつぶし」、コミットのメッセージを破棄するので、コマンドを使用することを好みます。
1行目のコミットはなのでHEAD
、ほとんどの場合、そのままにしておきますpick
。squash
またはfixup
コミットを押しつぶす他のコミットがないので、使用することはできません。
コミットの順序を変更することもできます。これにより、時間的に隣接していないコミットをスカッシュまたは修正することができます。
実用的な日常の例
最近、新しい機能をコミットしました。それ以来、2つのバグ修正をコミットしました。しかし、私はコミットした新機能にバグ(またはおそらくスペルミス)を発見しました。なんてうるさい!コミット履歴を汚染する新しいコミットが必要ない!
私が最初にやることは間違いを修正し、コメントで新しいコミットをすることです squash this into my new feature!
です。
次に、git log
またはgitk
を実行して、新機能のコミットSHAを取得します(この場合1ff9460
)。
次に、でインタラクティブなリベースエディターを起動しgit rebase -i 1ff9460~
ます。~
コミット後SHAは、エディタにコミットそれ含めるエディタに指示します。
次に、修正(fe7f1e0
)を含むコミットを機能コミットの下に移動し、に変更pick
しfixup
ます。
エディターを閉じると、修正により機能のコミットが抑制され、コミット履歴がきれいに表示されます。
これはすべてのコミットがローカルである場合にうまく機能しますが、すでにリモートにプッシュされているコミットを変更しようとすると、同じブランチをチェックアウトしている他の開発者に本当に問題を引き起こす可能性があります!
pick
1行目のままにします。1行目のコミットを選択したsquash
場合fixup
、またはgitの場合、gitは「エラー:以前のコミットがないと「修正」できません」というメッセージを表示します。次に、それを修正するオプションが表示されます。「これは「git rebase --edit-todo」で修正してから、「git rebase --continue」を実行できます。」または、中止して最初からやり直すこともできます:「または、 'git rebase --abort'を使用してリベースを中止できます。」
TortoiseGitを使用すると、次の機能を実行できますCombine to one commit
。
Show Log
Combine to one commit
コンテキストメニューから選択この関数は、必要な単一のgitステップをすべて自動的に実行します。残念ながらWindowsでのみ利用できます。
これを行うには、次のgitコマンドを使用できます。
git rebase -i HEAD~n
n(= 4 here)は最後のコミットの数です。次に、次のオプションがあります、
pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....
以下のようにpick
1つのコミットとsquash
他のコミットを最新のものに更新します。
p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....
詳細については、リンクをクリックしてください
この記事に基づいて、私はこの方法が私のユースケースにとってより簡単であることを発見しました。
私の 'dev'ブランチは 'origin / dev'より96コミット進んでいました(これらのコミットはまだリモートにプッシュされていません)。
変更をプッシュする前に、これらのコミットを1つにまとめたいと思いました。ブランチを 'origin / dev'の状態にリセットし(これにより、96コミットからのすべての変更がステージングされないままになります)、変更を一度にコミットすることを好みます。
git reset origin/dev
git add --all
git commit -m 'my commit message'
コミットを組み合わせるブランチで、次のコマンドを実行します。
git rebase -i HEAD~(n number of commits back to review)
例:
git rebase -i HEAD~1
これによりテキストエディターが開き、これらのコミットをマージする場合は、各コミットの前にある「ピック」を「スカッシュ」に切り替える必要があります。ドキュメントから:
たとえば、すべてのコミットを1つにマージする場合は、「ピック」が最初のコミットであり、将来のすべてのコミット(最初の下に配置)は「スカッシュ」に設定する必要があります。vimを使用している場合は、挿入モードで:xを使用して、エディターを保存して終了します。
次に、リベースを続行するには:
git rebase --continue
これとコミット履歴を書き換える他の方法の詳細については、この役立つ投稿を参照してください
--continue
and vimの:x
機能についても説明してください。
Anomiesの回答は良いですが、私はこれに不安を感じたので、スクリーンショットをいくつか追加することにしました。
あなたがいる場所を参照してくださいgit log
。最も重要なのは、スカッシュしたくない最初のコミットのコミットハッシュを見つけることです。だから:
git rebase -i [your hash]
私の場合、実行します:
$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d
私の場合、最初にコミットしたすべてのものを押しつぶしたいと思います。順序は最初から最後までなので、とまったく逆git log
です。私の場合、私は欲しい:
1つのコミットのみを選択して残りを押しつぶした場合は、1つのコミットメッセージを調整できます。
それでおしまい。これ(:wq
)を保存すると、完了です。でそれを見てくださいgit log
。
git log
1)コミットの短いハッシュを特定する
# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....
ここでもgit log --oneline
短いハッシュを取得するために使用できます。
2)最後の2つのコミットをスカッシュ(マージ)する場合
# git rebase -i deab3412
3)これによりnano
、マージ用のエディターが開きます。そして、それは以下のように見えます
....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....
4)ワードの名前を変更pick
するsquash
前に存在しているがabcd1234
。名前を変更すると、次のようになります。
....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....
5)次に、nano
エディターを保存して閉じます。を押してctrl + o
、を押しEnter
て保存します。次に、を押しctrl + x
てエディターを終了します。
6)次にnano
、必要に応じて更新して、コメントを更新するためのエディターを再度開きます。
7)これで正常につぶれたので、ログを確認することで確認できます。
# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....
8)リポジトリにプッシュします。+
ブランチ名の前に記号を追加することに注意してください。これは強制プッシュを意味します。
# git push origin +master
注:これはubuntu
シェルでのgitの使用に基づいています。異なるos(Windows
またはMac
)を使用している場合、上記のコマンドはエディターを除いて同じです。別のエディターが表示される場合があります。
git add <files>
--fixup
オプションを使用してコミットすると、OLDCOMMIT
このコミットをマージ(スカッシュ)する必要があります。git commit --fixup=OLDCOMMIT
これで、HEADの上に新しいコミットが作成されfixup1 <OLDCOMMIT_MSG>
ます。
OLDCOMMIT
ます。git rebase --interactive --autosquash OLDCOMMIT^
ここでは、^
以前はにコミットすることOLDCOMMIT
。このrebase
コマンドは、エディター(vimまたはnano)で対話式ウィンドウを開き、保存するだけで終了するだけで十分です。これに渡されたオプションは、最新のコミットを古いコミットの隣に自動的に移動し、操作をfixup
(スカッシュと同等)に変更するためです。その後、リベースが続行して終了します。
--amend
で使用できますgit-commit
。 # git log --pretty=oneline --abbrev-commit
cdababcd Fix issue B
deab3412 Fix issue A
....
# git add <files> # New changes
# git commit --amend
# git log --pretty=oneline --abbrev-commit
1d4ab2e1 Fix issue B
deab3412 Fix issue A
....
ここで--amend
は、最後のコミットへの新しい変更をマージし、cdababcd
新しいコミットIDを生成します1d4ab2e1
feature-branch
ゴールデンリポジトリ(golden_repo_name
)から複製されたリモートブランチ(と呼ばれます)を使用している場合、コミットを1つにまとめる方法は次のとおりです。
ゴールデンリポジトリをチェックアウトする
git checkout golden_repo_name
次のように、そこから新しいブランチを作成します(ゴールデンレポ)。
git checkout -b dev-branch
すでに持っているローカルブランチとスカッシュマージ
git merge --squash feature-branch
変更をコミットします(これは、開発ブランチで行われる唯一のコミットになります)
git commit -m "My feature complete"
ブランチをローカルリポジトリにプッシュする
git push origin dev-branch
これは非常に粗雑ですが、一種のクールな方法なので、リングに投げ込みます。
GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo
翻訳:gitに新しい「エディタ」を提供します。編集するファイル名が git-rebase-todo
(インタラクティブなリベースプロンプト)が最初の「pick」以外のすべてを「squash」に変更し、それ以外の場合はvimを生成するため、プロンプトが表示されたときに押しつぶされたコミットメッセージを編集するには、vimを取得します。(そして明らかに私はブランチfooの最後の5つのコミットを押しつぶしていましたが、好きなように変更できます。)
でも、おそらく私はMark Longairが提案したことをするでしょう。
これを行う最も簡単な方法は、masterから新しいブランチを作成し、機能ブランチのスカッシュをマージすることです。
git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch
その後、すべての変更をコミットする準備が整います。
スカッシュしたいブランチに現在いる場合、常に機能する単純なワンライナー、マスターはそのブランチの元のブランチであり、最新のコミットには使用するコミットメッセージと作成者が含まれます。
git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}
たとえば、最後の3つのコミットをブランチ(リモートリポジトリ)の単一のコミットに押しつぶしたい場合は、たとえば、https://bitbucket.org
私がしたことは
(MASTER)
Fleetwood Mac Fritz
║ ║
Add Danny Lindsey Stevie
Kirwan Buckingham Nicks
║ ╚═══╦══════╝
Add Christine ║
Perfect Buckingham
║ Nicks
LA1974══════════╝
║
║
Bill <══════ YOU ARE EDITING HERE
Clinton (CHECKED OUT, CURRENT WORKING DIRECTORY)
https://github.com/fleetwood-mac/band-historyリポジトリのこの非常に簡略化された履歴では、Bill Clintonのコミットを元の(MASTER
)Fleetwood Macコミット。
プルリクエストを開き、GitHubに次のように表示されます。
4つのコミット:
誰もリポジトリの完全な履歴を読もうとは思わないだろうと考えています。(実際にはリポジトリがあります。上のリンクをクリックしてください!)これらのコミットを破棄することにしました。だからあなたは行って実行しgit reset --soft HEAD~4 && git commit
ます。次にあなたgit push --force
GitHubにそれを追加して、PRをクリーンアップします。
そして、何が起こりますか?FritzからBill Clintonへの単一コミットを作成しました。昨日、このプロジェクトのバッキンガムニックスバージョンで作業していたことを忘れたためです。そしてgit log
、GitHubに表示されるものと一致しません。
git checkout
それらgit reset --soft
することをgit commit
直接ワープするa を作成します他の優れた答えに加えgit rebase -i
て、コミット順序と常に混乱する方法を追加したいと思います-古いものから新しいものへ、またはその逆ですか?これが私のワークフローです:
git rebase -i HEAD~[N]
ここで、Nは、最新のコミットから始めて、参加したいコミットの数です。つまりgit rebase -i HEAD~5
、「最後の5つのコミットを新しいものにつぶす」ことを意味します。このようなワークフローに関連する質問に対する回答はどうですか?
merge --squash
PR を実行する方が簡単ですが、チームはそれによりプロセスが遅くなると考えました。)このようなワークフローはこのページでは見ていません。(それが私の目かもしれません。)私がrebase
正しく理解している場合、複数のマージには複数の競合解決が必要になります。そんなこと考えたくない!
したがって、これは私たちにとってはうまくいくようです。
git pull master
git checkout -b new-branch
git checkout -b new-branch-temp
git checkout new-branch
git merge --squash new-branch-temp
//すべての変更をステージに入れますgit commit 'one message to rule them all'
git push
より一般的な解決策は、「N」コミットを指定するのではなく、スカッシュしたいブランチ/コミットIDを指定することです。これは、特定のコミットまでのコミットをカウントするよりもエラーが発生しにくくなります。タグを直接指定するか、本当にカウントしたい場合はHEAD〜Nを指定できます。
私のワークフローでは、ブランチを開始し、そのブランチでの最初のコミットで目標を要約します(つまり、これは通常、機能の「最終」メッセージとしてパブリックリポジトリにプッシュするものです)。私はやりたいgit squash master
最初のメッセージに戻った後、私がプッシュする準備ができています。
私はエイリアスを使用します:
squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i
これにより、破棄される前に履歴が破棄されます。元に戻す場合は、古いコミットIDをコンソールから取得することで、復元する機会があります。(Solarisユーザーは、GNU sed -i
オプションを使用していることに注意してください。MacおよびLinuxユーザーは、これで問題ありません。)
git squash master
ていて、スレーブでチェックアウトされているときに使用する場合。どうなりますか?紛争を隠しますか?
問題は、「最後の」が何を意味するのか曖昧である可能性があります。
たとえばgit log --graph
、以下を出力します(簡略化):
* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| |
* | commit H1
| |
* | commit H2
|/
|
次に、時間までの最後のコミットはH0、マージ、B0です。それらを押しつぶすには、コミットH1でマージされたブランチをリベースする必要があります。
問題は、H0にH1とH2が含まれていること(および一般に、マージ前と分岐後のコミット数が多い)ですが、B0には含まれていません。したがって、少なくともH0、マージ、H1、H2、B0からの変更を管理する必要があります。
リベースを使用することは可能ですが、別の方法で他の言及された回答とは異なります:
rebase -i HEAD~2
これにより、(他の回答で述べたように)選択オプションが表示されます。
pick B1
pick B0
pick H0
ピックの代わりにスカッシュをH0に配置します。
pick B1
pick B0
s H0
保存して終了した後、リベースはH1の後にコミットを順番に適用します。つまり、再び競合を解決するように求められます(最初にHEADがH1になり、適用されるとコミットが累積されます)。
リベースが終了したら、押しつぶされたH0とB0のメッセージを選択できます。
* commit squashed H0 and B0
|
* commit B1
|
* commit H1
|
* commit H2
|
PS BOへのリセットを行うだけの場合:(たとえば、それを使用するreset --mixed
ことについては、https://stackoverflow.com/a/18690845/2405850で詳しく説明しています):
git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'
次に、H0、H1、H2のB0変更につぶします(分岐後、マージ前の変更に対するコミットを完全に失います)。