「git push問題:非早送り」とは何ですか

このミニノートは、主に質問に対する答えです。 私のアカウントは読み取り専用なので、これが回答方法です。 「しかし、人生は良くなっています!」©



質問と回答を読んだ後の最初の結論-defuzが示唆したようにしないでください。 彼は問題の本質を理解しておらず、あなたが彼の提案通りにやると、おそらくデータを失うことになるでしょう。

第二に、 alekciyもまったく正しくありませんが、ここではデータを失う可能性ははるかに低くなります。 ほとんどなし。

そして3つ目:いまいましい、使用されたツールを所有することが本当に必要だと人々はいつ理解するでしょうか? ドキュメントを読んでください!





gitの作業の詳細と「内部」の多くは省略または簡略化されていますが、それらの不利益につながるものではないことをすぐに言わなければなりません。 それは理解の外観と記事の目的であり、あなたがすでにそれを持っているなら、そしてそれが真実であると確信しているなら:)-ここであなたにとって新しいものは何もないでしょう。 まだ理解していない場合は、行きましょう!



まず、早送りとは何かを調べます。 これは、マージ操作を実行するためのオプション(メソッド、戦略)の1つにすぎません。 現在のコミット(マージが行われるコミット、Aで示す)がマージされたコミットの祖先(マージが行われるコミット、Bで示す)の場合のみ可能です。 グラフィカルに、これは次のようになります(xは、私たちにとって重要ではない他のコミットです):



 ......-xxxxAxxx-B




コミット履歴が次のような場合:

 ......-B-xxxA
          \
           \ xxx-B


早送り方法を使用してAからBにマージすることはできません。AはBの祖先ではありません。同様に、この状況では、BからAへの早送り方法を使用してマージを実行できません。 AからBからBへ-可能であり、これらのマージのいずれかを実行する場合、git(デフォルト)は早送り方法を使用します。



次に理解すべきことは、プッシュとは何かです。 この操作には2つの重要なポイントがあります。 まず、「反対側へ」のコミットに関するデータの転送だけでなく、「反対側」での必須の後続操作のマージでもあります。 2番目:マージ操作の「その側」は、早送りのみを使用します。 なぜすべてがそうですか?



まあ、最初は明らかです。 ストーリーに何かを入れるには、ストーリーに新しいコミットオブジェクト(現在のオブジェクトの子孫)を作成し、ブランチの最後のコミット(HEADと呼ばれる)へのポインターをこの新しいコミットに変更する必要があります。 概して、これにはコミットとマージの2つの操作しかありません(マージ、ほとんどの場合、新しいコミットの出現につながります)。 明らかに、サーバー上で手動でコミットする人はいません。 また、単に履歴(新しいコミット)をサーバーに転送するだけでも、HEADは変更されません。 そのため、サーバーは単に新しいコミットとのマージを強制されます-HEADを変更するための別のオプションがないだけです。



2番目の点も難しくはありませんが、すぐには明らかではありません。 サーバーが早送りのみを使用してマージを行うのはなぜですか? 結局、 同じタコという他の素晴らしいメソッドがたくさんありますが、それらのサーバーは使用しません。 なんで? すべてが非常に簡単です。(すでに追加したものに加えて) 新しいコミットを生成しない唯一のオプションは、早送りです。 結局、gitは作成者がコミットを指定する必要があるため、サーバーは新しいコミットを生成できません。サーバーはコミットのみを保存できます。 さらに重要なこと-マージ中のこの方法(早送り)は決して競合を引き起こしません :早送り方法自体の性質により、マージ中の競合は不可能です。 これはサーバーにとって重要です。競合が発生した場合、誰も解決できないためです。 コンピューターがそれを理解できなかったので、それは人が解決できるように、それが競合である理由です。



これらの基本的なことを理解するだけで、状況を「明確にする」ことができます。 しかし、それはどのようにして起こるのでしょうか?



すべてが非常に単純です:リポジトリが複数の人(または1人ですが、複数のマシン)で使用されているか、ローカルの保管庫でリベースが行われました。 これらのオプションのいずれでも、2番目の画像に表示されるものが可能です。 そして、この状況が発生したので(つまり、「git fetch」を実行し、すべてがそうであることがわかりました)、HEADサーバーでA(リモート/起源/マスター)を指していることに同意しましょうローカル-B(マスター)上。



問題を解決する方法は? 2つのオプションがあり、どちらもAがそのようなコミットとマージする(Xと呼びましょう)という事実につながります。Aはその先祖になります。 これを達成する方法は?



オプション1:マージ。 コード(B)とサーバー上にあるコード(A)をローカルにマージする必要があります。 マージの結果、ローカル履歴に同じコミットが追加され、同じX-プッシュサーバーはプッシュを拒否しません。 納得させるために-写真:

 ......- B-xxxA-
          \ \ <-マージ操作AからB:git merge origin / master
           xxx-b-x




オプション2:リベース。 リベースは、ブランチの「ルート」を変更するようにストーリーの一部を「転送」する操作ですが、ブランチ自体は変更しません。 わかりやすくするために、再び画像を示します。 初期状態は図2と同じで、リベース後の状態は次のようになります。

 ......-B-xxxA
                 \ <-リベース操作:git rebase origin / master
                  xxx-b


その結果、コミットXの役割はBによって果たされます-しかし、これは別のBです。 これは、リベースの前後にcommit-idを調べることで簡単に確認できます。 ただし、すでに述べたように、ブランチ自体(つまり、それを構成するコミットの内容)は変更されません。commit-idのみが変更されます。 しかし、目標は達成されています-あなたはプッシュを行うことができます。



どのオプションを選択するかはあなた次第です。 ネットでは、これについて複数の犬のパックが食べられました。 初心者向けの推奨事項は、よりシンプルでエラーが発生しにくいため、最初のオプションです。



3番目の解決策があり、プッシュプッシュ提案したのはdefuzでした。次に例を示します。

git push origin +master







サーバーはコミットを受け入れ、 無条件に HEADを変更して、最後のコミットを指すようにします-つまり、BからAへのすべての「あなたの」コミットはサーバーです。 「忘れて」。 これは最初に話したまさにデータ損失です。 ただし、これがまさに必要な操作である場合があります。 しかし、このようなケースがあり、これを理解しているのであれば、おそらくこの記事は長い間必要とされていなかったでしょう。



さて、上記のすべてのポイントを置くために-答えalekciyに関して。 そのバージョンは強制プッシュを使用しないため、データの損失はありません。 しかし、マージ後、まず、隠し場所に入れるものが何もない(gitによってまだ監視されていない新しいファイルを除く)こと、そして次に、リベースが不要になります(そして、新しいファイルがある場合、それは途切れます) 。



この資料が何が起こっているかを明確にし、問題の解決に役立つことを願っています。



PS1:「gitk -a」(Linux)を使用してストーリーを表示すると非常に便利です

UPD: Terentichは Windowsでも正常に動作します。

UPD: eyeofhellそしてOSXでは、これも機能します:)

PS2:Stash(および他の多くの操作)は、別の議論のトピックです。



All Articles