Git のリベースと強制プッシュ

このガイドでは、リベースやフォースプッシュ、そしてローカルでのマージ競合の修正について説明します。強制プッシュやリベースを試みる前に、コマンドラインを通して Git に慣れていることを確認しましょう。

caution
git rebase はコミット履歴を書き換えます。共有ブランチでこれを行うのは有害です。複雑で解決しにくいマージ競合を引き起こす可能性があります。このような場合は、デフォルトブランチに対してブランチをリベースするのではなく、そのブランチを pull することを検討しましょう (git pull origin master)。プルすることで、貢献者の作業を危険にさらすリスクは少なくなりますが、似たような効果が得られます。

Git では、リベースによって機能ブランチを別のブランチの内容で更新します。このステップは、Git ベースの開発戦略にとって重要です。リベースを使って、ブランチの変更が feature ブランチの作成_後に_ターゲットブランチに追加された変更と衝突しないことを確認しましょう。

リベースすると

  1. Git は、最初に機能ブランチを作成した_後に_ターゲットブランチに投入されたすべてのコミットをインポートします。
  2. Git は、feature ブランチにあるコミットを、ブランチからインポートしたすべてのコミットの上に重ねます:

Git rebase illustration

ほとんどのリベースはmain に対して行われますが、release-15-3 のような他のブランチに対してリベースを行うこともできます。また、originの代わりに別のリモートリポジトリ (upstreamなど) を指定することもできます。

リベース前のブランチのバックアップ

リベースや強制プッシュのような破壊的アクションを行う前にブランチをバックアップします:

  1. 機能ブランチをターミナルで開きます:git checkout my-feature
  2. バックアップブランチを作成します: git branch my-feature-backup この時点以降にmy-feature に追加された変更は、バックアップブランチからリストアすると失われます。

ブランチがバックアップされているので、リベースや強制プッシュを試すことができます。何か問題が発生したら、バックアップからブランチを復元しましょう:

  1. 正しいブランチ (my-feature) にいることを確認しましょう:git checkout my-feature
  2. my-feature-backup に対してリセットしてください:git reset --hard my-feature-backup

ブランチのリベース

Git ではリベースはごく一般的なオペレーションで、次のようなオプションがあります:

ブランチをリベースしたユーザーは、そのブランチにコミットを追加したものとして扱われます。プロジェクトに プロジェクトがコミットを追加したユーザーによる承認者を防ぐように設定されている場合に設定されている場合は、ブランチをリベースしたユーザーはマージリクエストを承認することもできません。

通常のリベース

標準的なリベースは、ブランチの直前のコミットを変更せずに再生し、マージが衝突した場合にのみ停止します。

前提条件:

  • ブランチを強制的にプッシュする権限が必要です。

ブランチmy-featureデフォルトブランチからの最新の変更で更新するには (ここではmainを使用します):

  1. mainから最新の変更点を取得します:git fetch origin main
  2. あなたの機能ブランチをチェックしてください:git checkout my-feature
  3. main に対してリベースしてください:git rebase origin/main
  4. ブランチに強制プッシュします。

マージにコンフリクトがある場合は、リベースを続ける前にそれを修正するよう Git が促します。

GitLab UIから

/rebase クイックアクションは、これらの条件がすべて満たされた場合にマージリクエストから直接機能ブランチをリベースします:

  • あなたの機能ブランチにマージ競合が存在しないこと。
  • あなたはソースプロジェクトの開発者ロールを持っています。このロールによって、ソースプロジェクトのソースブランチにプッシュする権限が与えられます。
  • マージリクエストがフォークにある場合、そのフォークはアップストリームプロジェクトのメンバーからのコミットを許可しなければなりません。

UIからリベースするには

  1. マージリクエストに移動してください。
  2. コメントに/rebase と入力してください。
  3. コメント]を選択します。

GitLab は、デフォルトブランチに対する feature ブランチのリベースをスケジュールし、できるだけ早く実行します。

インタラクティブなリベース

