git 重命名/删除混淆

发布于 2024-09-06 20:25:35 字数 3303 浏览 0 评论 0 原文

我对特定的 git 行为有一个困惑:

以下是步骤和情况(稍后还会给出命令列表):

  1. 我有两个分支: master 和 XBranch
  2. 它们都有一个文件 src/a.txt 。它的内容是“旧内容”
  3. 在 XBranch 中我将 src/a.txt 重命名为 src/b.txt,使用:mvgit rmgit add< /代码>。
  4. 在 master 中将文件重命名为 a.txt。在提交期间,我执行了 git rm src/a.txt 但忘记执行 git add src/b.txt 在 master 中,我执行以下操作: git rm src/a.txtgit commit

  5. 在 master 中,我将文件 b.txt 的内容编辑为“新内容

  6. 在master中我执行git add src/b.txt 和 git commit
  7. 在 master 中我这样做: git merge XBranch

文件 src/b.txt 冲突,这是完全可以理解的。 但内容是“旧内容”。为什么?

为什么是这样的:

<<<<<<< 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

I having one confusion about a particular git behavior:

Following are the steps and situation (the list of commands are also given later):

  1. I have two branches: master and XBranch
  2. There is a file src/a.txt in both of them. It's content is "Old Content"
  3. In XBranch I rename src/a.txt to src/b.txt, using: mv, git rm, git add.
  4. In master rename the file a.txt. During commit I did git rm src/a.txt but forgot to do git add src/b.txt
    In master I do: git rm src/a.txt and git commit

  5. In master, I edit the content of the file b.txt to "New Content

  6. In master I do git add src/b.txt and git commit
  7. In master I do: git merge XBranch

The file src/b.txt conflicts, which is perfectly understandable.
But the content is "Old Content". Why?

Why not is it something like:

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

List of Commands:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

滥情稳全场 2024-09-13 20:25:35

存在冲突,但与文件内容无关。这是关于
内容。

  • 有一个文件 b.txt
  • 在一棵树中,case2 目录(在 master 中),同一目录(在 XBranch 中)中 有一个重命名文件a.txt => b.txt

当您解决冲突时,您实际上选择了一个文件或
其他(文件中没有一行)。因此,结果中的“旧内容”
文件。


OP 在评论中添加:

但是它与以下情况在逻辑上有何不同:

  1. 我在 master 中添加了一个包含“旧内容”的文件 a.txt 并提交。
  2. 我在 XBranch 中添加了一个带有“新内容”的文件 a.txt 并提交。
  3. 我将 XBranch 合并到 master 中。
    这次它显示了该文件中的两个内容!

这次,两棵树(分支 masterXBranch 中的 case2 目录)
引用文件a.txt:其内容被合并,但存在冲突
解决。之前,a.txt之间存在冲突
重命名b.txt)和 b.txt:这两个文件不能存在于
同一分支,必须做出选择(文件,而不是文件内容)。

在我的问题的第 4 步中,如果我在单个中执行“git rm”和“git add
提交,它按我的预期工作!我现在无法理解这一点。
如何预测文件何时将包含这两种内容?
什么时候它只有 XBranch 的内容以及什么时候它只有
master 的内容?

这意味着:

  • 不要将 XBrancha.txt 重命名为 b.txt)合并到 master 提交
    使用步骤 6 中的新 b.txt(树冲​​突),
  • 您可以合并 XBrancha.txt 重命名为 b .txt)与新的master
    步骤 4(a.txt 也重命名b.txt):相同的树内容,但不同
    blob内容
    :行冲突。

话虽这么说,OP仍然认为一定有一个错误:


注意:Git 2.18(2018 年第 2 季度)更改了合并递归的冲突检测报告。
请参阅 提交 6e7e027(2018 年 4 月 19 日),作者:伊利亚·纽伦 (newren)

merge-recursive:避免目录重命名中的虚假重命名/重命名冲突

