機能ブランチのリベース後にGitプッシュが拒否されました


919

OK、これは単純なgitシナリオだと思いましたが、何が欠けていますか?

私はmaster枝と枝を持っていfeatureます。私はいくつかの作業master、いくつかの作業feature、そしていくつかの作業を行いmasterます。私はこのようなものになります(辞書式順序はコミットの順序を意味します):

A--B--C------F--G  (master)
       \    
        D--E  (feature)

私がする何の問題もないgit push origin master遠隔保つためにmasterもして、更新をgit push origin feature(ときにfeature私のためのリモートバックアップを維持するために)feature仕事を。今まで、私たちは元気です。

しかし今、私はマスターfeatureF--Gコミットの上にリベースしたいので、私git checkout featuregit rebase master。まだいい。今私たちは持っています:

A--B--C------F--G  (master)
                 \
                  D'--E'  (feature)

問題:バックアップに私はしたい瞬間新しいリベースfeatureで分枝状git push origin featureプッシュが拒否されたツリーがリベースによって変化しているため。これはでのみ解決できgit push --force origin featureます。

私は--forceそれが必要かどうか確信せずに使用することを嫌います。それで、私はそれが必要ですか?リベースは必ずしもpushがフルであることを意味し--forceますか?

この機能ブランチは他の開発者と共有されていないので、強制プッシュで事実上問題はありません。データを失うことはありません。質問はより概念的です。

回答:


682

問題は、git pushリモートブランチをローカルブランチに高速転送できることを前提としていることです。つまり、ローカルブランチとリモートブランチの違いはすべて、ローカルに次のような新しいコミットがいくつかあることです。

Z--X--R         <- origin/some-branch (can be fast-forwarded to Y commit)
       \        
        T--Y    <- some-branch

git rebaseコミットを実行すると、DとEが新しいベースに適用され、新しいコミットが作成されます。つまり、リベース後は次のようになります。

A--B--C------F--G--D'--E'   <- feature-branch
       \  
        D--E                <- origin/feature-branch

この状況では、リモートブランチをローカルに早送りすることはできません。ただし、理論的にはローカルブランチをリモートにマージすることはできますが(もちろん、その場合は必要ありません)、git push早送りマージのみを実行するため、スローとエラーが発生します。

そして、--forceオプションは、リモートブランチの状態を無視して、プッシュするコミットに設定するだけです。したがって、git push --force origin feature-branch単にorigin/feature-branchlocalでオーバーライドしますfeature-branch

私の意見では、機能ブランチのリベースmasterとリモートリポジトリへの強制プッシュバックは、あなたがそのブランチで作業する唯一のユーザーである限り問題ありません。


68
正直に言うと、機能ブランチの元のバージョンをリベースした1つにプルしてマージすると、リベースの全体的なアイデアがなくなります。
KL-7

24
多分私はあなたを正しく理解していなかったかもしれませんが、機能ブランチをプルして新しいマスターブランチにリベースすると、機能ブランチのリモートバージョンは新しいバージョンに早送りできないため、無理に押し戻すことはできません。 (リベース)バージョンの機能ブランチ。それはまさにOPが彼の質問で述べたことです。リベース後、プッシュする前に行うgit pull feature-branch場合、このプルは(機能ブランチのリモートバージョンとローカルバージョンをマージすることにより)新しいマージコミットを生成します。したがって、リベース後に不要なマージを取得するか、でプッシュし--forceます。
KL-7

6
ああ、わかったと思います。Mark Longairの回答と同じアプローチを説明しています。ただし、マージコミットは生成されます。場合によっては役立つかもしれませんがpush --force、マージのコミットをまったく行わずにコミット履歴を線形に保つために、ほとんどの場合自分の機能ブランチでリベースを使用しています(したがって問題ありません)。
KL-7

11
「force-push」の問題は、実際には「ルーズなもの」(以前のコミット)であり、通常はどのバージョン管理システムでも決して可能ではないことです。そのため、少なくとも1つの「master-ish」ブランチには潜在的な損傷を制限するために、force-pushesを受け入れない設定。(次のいずれかを挙げてください:不機嫌な/解雇された従業員、自分の愚かさ、疲れていて過労した「決定」...)。
Frank Nocke

13
--force-with-lease@hardevが示唆するように素晴らしいオプションです
augustorsouza '

466

-fまたは--forceを使用する代わりに、開発者は

--force-with-lease

どうして?なぜなら、リモートブランチの変更をチェックするからです。これは絶対に良い考えです。JamesとLisaが同じ機能ブランチで作業していて、Lisaがコミットをプッシュしたとしましょう。Jamesはローカルブランチをリベースし、プッシュしようとすると拒否されます。もちろん、Jamesは、これはリベースと--forceの使用によるものであり、Lisaのすべての変更を書き換えると考えています。ジェームズが--force-with-leaseを使用していた場合、他の誰かによってコミットが行われたという警告を受け取ることになります。リベース後にプッシュするときに--force-with-leaseではなく--forceを使用する理由がわかりません。


33
素晴らしい説明。git push --force-with-leaseたくさん助けてくれました。
ckib16 2017年