対話的リベース (--interactive フラグ、あるいは-i) を使うと、コミットの処理方法を変更しながら同時にブランチを更新することができます。たとえば、ブランチ (HEAD~5) の直近の 5 つのコミットを編集するには、次のように実行します:

git rebase -i HEAD~5

Git は、直近の 5 つのコミットを一番古いコミットから順にテキストエディタで開きます。それぞれのコミットには、そのコミットに対するアクションと SHA、そしてコミットタイトルが表示されます:

pick 111111111111 Second round of structural revisions
pick 222222222222 Update inbound link to this changed page
pick 333333333333 Shifts from H4 to H3
pick 444444444444 Adds revisions from editorial
pick 555555555555 Revisions continue to build the concept part out

# Rebase 111111111111..222222222222 onto zzzzzzzzzzzz (5 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous

コミットの一覧の後には、コミットに対して実行できる一般的なアクションがコメントアウトされた形で表示されます:

  • コミットを選択して、何も変更せずに使用します。デフォルトのオプションです。
  • コミットメッセージを書き換えます
  • コミットを編集してそれを使用し、リベースを一時停止してそれを修正 (変更を追加) します。
  • 複数のコミットをひとまとめにして、機能ブランチのコミット履歴をシンプルにします。

キーワードpick は、各コミットで実行したいオペレーションに応じて置き換えてください。そのためには、ターミナルのテキストエディタでコミットを編集します。

たとえば、MacOSのZshシェルのテキストエディタとしてVimを使用すると、すべてのコミットをまとめてsquash 、またはfixup (結合)することができます:

note
コマンドラインで編集する手順は、オペレーションシステムや使用するシェルによって若干異なります。
  1. キーボードのi を押して、Vim の編集モードに切り替えます。
  2. キーボードの矢印を使用して、2 番目のコミットキーワードをpick からsquash またはfixup (またはs またはf) に編集します。残りのコミットも同じようにします。最初のコミット (pick) は変更しないでください。他のすべてのコミットをこのコミットに統合したいからです。
  3. エスケープ を押して編集モードを終了します。
  4. :wq を入力して「書き込み」(保存)し、「終了」します。
  5. スクシャッシュするとき、Git はコミットメッセージを出力します:
    • # で始まる行はすべて無視され、コミットメッセージには含まれません。それ以外はすべて含まれます。
    • そのままにするには、:wq と入力してください。コミットメッセージを編集するには: 編集モードに切り替えてコミットメッセージを編集し、先ほどと同じように保存します。
  6. リベースする前にまだコミットをリモートブランチにプッシュしていない場合は、強制プッシュせずに変更をプッシュします。すでにコミットをプッシュしていた場合は、代わりに強制プッシュを行います。

プロジェクトのスカッシュオプションの設定

デフォルトブランチのコミット履歴をクリーンに保つには、マージリクエストごとに手動ですべてのコミットをつぶす必要はありません。GitLabはプロジェクトレベルでsquashとmergeのオプションを提供します。

強制プッシュ

Git の複雑なオペレーションでは、リモートブランチを強制的に更新する必要があります。コミットの取り消しやブランチのリセット、ブランチのリベースといったオペレーションは、ブランチの履歴を書き換えてしまいます。Git では、このような破壊的な変更が誤って起こらないようにするために強制更新が必要です。

共有ブランチでの強制プッシュはお勧めしません。

強制プッシュしたいブランチが保護されている場合は、次のいずれかを行わない限り強制プッシュはできません:

そうすれば、強制的にプッシュし、再び保護することができます。

--force-with-lease フラグ

--force-with-lease フラグは強制的にプッシュします。他の人がリモートブランチに追加した新しいコミットを保持するので、--force よりも安全です:

git push --force-with-lease origin my-feature

--force フラグ

--force フラグは強制的にプッシュしますが、他の人がリモートブランチに追加したコミットは保持しません --force--force この方法を使う --forceには、push コマンドに--force フラグか --force -f--force 渡します --force

git push --force origin my-feature