7

特定の git 動作について 1 つの混乱があります。

手順と状況は次のとおりです (コマンドのリストも後で示します)。

  1. master と XBranch の 2 つのブランチがあります。
  2. どちらにも src/a.txt ファイルがあります。そのコンテンツは「古いコンテンツ」です
  3. XBranch で、src/a.txt の名前を src/b.txt に変更mvgit rmますgit add
  4. マスターで、ファイルの名前を a.txt に変更します。コミット中に私はやっgit rm src/a.txtたが忘れていたgit add src/b.txt マスターでは:git rm src/a.txtそしてgit commit

  5. マスターで、ファイル b.txt の内容を「New Content

  6. マスターで私はそうしgit add src/b.txtgit commit
  7. マスターで私は:git merge XBranch

ファイル src/b.txt が競合していますが、これは完全に理解できます。しかし、内容は「Old Content」です。なんで?

なぜそれは次のようなものではありません

<<<<<<< HEAD
New Content
=======
Old content
>>>>>>> XBranch

コマンドのリスト:

sabya@SABYA-PC d:/merge_temp/test/case2
$ mkdir source

sabya@SABYA-PC d:/merge_temp/test/case2
$ git init
Initialized empty Git repository in d:/merge_temp/test/case2/.git/

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ mkdir src

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ vi src/a.txt

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ cat src/a.txt
Old Content

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ git add src/

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ git commit
[master (root-commit) 148500e] added src/a.txt
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 src/a.txt

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ git branch XBranch

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ git checkout XBranch
Switched to branch 'XBranch'

sabya@SABYA-PC d:/merge_temp/test/case2 (XBranch)
$ mv src/a.txt src/b.txt

sabya@SABYA-PC d:/merge_temp/test/case2 (XBranch)
$ git rm src/a.txt
rm 'src/a.txt'

sabya@SABYA-PC d:/merge_temp/test/case2 (XBranch)
$ git add src/b.txt

sabya@SABYA-PC d:/merge_temp/test/case2 (XBranch)
$ git commit
[XBranch b3ff8fa] changed a.txt to b.txt in XBranch
 1 files changed, 0 insertions(+), 0 deletions(-)
 rename src/{a.txt => b.txt} (100%)

sabya@SABYA-PC d:/merge_temp/test/case2 (XBranch)
$ git checkout master
Switched to branch 'master'

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ mv src/a.txt src/b.txt

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ git rm src/a.txt
rm 'src/a.txt'

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ git commit
[master bfeaecb] removed src/a.txt
 1 files changed, 0 insertions(+), 1 deletions(-)
 delete mode 100644 src/a.txt

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ vi src/b.txt

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ cat src/b.txt
New Content

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ git add src/b.txt

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ git commit
[master 2361d5e] changed content of b.txt
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 src/b.txt

sabya@SABYA-PC d:/merge_temp/test/case2 (master)
$ git merge XBranch
CONFLICT (rename/delete): Rename src/a.txt->src/b.txt in XBranch and deleted in HEAD
Automatic merge failed; fix conflicts and then commit the result.

sabya@SABYA-PC d:/merge_temp/test/case2 (master|MERGING)
$ cat src/b.txt
Old Content
4

1 に答える 1

6

競合がありますが、ファイルの内容についてではありません。ツリーの内容について です。

  • 1 つのツリー、case2ディレクトリ (マスター) には、新しいファイルがあります。b.txt
  • 同じディレクトリ (XBranch 内) に、名前が変更されたファイルがありますa.txt => b.txt

競合を解決するときは、事実上、(ファイル内の 1 行ではなく) どちらかのファイルを選択していることになります。したがって、結果のファイルには「古いコンテンツ」が含まれます。


OPはコメントに追加します:

しかし、それは次の状況とどのように論理的に異なるのでしょうか。

  1. 「古いコンテンツ」でファイルを追加してコミットしa.txtます。master
  2. 「新しいコンテンツ」でファイルを追加してコミットしa.txtます。XBranch
  3. にマージXBranchmasterます。今回はそのファイルの両方の内容を表示しています!

今回は、両方のツリー (ブランチmasterおよびの case2 ディレクトリ) が新しいファイルをXBranch参照します。その内容は、競合の解決とともにマージされます。 以前は、 (名前が変更された) と新しいb.txtの間に競合がありました。両方のファイルが同じブランチに存在することはできず、(ファイルの内容ではなくファイルの) 選択を行う必要がありました。a.txta.txtb.txt

私の質問のステップ 4 で、" git rm" と " git add" を 1 回のコミットで実行すると、期待どおりに動作します。私は今それを理解できません。ファイルに両方のコンテンツが含まれる時期を予測するにはどうすればよいですか? のコンテンツだけを持つのはいつでXBranch、いつのコンテンツだけを持つのmasterですか?

つまり、次のことを意味します。

  • ステップ6(ツリーの競合)からの新しいものでコミットするためにXBrancha.txt名前を変更してb.txt)マージする代わりに、masterb.txt
  • 新しいステップ 4 の masterとマージXBranch(a.txtの名前に変更) ( の名前にも変更):同じツリー コンテンツですが、異なる BLOB コンテンツ: 行の競合。b.txta.txt b.txt

そうは言っても、OPはまだバグがあるに違いないと考えています:


注: Git 2.18 (2018 年第 2 四半期) では、再帰的なマージで競合検出が報告されるように変更されています。Elijah Newren ( )によるcommit 6e7e027 (2018 年 4 月 19 日)
を参照してください。 newren

merge-recursive: ディレクトリの名前変更による誤った名前変更/名前変更の競合を回避します

履歴の一方のファイルの名前が変更され、もう一方の側で変更されただけの場合、変更された側にディレクトリの名前変更を適用すると、rename/rename(1to2)競合が発生します。
ディレクトリの名前変更は、追加または名前変更のいずれかを表すペアにのみ適用する必要があります。

この変更を行うと、 以前は競合として報告されていたディレクトリ名変更テストケースが、競合rename/deleteとして報告されるようになります modify/delete


バイナリ ファイルが変更され、履歴の両側で別の場所に名前が変更されると、両方のファイルが作業ツリーに書き込まれますが、どちらも " ours" の内容を保持します。

これは Git 2.27 (2020 年第 2 四半期) で修正され、各側からのパスが元のコンテンツを取得するようになりました。

Elijah Newren ( )によるcommit 95983da (2020 年 5 月 13 日)を参照してください。( 2020 年 5 月 20 日コミット abbd1d9Junio C Hamanoによってマージされました)newren
gitster

merge-recursive: バイナリを使用する作業ツリーの rename/rename(1to2) を修正

報告者: Chunlin Zhang
署名者: Elijah Newren

rename/rename(1to2) 競合により、ファイル コンテンツの 3 方向のマージを試みて、両方のパスの作業ツリーに正しいコンテンツを配置できるようにします。

ただし、ファイルがバイナリの場合、コンテンツのマージは不可能であり、各パスでファイルの元のバージョンを使用する必要があります。

于 2010-06-23T10:41:43.833 に答える