5
これは便利なコメントですが、質問に対する答えではありません。
ダリン

4
これが答えです。マスター/開発をリベースすると問題が発生します。これがまさに--force-with-leaseが存在する理由です。
Tamir Daniely 2018

3
これは受け入れられる答えになるはずです。説明された問題を正確に解決します-その間に誰かがコミットした場合、強制せずに強制的にプッシュします。
Luzian

3
私は受け入れられた答えとこれの両方が質問に対処すると思います。受け入れられた答えは、あなたが強制する必要がある理由を説明しています。そして、これはなぜ--force-with-lease使用の懸念に対処するのかを説明しています--force
Jeff Appareti '13

48

代わりに「checkout -b」を使用するので、理解しやすくなります。

git checkout myFeature
git rebase master
git push origin --delete myFeature
git push origin myFeature

削除すると、異なるSHA IDを含む既存のブランチをプッシュすることができなくなります。この場合、リモートブランチのみを削除しています。


6
これは特に、チームにすべてのgit push --forceコマンドを拒否するgitフックがある場合にうまく機能します。
Ryan Thames

1
それがうまくいったことをありがとう これは私がよく理解するために読んだ詳細です。これは、強制プッシュを実行したくない、または実行できない場合に非常に役立ちます。リモートブランチの削除リベース
RajKon 2016

5
これはと同じ結果push --forceになるため、git repoを回避する唯一の方法--forceです。そのため、これは決して良い考えではないと思います-リポジトリが許可するpush --forceか、正当な理由で無効にします。Nabiの回答は--force、他の開発者からのコミットが失われたり、問題が発生したりするリスクがないため、リモートリポジトリで無効になっている場合に適しています。
ローガンピックアップ

19

これに対する1つの解決策は、msysGitのリベースのマージスクリプトが行うことを実行することです。リベースの後、古いのfeaturewithにマージし-s oursます。あなたはコミットグラフで終わります:

A--B--C------F--G (master)
       \         \
        \         D'--E' (feature)
         \           /
          \       --
           \    /
            D--E (old-feature)

...そしてあなたのプッシュはfeature早送りになります。

つまり、次のことができます。

git checkout feature
git branch old-feature
git rebase master
git merge -s ours old-feature
git push origin feature

(テストされていませんが、それは正しいと思います...)


26
git rebasemaster機能ブランチにマージする代わりに)を使用する最も一般的な理由は、クリーンな線形コミット履歴を作成することです。あなたのアプローチのコミットで歴史はさらに悪化します。そして、リベースは以前のバージョンを参照せずに新しいコミットを作成するので、このマージの結果が適切であるかどうかさえわかりません。
KL-7

6
@ KL-7:の全体的なポイントはmerge -s ours、以前のバージョンに親参照を人工的に追加することです。確かに、歴史はきれいに見えませんが、質問者はfeatureブランチのプッシュを強制しなければならないことに特に悩まされているようで、これはそれを回避します。リベースしたい場合は、多かれ少なかれどちらかです。:)より一般的に、私はそれがmsysgitプロジェクトが....このないことは興味深いことだと思う
マーク・Longair

@ KL-7:ちなみに、私はあなたの回答を+1しました。これは明らかに正しい回答です-これも面白いかもしれないと思っただけです。
Mark Longair 2012年

少なくとも私にとっては間違いなく興味深いものです。ありがとうございました。私はours以前に戦略を見たことがありますが、私たちのブランチの変更を使用してそれらを自動的に解決することにより、競合状況にのみ適用されると思いました。動作が異なることがわかりました。そして、そのように動作することは、リベースバージョンが必要な場合(たとえば、リポメンテナがそれをクリーンに適用masterするため)に強制プッシュを回避したい場合(非常に多くのPPLが何らかの理由で機能ブランチを使用している場合)に非常に役立ちます。
KL-7

15

このブランチには開発者が1人しかいない場合とそうでない場合があります。つまり、現在(リベース後)はorigin / featureと一致していません。

そのため、次のシーケンスを使用することをお勧めします。

git rebase master
git checkout -b feature_branch_2
git push origin feature_branch_2

うん、新しいブランチ、これは--forceなしでこれを解決するはずです。これは一般的にgitの大きな欠点だと思います。


3
申し訳ありませんが、既存のブランチを強制的に上書きしないように「生成ブランチを保持」しても、「オーバーライドできる孤独な機能開発者」や、機能ブランチで作業している複数の人々(そのブランチを「インクリメント」して通知する必要がある)には役立ちません上に移動するには、人々)。—バージョン管理システム内での手動バージョン管理(“ thesis_00.doc、thesis_01.doc、...”)に
似てい

2
さらに、1つのブランチ名でgithub PRを開いている場合、これは役に立ちません。プッシュした新しいブランチ名の新しいPRを作成する必要があります。
gprasant 2015

