git push --force-with-leaseと--force


182

私は両者の違いを理解しようとしています

git push --force

そして

git push --force-with-lease

私の推測では、ローカルブランチが持っていないコミットがリモートにない場合、後者はリモートにプッシュするだけですか?


「ローカルリモートトラッキングブランチ」。基本的に、リモートは、クライアントが期待するもののように見える必要があります。 git help pushその目的を説明するユースケースがあります(基本的に、誰かが押し上げた変更を破棄しないようにするため)。リモートトラッキングブランチがどのように機能するかは、私には少し不明確です。しかし、おそらく通常は、前回コミットしたとき、fetchまたはpull新しいコミットを行わなかったときの外観を正確に調べる必要があるでしょう。
zzxyz 2018年

4
@zzxyz:の実際の実装は--force-with-lease、最新のCPUでの比較とスワップの命令の実装と同様です。スワップを実行したい人が期待値と新しい値を提供します。スワップを実行するシステムは、期待値と実際の現在値を比較し、2つが等しい場合にのみスワップを実行します。を使用するgit pushと、期待値はリモートトラッキング名に含まれるものになります。たとえば、新しい値と一緒にgit push --force-with-lease origin X独自のorigin/X値を送信します。originのGitは、交換が行われたかどうかを通知します。
torek 2018年

1
Gitでorigin交換が行われた場合は、完了です。そうでない場合は、実行git fetch originして新しい現在の値を取得し、必要に応じて変更をやり直し、もう一度強制的にリースして比較とスワップを実行して再試行できます。
torek 2018年

回答:


172

force リモートブランチをローカルブランチで上書きします。

--force-with-lease(別のチームメンバーや同僚、またはあなたが持っているものによって)リモートブランチにコミットが追加された場合に、リモートブランチの作業を上書きしない、より安全なオプションです。強制プッシュによって他の人の作業を上書きしないようにします。

コマンドに関するあなたの一般的な考えは正しいと思います。リモートブランチの値がローカルマシンのリモートブランチと同じである場合、リモートを上書きします。同じ値でない場合は、コードの作業中にリモートブランチに対して他の誰かが行った変更を示しているため、コードを上書きしません。明らかにリモートに追加のコミットがある場合、値は同じにはなりません。

--force-with-leaseチームメイトのコードを上書きしないようにしたいときに使用するオプションとして考えています。私の会社の多くのチーム--force-with-leaseが、フェールセーフのデフォルトオプションとして使用しています。ほとんどの状況では不要ですが、別の人がリモートに貢献したことを偶然上書きしてしまった場合でも、多くの頭痛を軽減できます。

ドキュメントをご覧になったと思いますが、ここにはさらに詳しい説明が含まれている可能性があります。

https://git-scm.com/docs/git-push


38

信頼できる情報源や公式な情報源からの回答を探しています。

コメント他の回答torekが言及した「比較して交換」は、Git自体のソースによってさらに説明されています

後者は、リモートがローカルブランチにないコミットを持たない場合にのみリモートにプッシュしますか?

この機能はこのコミットで導入されました(2013年12月、Git v1.8.5-rc0)

--force-with-lease 特に明記されていない限り、現在の値を適切なデフォルトと同じにすることを要求することで、更新されるすべてのリモート参照を保護します。

現時点では、「ある程度の妥当なデフォルト」は、「更新されるリモートの参照に対するリモート追跡ブランチの値」として暫定的に定義されており、そのようなリモート追跡ブランチがない場合はエラーになります。

したがって、「リース」とは次のことを意味します。

" force-with-lease":リフェッチされた履歴がどうあるべきかを決定するためにフェッチしたときに、参照でリースを取得したと想定し、リースが壊れていない場合にのみプッシュバックできます。

ソースはまだ "cas"に言及しています:

  • このオプションは、元々は " cas"( "compare and swap"の略)と呼ばれていましたが、あまりに専門的すぎたため誰も好まなかった名前です。
  • 2回目の試行では「lockref」と呼ばれていました(概念的には、ロックを取得した後のプッシュに似ているため)。「lock」という言葉は、他のユーザーによるプッシュを拒否する可能性があることを意味しており、このオプションの機能ではありません。
  • このラウンドでは、「リース付きフォース」と呼んでいます。
    リフェッチされた履歴がどうあるべきかを決定するためにフェッチしたときに、参照でリースを取得したと想定し、リースが壊れていない場合にのみプッシュバックできます。

つまり、「git push --force-with-lease--force

push --force-with-leaseデフォルト」で述べたように、Git 2.13(2017年第2四半期)が言及しているように、バックグラウンドプロセス(Gitプラグインを備えたIDEにあるプロセスなど)が実行されている場合、このオプション--force-with-lease無視できますgit fetch origin
その場合、--force優勢です。


29

git push --forceは、ローカルにあるものでリモートリポジトリを無条件に上書きするため、破壊的です。gitのpush --forceは、共有リポジトリに既にプッシュされている他のコミットを破壊する可能性があるため、強くお勧めしません。強制プッシュの最も一般的な原因の1つは、ブランチのリベースを強制される場合です。