如果历史记录一侧的文件被重命名,并且仅在另一侧进行修改
另一侧,然后将目录重命名应用到修改后的一侧给我们
重命名/重命名(1to2)冲突。
我们应该只将目录重命名应用于表示添加或重命名的对。

进行此更改意味着目录重命名测试用例
之前报告为重命名/删除冲突,现在将报告为
修改/删除冲突


当二进制文件在历史记录两侧被修改并重命名到不同位置时,两个文件都将写入工作树,但都将具有“ours”中的内容。

Git 2.27(2020 年第 2 季度)已更正此问题,以便各方的路径都能获取其原始内容。

请参阅 提交 95983da(2020 年 5 月 13 日),作者:伊利亚·纽伦 (newren)
(由 Junio C Hamano -- gitster -- 合并于 提交 abbd1d9,2020 年 5 月 20 日)

合并递归:修复重命名/重命名(1to2) 用于具有二进制的工作树

报告人:张春林
签字人:伊利亚·纽伦

对于重命名/重命名(1to2)冲突,我们尝试对文件内容进行三向合并,以便可以将正确的内容放置在两个路径的工作树中。

但是,如果文件是二进制文件,则不可能进行内容合并,我们应该在每个路径上使用文件的原始版本。

There is a conflict, but not about the file content. It is about the
tree content.

  • In one tree, the case2 directory (in master), there is a new file b.txt
  • in the same directory (in XBranch), there is a renamed file a.txt => b.txt

When you are resolving the conflict, you are in effect choosing one file or the
other (not one line within the file). Hence the "Old Content" in the resulting
file.


The OP adds in the comments:

But then how logically is it different from the following situation:

  1. I add a file a.txt in master with "Old Content" and commit it.
  2. I add a file a.txt in XBranch with "New Content" and commit it.
  3. I merge XBranch into master.
    This time it is showing both contents in that file!

This time, both trees (the case2 directory in branches master and XBranch)
reference a new file a.txt: its content get merged, with conflict
resolution. Before, there was a conflict between a a.txt
(renamed as b.txt) and a new b.txt: both files cannot exist in the
same branch, a choice (of file, not of file content) had to be made.

In step 4 of my question, if I do "git rm" and "git add" in a single
commit, it works as I expect! I fail to understand that now.
How can I predict when the file will have both contents?
When it will just have the content of XBranch and when will it have just the
content of master?

That means that:

  • instead of merging XBranch (a.txt renamed as b.txt) to master commit
    with a new b.txt from step 6 (conflict of tree),
  • you would merge XBranch (a.txt renamed as b.txt) with master from new
    step 4 (a.txt also renamed as b.txt): same tree content, but different
    blob content
    : conflict of lines.

That being said, the OP still thinks there must be a bug:


Note: Git 2.18 (Q2 2018) changes that conflict detection report with a merge recursive.
See commit 6e7e027 (19 Apr 2018) by Elijah Newren (newren).

merge-recursive: avoid spurious rename/rename conflict from dir renames

If a file on one side of history was renamed, and merely modified on the
other side, then applying a directory rename to the modified side gives us
a rename/rename(1to2) conflict.
We should only apply directory renames to pairs representing either adds or renames.

Making this change means that a directory rename testcase that was
previously reported as a rename/delete conflict will now be reported as a
modify/delete conflict
.


When a binary file gets modified and renamed on both sides of history to different locations, both files would be written to the working tree but both would have the contents from "ours".

This has been corrected with Git 2.27 (Q2 2020), so that the path from each side gets their original content.

See commit 95983da (13 May 2020) by Elijah Newren (newren).
(Merged by Junio C Hamano -- gitster -- in commit abbd1d9, 20 May 2020)

merge-recursive: fix rename/rename(1to2) for working tree with a binary

Reported-by: Chunlin Zhang
Signed-off-by: Elijah Newren

With a rename/rename(1to2) conflict, we attempt to do a three-way merge of the file contents, so that the correct contents can be placed in the working tree at both paths.

If the file is a binary, however, no content merging is possible and we should just use the original version of the file at each of the paths.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文