1
@frankee半分は私の経験から正しい。孤独な開発者にとっては、ええ、強制プッシュは十分簡単ですが、後で気になるかもしれません。+新しい開発者が参加しましたか?または-ハードリセットを使用していないCIシステム 共同作業をしているチームの場合、新しいブランチ名を伝えるのは簡単です。これも簡単にスクリプト化できます。+チームの場合は、日常の作業中ではなく、ローカルまたはブランチをマージする準備ができたときにリベースすることをお勧めします、追加のコミットは、結果としてリベース/マージの競合を処理するよりも問題が少ないです。
JAR.JAR.beans 2015年

@gprasant for PR、繰り返しますが、これはリベースするのは間違っていると思います。実際には、PR修正を含む単一のコミットを見たいと思います。リベース(スカッシュ)は、後でマスターへのマージの一部として、PRがすべて完了して準備ができたときにのみ行う必要があります(そのため、新しいPRを開く必要はありません)。
JAR.JAR.beans 2015年

13

強制プッシュを回避する私の方法は、新しいブランチを作成してその新しいブランチで作業を継続し、ある程度の安定後、リベースされた古いブランチを削除することです。

  • チェックアウトしたブランチをローカルでリベースする
  • リベースされたブランチから新しいブランチへのブランチ
  • そのブランチを新しいブランチとしてリモートにプッシュします。リモートの古いブランチを削除する

1
なぜこのオプションを愛さないのですか?それは間違いなく最もクリーンで、最も単純で、最も安全です。
cdmo 2018年

ブランチ名を追跡する約200のシステムがあり、タスクに特定の名前を付ける必要があるため、ブランチの名前変更を行うと、プッシュごとに名前が変更されるので、気が散ります。
Tamir Daniely

@TamirDaniely私は試していませんが、プッシュする前に古いブランチを(リモートから)削除して、同じ古い名前で新しいブランチをプッシュすると問題が解決しますか?
Nabi

2
@Nabiそれはまさに--force-with-leaseが行うことですが、自分のものではない新しいコミットがないことも確認します。
Tamir Daniely

12

他の人があなたの質問に答えました。ブランチをリベースする場合、そのブランチを強制的にプッシュする必要があります。

リベースと共有リポジトリは一般的にうまくいきません。これが書き換え履歴です。他の人がそのブランチを使用しているか、そのブランチからブランチしている場合、リベースは非常に不愉快になります。

一般に、リベースはローカルのブランチ管理に適しています。リモートブランチ管理は、明示的なマージ(--no-ff)で最適に機能します。

また、マスターを機能ブランチにマージすることも避けます。代わりに、マスターにリベースしますが、新しいブランチ名を使用します(たとえば、バージョンサフィックスの追加)。これにより、共有リポジトリをリベースする問題が回避されます。


5
例を追加していただけませんか?
Thermech、2014

8

何が問題になっているgit merge masterfeatureブランチは?これにより、メインラインブランチから分離したまま、作業内容が保持されます。

A--B--C------F--G
       \         \
        D--E------H

編集:ああ、申し訳ありませんが問題のステートメントを読みませんでした。を実行するときに、力が必要になりますrebase。履歴を変更するすべてのコマンドには--force引数が必要です。これは、(古い仕事を失うことからあなたを防ぐためのフェイルセーフであるDE失われてしまいます)。

だから、行ってgit rebase(一部として隠されていないが、のような木の表情を作っているDE、もはや名前のブランチであるが)。

A--B--C------F--G
       \         \
        D--E      D'--E'

そのため、新しいfeatureブランチを(それを含めて)プッシュしようとするとD'、およびE'が失わDEます。


3
それには何の問題もありません。それは私が必要としているものではありません。私が言ったように、質問は実用的というより概念的です。
ユヴァルアダム2012

4

私にとって簡単な手順に従うとうまくいきます:

1. git checkout myFeature
2. git rebase master
3. git push --force-with-lease
4. git branch -f master HEAD
5. git checkout master
6. git pull

上記のすべてを行った後、次のコマンドでmyFeatureブランチも削除できます。

git push origin --delete myFeature

3

次は私のために働きます:

git push -f origin branch_name

そしてそれは私のコードを削除しません。

ただし、これを回避したい場合は、次の操作を実行できます。

git checkout master
git pull --rebase
git checkout -b new_branch_name

その後、すべてのコミットを新しいブランチにチェリーピックできます。 git cherry-pick COMMIT ID 次に、新しいブランチをプッシュします。


5
-fはのエイリアスです--force。これは、可能であれば質問が回避しようとしているものです。
epochengine、2015

1

OPは問題を理解しているので、より良い解決策を探すだけです...

これは練習としてどうですか?

  • 実際の機能開発ブランチを使用します(リベースや強制プッシュを決して行わないため、他の機能開発者はあなたを嫌いません)。ここでは、定期的にmainからの変更をマージで取得します。メシエの歴史、そうですが、人生は簡単で、誰も彼の仕事に邪魔されません。

  • 2番目の機能開発ブランチを用意します。1つの機能チームメンバーが定期的にすべての機能コミットをプッシュし、実際にリベースし、実際に強制します。したがって、かなり最近のマスターコミットにほぼ完全に基づいています。機能が完了したら、そのブランチをマスターの上にプッシュします。

このメソッドのパターン名はすでに存在している可能性があります。

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