0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PR提出で差分が変になった!

dev > parent-feature > child-feature のようにブランチを親子関係で積み上げて開発している時、こんな現象に遭遇したことはありませんか?

  • PRの差分がおかしい: 子ブランチから親ブランチへPRを出したのに、親ブランチのコミットや、さらにその上の dev のコミットまで混ざって表示される。
  • Rebaseが終わらない: 親ブランチの最新を取り込もうとして git rebase したら、以前解消したはずのコンフリクトが何度も発生し、無限ループのようになる。
  • ログが重複している: git log を見ると、同じコミットメッセージが2回ずつ登場している。

この記事では、これらの現象の原因である「汚れた履歴」の正体と、それを最も確実かつクリーンに修復する手順(Reset & Cherry-pick法)を解説します。

原因:親の変更を git pull (Merge) してしまっている

最大の原因は、親ブランチ(devparent-feature)の更新を取り込む際に、git pull (または git merge) を使ってしまったことです。

ブランチを積み上げている場合、マージコミットを作成すると履歴が複雑に絡み合ってしまいます。

  • 汚れた状態(Mergeを使用): 親の履歴が子に合流し、リベース時にGitが「どれが独自の変更か」を判別できなくなります。これが重複コミットやRebaseループの原因です。
  • 理想の状態(Rebaseを使用): 親の更新の「上に」、子の変更を積み直すことで履歴は一直線になります。

対処法:履歴の再構築(Reset & Cherry-pick)

もし履歴が汚れてしまい、通常の rebase ができなくなった場合は、「ブランチを一旦クリーンな状態にリセットし、必要なコミットだけを手動で積み直す」のが最短の解決策です。

手順

ここでは例として、以下の構成で feature ブランチを修復します。

  • 親ブランチ: parent-feature
  • 作業ブランチ: child-feature

1. 作業中のRebaseを中止する

もしRebaseの泥沼にハマっている場合は、まず中止して元の状態に戻ります。

git rebase --abort

2. 救出したいコミットを特定する

feature ブランチ独自の作業コミット(残したいコミット)のIDを控えます。

# 親ブランチとの差分ログを表示
git log parent-feature..child-feature --oneline

表示されたリストの中から、自分が作業したコミットのIDをメモ帳などにコピーしてください。
Merge pull request... などのマージコミットは無視してください。

3. 親ブランチを最新化・クリーンにする

土台となる親ブランチも汚れている可能性があるため、まずは親を完璧な状態にします。

# 親ブランチに移動
git switch parent-feature

# 親の親(devなど)の最新状態を取得
git pull origin dev

# 親ブランチをリベース(または同様の手順で再構築)
git rebase dev
git push --force-with-lease origin parent-feature

4. 作業ブランチを強制リセットする

ここがポイントです。feature ブランチの汚れた履歴をすべて捨て、クリーンになった親ブランチと全く同じ状態にリセットします。

git switch feature
git reset --hard parent-feature

5. コミットを積み直す (Cherry-pick)

手順2で控えたコミットIDを、古い順(ログの下から上)に1つずつ適用します。

git cherry-pick <一番古いコミットID>
git cherry-pick <その次のコミットID>
# ...

もしコンフリクトが発生したら、通常通り解消してください。履歴がクリーンなので、過去の不要なコンフリクトは発生しません。

# コンフリクト解消後
git add .
git cherry-pick --continue

6. 強制プッシュする

履歴を書き換えたため、通常のPushはできません。安全な強制プッシュを使用します。

git push --force-with-lease origin feature

再発防止:今後の運用

今後、親ブランチの変更を取り込む際は、以下の内容に気をつけると良いと思います。

  1. devブランチ以外でのgit pull origin <親ブランチ> は禁止
    マージコミットを作らないようにします。
  2. 必ず git rebase を使う
    履歴を一直線に保ちます。
  3. 親から順番にリベースする
    dev > parent > child の順で、根元から最新化していきます。

正しい更新手順の例

# 1. 一番親(dev)を最新化
git switch dev
git pull origin dev

# 2. 中間(parent)をリベース
git switch parent
git rebase dev
git push --force-with-lease origin parent

# 3. 子(child)をリベース
git switch child
git rebase parent
git push --force-with-lease origin child

トラブルシューティング

Q. git reset --hard で作業中のコミットが消えてしまった!

A. git reflog で復活できます。
git reflog コマンドを実行すると、過去の操作履歴が見られます。戻りたい時点の HEAD@{n} を探して、git reset --hard HEAD@{n} すれば元通りです。

Q. Pushしようとしたら (stale info) と出て拒否された

A. リモートの情報が古い状態です。
git fetch origin を実行して最新情報を取得してから、再度 git push --force-with-lease を実行してください。

Q. PRの差分がまだ多い(コミット数がおかしい)

A. PRのターゲットブランチ(Base)が間違っていませんか?
GitHub上でPRのBaseが dev ではなく、直近の親である parent ブランチになっているか確認してください。また、親ブランチ自体が古い場合は、親ブランチをPushしてGitHub側を最新にする必要があります。

まとめ

devブランチからparentブランチを切り、そこから作業ブランチを切る場合には、特に履歴の管理には注意が必要です。「困ったら reset して cherry-pick で積み直す」が大切ですね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?