web-dev-qa-db-ja.com

Gitの最初の2つのコミットを潰しますか?

git rebase --interactive <commit>を使えば、任意の数のコミットをまとめて単一のコミットにまとめることができます。

コミットを最初のコミットに潰したくないのであれば、これですべてです。それは不可能なようです。

それを達成する方法はありますか?


中程度に関連:

関連した質問で、私は最初のコミットに対して押しつぶす必要があるという別のアプローチを思いついたのです。

もし興味があるなら--- git:最初にコミットを挿入し、他の全てをシフトする方法は?

496
kch

2012年7月更新( git 1.7.12 +

これで、ルートまでのすべてのコミットをリベースし、最初のYで押しつぶされる2番目のコミットXを選択できます。

git rebase -i --root master

pick sha1 X
squash sha1 Y
pick sha1 Z
git rebase [-i] --root $tip

このコマンドを使用して、「$tip」からルートコミットまでのすべての履歴を書き換えることができます。

GitHubでcommit df5df20c1308f936ea542c86df1e9c6974168472 from Chris Webb(arachsys を参照してください。


元の回答(2009年2月)

SO質問「gitリポジトリの最初の2つのコミットをどのように組み合わせるのですか?

Charles Bailey 最も多く提供されています 詳細な回答 、コミットは完全なツリーであることを思い出させます(以前の状態との差分だけではありません)。
そして、ここでは、古いコミット(「初期コミット」)と新しいコミット(スカッシュの結果)に共通の祖先はありません。
つまり、最初のコミットを新しいものに「commit --amend」してから、新しい最初のコミットに以前の最初のコミットの履歴をリベースすることはできません(多くの競合)

(最後の文はgit rebase -i --root <aBranch>で真実ではなくなりました)

むしろ(Aは元の「初期コミット」であり、Bは後続のコミットを最初のコミットに押し込む必要があります):

  1. 最初のコミットを形成する最後のコミットに戻ります(HEADを切り離します):

    git checkout <sha1_for_B>
    
  2. ブランチポインターを初期コミットにリセットしますが、インデックスと作業ツリーはそのまま残します。

    git reset --soft <sha1_for_A>
    
  3. 「B」のツリーを使用して初期ツリーを修正します。

    git commit --amend
    
  4. この新しい初期コミットに一時的にタグを付けます(または、新しいコミットsha1を手動で覚えることができます):

    git tag tmp
    
  5. 元のブランチに戻ります(この例ではmasterを想定しています):

    git checkout master
    
  6. Bの後のすべてのコミットを新しい初期コミットに再生します。

    git rebase --onto tmp <sha1_for_B>
    
  7. 一時タグを削除します。

    git tag -d tmp
    

そうすれば、「rebase --onto」はマージ中に競合を引き起こしません。これは、履歴をリベースするためですmade after最後のコミット(B)が最初のコミット(これはA)からtmp(押しつぶされた新しい初期コミットを表す)でした:些細な早送りマージのみ。

これは「A-B」で機能しますが、「A-...-...-...-B」でも機能します(この方法で、任意の数のコミットを最初のコミットに押し込めます)。

683
VonC

私はVonCのスクリプトを書き直して、すべてを自動的に行い、何も要求しないようにしました。あなたはそれに2つのコミットSHA1を与えると、それはそれらの間のすべてを "squashed history"と名付けられた1つのコミットに潰します:

#!/bin/sh
# Go back to the last commit that we want
# to form the initial commit (detach HEAD)
git checkout $2

# reset the branch pointer to the initial commit (= $1),
# but leaving the index and working tree intact.
git reset --soft $1

# amend the initial tree using the tree from $2
git commit --amend -m "squashed history"

# remember the new commit sha1
TARGET=`git rev-list HEAD --max-count=1`

# go back to the original branch (assume master for this example)
git checkout master

# Replay all the commits after $2 onto the new initial commit
git rebase --onto $TARGET $2
30
fonsinchen

それが価値があるもののために、私は常に「無操作」の最初のコミットを作成することによってこの問題を回避します。そこでは、リポジトリの唯一のものは空の.gitignoreです:

https://github.com/DarwinAwardWinner/git-custom-commands/blob/master/bin/git-myinit

そのようにして、最初のコミットを台無しにする理由は決してありません。

23
Ryan Thompson

すべてのコミットを単一の初期コミットに単純にまとめたい場合は、リポジトリをリセットして最初のコミットを修正します。

git reset hash-of-first-commit
git add -A
git commit --amend

Git resetは作業ツリーをそのままにしておくので、すべてが残っています。そのため、git addコマンドを使用してファイルを追加し、これらの変更で最初のコミットを修正してください。リベース-iと比較すると、gitのコメントをマージすることはできません。

16
Mike Looijmans

これにより、2番目のコミットが最初のコミットにスカッシュされます。

A-B-C-... -> AB-C-...

git filter-branch --commit-filter '
    if [ "$GIT_COMMIT" = <sha1ofA> ];
    then
        skip_commit "$@";
    else
        git commit-tree "$@";
    fi
' HEAD

ABへのコミットメッセージはBから取得されます(私はAから希望します)。

Uwe Kleine-Königの答えと同じ効果がありますが、頭文字以外のAにも使えます。

5

最初のコミットと2番目のコミットを潰すと、最初のコミットが書き直されます。最初のコミットを基にしたブランチが複数ある場合は、そのブランチを切り離します。

次の例を見てください。

a---b---HEAD
 \
  \
   '---d

Aとbを新しいコミット "ab"に押し潰すと、2つの異なるツリーが生成され、git-mergegit-rebaseが機能しなくなるため、ほとんどの場合は望ましくありません。二つの枝にまたがって。

ab---HEAD

a---d

あなたが本当にこれが欲しいならば、それはすることができます。歴史の書き換えのための強力な(そして危険な)ツールについてはgit-filter-branchを見てください。

3
hillu

そのためにはgit filter-branchを使うことができます。例えば.

git filter-branch --parent-filter \
'if test $GIT_COMMIT != <sha1ofB>; then cat; fi'

これにより、AB-CはAのコミットログを破棄します。

3

対話式のrebaseを使用して、最後の2つのコミットをリモートにプッシュする前に変更することができます。

git rebase HEAD^^ -i
1
todd