例えば。AliceとBobの両方が取り組む機能ブランチを持つプロジェクトがあります。どちらもこのリポジトリのクローンを作成して作業を開始します。アリスは最初に機能の一部を完成させ、これをメインリポジトリにプッシュします。これはすべて順調です。ボブも作業を終えましたが、プッシュする前に、いくつかの変更がマスターにマージされていることに気付きました。クリーンなツリーを維持するために、彼はmasterブランチに対してリベースを実行します。もちろん、彼がこのリベースのブランチをプッシュしようとすると、拒否されます。しかし、アリスが彼女の仕事をすでにプッシュしていることに気付かずに、彼はプッシュ--forceを実行します。残念ながら、これにより、中央リポジトリにあるアリスの変更のすべての記録が消去されます。

--force-と、リースが行うことは、それは我々が期待している状態でない限り、分岐を更新することを拒否しています。つまり、誰も上流のブランチを更新していません。実際には、これは上流のrefが期待どおりであることをチェックすることで機能します。refはハッシュであり、親のチェーンをその値に暗黙的にエンコードするためです。

ここに git push --forceとgit push --force-with-leaseに関する良い投稿があります。


2
私が理解していないこと-あなたがマスターでリベースするなら、あなたはそれなしで プッシュすることができるはず--force-with-leaseですか?--force-with-leaseマスターでリベースした後になぜ必要なのですか?
Alexander Mills

3
@AlexanderMillsには​​問題を引き起こす可能性がある可能性があります。あなたがこれをマスターする最後の人である場合は問題ありませんが、別のタスクで作業している別の人がいる場合、深刻な問題を引き起こす可能性があります。最初にA-B-Cがマスターだったと言います。最初にアリスがA-B-C-D-Eにし、同時にボブがA-B-C-F-Gにします。アリスが最後の人の場合はA-B-C-D-E-F-Gをリベースした後のマスターでは問題は発生しませんが、マスター災害で同時にA-B-C-D-E-Rをプッシュしようとする別の人が起こります!! !
Shakil

3
--forceコミットは失われず、デタッチされるだけです。git checkout <SHA>またはのようにgit reset --hard <SHA>、リモートリポジトリのメンテナがGCまたはプルーニング操作を行わないと想定すると、それらはハッシュによって復活できます。
キースラッセル

1
「gitのpush --forceは、共有リポジトリにすでにプッシュされている他のコミットを破壊する可能性があるため、強くお勧めしません」このステートメントは、強く意見に基づいています。強制プッシュの実行は、通常のgitリベースコードレビューワークフローの一部です。また、すでに述べたように、これを行った後でもコミットを取得できます。
エティエンヌ

9

サーバー上のすべての事前受信フックがプッシュを受け入れると仮定すると、これは常に成功します:

git push --force

これが続行する前に特定のクライアント側のチェックを実行するのに対して、

git push --force-with-lease

特定のチェックを手動で実行できます。次に「リースチェック」アルゴリズムを示します。

  1. 現在のブランチを把握します。

  2. を実行しますgit for-each-ref refs/remotes。gitクライアントが現在のブランチの上流の状態に対応すると考えているcommit-idに注意してください。

たとえば、ブランチ「foo」を使用している場合は、「refs / remotes / origin / foo」に関連付けられているcommit-idに注意してください。

  1. 現在、上流のgitサーバー上のリモートブランチの実際のcommit-idを確認します。

  2. 手順2と手順3で抽出したcommit-idが一致する場合のみ、「git push」を続行します。言い換えれば、ローカルのgitクローンの上流の概念が実際の上流と一致する場合にのみ続行します。

ここに悲しい意味があります。git fetch「refs / remotes / origin / *」の下にあるすべての参照を最新バージョンに更新するため、このコマンドの組み合わせは基本的に次のものと同じですgit push --force

git fetch

# The command below behaves identically to "git push --force"
# if a "git fetch" just happened!

git push --force-with-lease

この固有の弱点を回避git push --force-with-leaseするには、決して実行しないようにしgit fetchます。代わりに、私は常にgit pull --rebaseアップストリームと同期する必要があるときはいつでも実行しgit pullます。これは、refs / remotesの下の単一のrefのみを更新し、「リース」を--force-with-lease有効に保つためです。


1
チェックがクライアント側である場合、競合状態の可能性はありますか?つまり、誰かがチェックの後、強制プッシュの前に変更をプッシュしますか?
craq

2

Force-with-leaseは必ずしも安全ではありません。それはシルヴィが言ったように機能します。注:gitでは、ブランチはコミットのポインタにすぎません。そして、コミットはゼロ以上の親コミットを指します。ハードgitリセットと強制プッシュまたは-with-force-with-leaseを使用せずにブランチを完全に変更したとしても、それは必ずしも大きな問題ではありません。ローカルのgit reflogを使用して、ブランチのローカルヒント(その時点でのHEADはどこでしたか)がどのように変更されてリセットされ、ブランチが再度プッシュされたかを確認できます。そうすると、リモートブランチの新しいコミットのみが失われますが、チームメンバーがそれらを復元することもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.