可能性
これが起こった可能性があるいくつかの方法を考えることができます。
早送りマージ。 これが一番の原因だと思います。これは私が信じていることです:
release-1.3
に合併されましたdevelop
- 直後に、
develop
早送りマージされましたrelease-1.3
それで全部です。 release-1.3
にマージされた後、 にマージされた誰かdevelop
に対して追加のコミットが行われる前。可能であれば、デフォルトで早送りマージを行います (半分の時間で間違ったことを行う「機能」)。これが、グラフがわかりにくく見える理由です。release-1.3
develop
release-1.3
git
早送りマージは、結果のグラフに直接的な証拠を残さないことに注意してください。通常のマージとは異なり、早送りマージは新しいコミット オブジェクトを作成せず、既存のコミットは変更されません。早送りマージは、既存のコミットを指すようにブランチ参照を調整するだけです。
早送りマージの直接的な証拠はありませんが、各ブランチの最初の親パスをたどることでその 1 つのマージ コミットに到達できるという事実は、これが早送りマージの結果であることを強く示しています。
git branch
の代わりに臨時記号git checkout
。私が取り組んでいたプロジェクトで、Git を初めて使用する別の開発者が、git branch foo
branch に切り替えようとしてタイプミスを犯しましたfoo
。これは完全に自然な間違いであり、専門家でさえ疲れたときに犯す可能性があります. とにかく、この間違いは最終的に、入力されていないにもかかわらず、早送りマージのように見えるものになりましたgit merge
。ここでも同様のことが起こった可能性があります。シナリオの展開は次のとおりです。
- ユーザーは
develop
ブランチをチェックアウトしており、develop
たまたまこのミステリーの中心にあるマージ コミットを指しています。
release-1.3
ユーザーはブランチに切り替えて作業を行いたいと考えていますが、誤って のgit branch release-1.3
代わりに入力してしまいgit checkout release-1.3
ました。
- これは新しいクローンであるため、
release-1.3
まだローカル ブランチはありません (のみorigin/release-1.3
)。したがって、Git はrelease-1.3
、同じマージ コミットを指す、という名前の新しいローカル ブランチを問題なく作成します。
- ユーザーがいくつかのファイルを編集している間、しばらく時間が経過します。
- コミットの準備として、ユーザーは
git status
. 現在のブランチはまだdevelop
であり、そうではないためrelease-1.3
、Git は「ブランチ開発中」と出力します。
- ユーザーは驚いて、「ずっと前に release-1.3 に切り替えたんじゃないの? ああ、ブランチを切り替えるのを忘れていたに違いない」と考えます。
- ユーザーは を実行しますが
git checkout release-1.3
、今度は正しいコマンドを覚えています。
- ユーザーはコミットを作成して実行します
git push
。
- Git のデフォルトのプッシュ動作 ( を参照) はであるため、アップストリーム ブランチが構成されていなくても、
push.default
プッシュ先のアップストリームのブランチを選択します。git help config
matching
release-1.3
git push
release-1.3
- の新しいバージョンは
release-1.3
以前の の子孫であるrelease-1.3
ため、リモート リポジトリは喜んでプッシュを受け入れます。
これは、質問で提供したグラフを作成するために必要なすべてです。
develop
意図的にマージされたと仮定しますrelease-1.3
develop
へのマージが意図的なものである場合(誰かが出荷するのに十分なrelease-1.3
意識的な決定を行った場合)、これは完全に正常であり、正しいことです。develop
視覚的な違いはありますが、青と黒の線は両方ともrelease-1.3
枝にあります。青い線はたまたま枝にもあります。develop
唯一間違っているのは、歴史を見直して何が起こっているのかを理解するのが少し面倒だということです (それ以外の場合、この質問はありません)。これが再び起こらないようにするには、次の経験則に従ってください。
- 2 つの異なる名前のブランチをマージする場合 (たとえば、
develop
とrelease-1.3
、常にgit merge --no-ff
.
develop
同じブランチの 2 つのバージョン (例:とorigin/develop
)をマージする場合は、常にgit merge --ff-only
. 早送りできないためにそれが失敗した場合は、git rebase
.
上記の経験則に従った場合、グラフは次のようになります。
* (develop)
| * (release-1.3)
* | Merge...
|\|
| * Added...
| * using ...
* | adding...
| * Hide s...
* | Date ...
* | updati...
* | Candi...
| * Locali...
| * <---- merge commit that would have been created by
|/| 'git merge' had you used the '--no-ff' option
* | Merge...
|\|
| * Un-ign...
| * Added...
* | Merge...
|\|
| * Remov...
| * Move...
* | Fixed...
追加のマージ コミットにより、履歴が読みやすくなっていることに注目してください。
develop
へのマージがrelease-1.3
間違っていた場合
どっ!リベースと強制プッシュが必要なようです。これでは、このリポジトリの他のユーザーは満足しません。
これを修正する方法は次のとおりです。
- 実行します
git checkout release-1.3
。
- その中間コミットの sha1 を見つけます (2 つのブランチが一緒になる場所)。と呼びましょう
X
。
実行しますgit rebase --onto X^2 X
。結果のグラフは次のようになります。
* (develop)
| * (release-1.3)
* | Merge...
|\ |
| * | Added...
| | * Added...
| * | using ...
| | * using ...
* | | adding...
| * | Hide s...
| | * Hide s...
* | | Date ...
* | | updati...
* | | Candi...
| * | Locali...
|/ * Locali...
* / Merge...
|\|
| * Un-ign...
| * Added...
* | Merge...
|\|
| * Remov...
| * Move...
* | Fixed...
これでブランチは修正されますが、コミットrelease-1.3
の 2 つのバージョンがあることに注意してください。次の手順では、これらの重複をブランチrelease-1.3
から削除します。develop
- 実行します
git checkout develop
。
git branch temp
このコミットの一時的なプレースホルダーとして機能するために実行します。
- 実行して、ブランチ
git reset --hard HEAD^^
から 2 つのコミットを削除します。tip コミットと、古いバージョンの を にマージするコミットです。そのヒントのコミットは後で復元します。develop
develop
release-1.3
develop
- を実行して、新しいブランチとその祖先
git merge --no-ff release-1.3^
の 2 番目のコミットを にマージします。release-1.3
develop
- 実行
git cherry-pick temp
して、手順 6 で削除されたヒント コミットを元に戻します。
git branch -D temp
一時的なプレースホルダー ブランチを削除するために実行します。グラフは次のようになります。
* (develop)
| * (release-1.3)
* | Merge...
|\|
| * Added...
| * using ...
* | adding...
| * Hide s...
* | Date ...
* | updati...
* | Candi...
| * Locali...
* | Merge...
|\|
| * Un-ign...
| * Added...
* | Merge...
|\|
| * Remov...
| * Move...
* | Fixed...
実行git push -f origin release-1.3 develop
して、上流のブランチを強制的に更新します。
今後このようなことが起こらないようにする
アップストリーム リポジトリを制御でき、いくつかのフックをインストールできる場合は、ブランチの新しいバージョンから開始して最初の親パスをたどることで、古いバージョンのブランチに到達できないプッシュを拒否するフックを作成できます。 . これには、によって作成された愚かなコミットgit pull
を拒否するという利点もあります。