質問: 以下のような git のコミット履歴があります。 <現状> ``` * ローカルコミット7 * ローカルコミット6 * ローカルコミット5 * マージ |\ | * リモートコミット3 | * リモートコミット2 | * リモートコミット1 | | * | ローカルコミット4 * | ローカルコミット3 * | ローカルコミット2 | | |/ | * ローカルコミット1 | ... ``` `git log` を見やすくする目的で、以下の<A>もしくは<B>のようにコミットをまとめたいと考えています。どのようにすればいいでしょうか。 <A> ``` * マージ+ローカルコミット5〜7 |\ | * リモートコミット3 | * リモートコミット2 | * リモートコミット1 | | * | ローカルコミット2〜4 | | |/ | * ローカルコミット1 | ... ``` <B> ``` * マージ+ローカルコミット2〜7 |\ | * リモートコミット3 | * リモートコミット2 | * リモートコミット1 | | | | |/ | * ローカルコミット1 | ... ``` ポイントは以下の2点になります。 * `git log --graph` の表示上に「”ローカルコミット4"と"リモートコミット3"をマージした」という記録を残した上で、"マージ" から "リモートコミット7" までを一つのコミットにまとめたい * ”ローカルコミット4"と"リモートコミット3"をマージした際に行ったコンフリクト解消のための手作業を、再度行わずに"ローカルコミット2"から"ローカルコミット4"をまとめたい なにか方法はあるでしょうか。
回答: `gite rebase`の `--rebase-merges`(`-r`) オプションが利用可能かと思います。 ### 初期設定 初期リポジトリを作成します: ``` #!/bin/bash set -eu git init git commit --allow-empty -m init git checkout -b local-branch for i in {1..4} ; do git commit --allow-empty -m "ローカルコミット${i}" done git checkout @~3 git checkout -b remote-branch for i in {1..3} ; do git commit --allow-empty -m "リモートコミット${i}" done git checkout local-branch git merge --no-edit remote-branch for i in {5..7} ; do git commit --allow-empty -m "ローカルコミット${i}" done ``` 上記のスクリプトを実行すると次のような状態になります(`git log --graph --pretty=format:"%h %s"`): ``` * a231d0e ローカルコミット7 * 54dccc9 ローカルコミット6 * 28345fa ローカルコミット5 * 19e3401 Merge branch 'remote-branch' into local-branch |\ | * 6a1cdf4 リモートコミット3 | * cbf2e5a リモートコミット2 | * ecf9ddf リモートコミット1 * | 1cecd54 ローカルコミット4 * | 44c9adf ローカルコミット3 * | 662d8d5 ローカルコミット2 |/ * f40cad8 ローカルコミット1 * 35a78cc init ``` このリポジトリを、質問文中にある[A], [B]それぞれの状態に変更します。 `git rebase -i -r master` を実行すると、次のように表示されます: ``` label onto # Branch remote-branch reset onto pick f40cad8 ローカルコミット1 # empty label branch-point pick ecf9ddf リモートコミット1 # empty pick cbf2e5a リモートコミット2 # empty pick 6a1cdf4 リモートコミット3 # empty label remote-branch reset branch-point # ローカルコミット1 pick 662d8d5 ローカルコミット2 # empty pick 44c9adf ローカルコミット3 # empty pick 1cecd54 ローカルコミット4 # empty merge -C 19e3401 remote-branch # Merge branch 'remote-branch' into local-branch pick 28345fa ローカルコミット5 # empty pick 54dccc9 ローカルコミット6 # empty pick a231d0e ローカルコミット7 # empty ``` ### [A]へrebaseする ローカルコミット3, 4をsquashしてローカルコミット2にまとめます。 同様に、ローカルコミット5,6,7をmergeコミットにまとめます。 ``` label onto # Branch remote-branch reset onto pick f40cad8 ローカルコミット1 # empty label branch-point pick ecf9ddf リモートコミット1 # empty pick cbf2e5a リモートコミット2 # empty pick 6a1cdf4 リモートコミット3 # empty label remote-branch reset branch-point # ローカルコミット1 pick 662d8d5 ローカルコミット2 # empty s 44c9adf ローカルコミット3 # empty s 1cecd54 ローカルコミット4 # empty merge -C 19e3401 remote-branch # Merge branch 'remote-branch' into local-branch s 28345fa ローカルコミット5 # empty s 54dccc9 ローカルコミット6 # empty s a231d0e ローカルコミット7 # empty ``` 結果: ``` * 3494c34 Merge branch 'remote-branch' into local-branch ローカルコミット5 ローカルコミット6 ローカルコミット7 |\ | * 6a1cdf4 リモートコミット3 | * cbf2e5a リモートコミット2 | * ecf9ddf リモートコミット1 * | 7b17503 ローカルコミット2 ローカルコミット3 ローカルコミット4 |/ * f40cad8 ローカルコミット1 * 35a78cc init ``` ### [B]へrebaseする mergeコミットをローカルコミット2の前に持ってきたうえで、ローカルコミット2-7をmergeコミットにsquashすればよいです。 ``` label onto # Branch remote-branch reset onto pick f40cad8 ローカルコミット1 # empty label branch-point pick ecf9ddf リモートコミット1 # empty pick cbf2e5a リモートコミット2 # empty pick 6a1cdf4 リモートコミット3 # empty label remote-branch reset branch-point # ローカルコミット1 merge -C 19e3401 remote-branch # Merge branch 'remote-branch' into local-branch s 662d8d5 ローカルコミット2 # empty s 44c9adf ローカルコミット3 # empty s 1cecd54 ローカルコミット4 # empty s 28345fa ローカルコミット5 # empty s 54dccc9 ローカルコミット6 # empty s a231d0e ローカルコミット7 # empty ``` 結果: ``` * 4dc1987 Merge branch 'remote-branch' into local-branch ローカルコミット2 ローカルコミット3 ローカルコミット4 ローカルコミット5 ローカルコミット6 ローカルコミット7 |\ | * 6a1cdf4 リモートコミット3 | * cbf2e5a リモートコミット2 | * ecf9ddf リモートコミット1 |/ * f40cad8 ローカルコミット1 * 35a78cc init ``` ### 補足 ただし、[A],[B]ともにmergeコミットでマージ以外のことも行う結果になります。 通常このようなrebaseは行うべきではないです。 (一般的には、logグラフが複雑であることよりもmergeコミットでマージ以外のことが行われていることの方が理解するのに時間がかかる) 今回の質問文のブランチへの対応として最も適切なのは、単純にリモートコミット(リモートブランチ)に対してrebaseすることだと考えます: ``` git checkout ローカルブランチ git rebase リモートブランチ ```