当执行git diff时,"deleted file mode"是什么意思?

3

我已经获取了源代码,并在执行git diff origin/master命令时看到以下内容:

diff --git a/flash/controllers b/flash/controllers
deleted file mode 120000
index 350cadb..0000000
--- a/flash/controllers
+++ /dev/null
@@ -1 +0,0 @@
-/home/testapp/www/homedir/flash/controllers
\ No newline at end of file

我对这个的意思感到困惑。这是否意味着如果我执行合并,文件将被删除?这很令人困惑,因为我在gitignore中有/flash/controllers/


没错。文件已被删除,但不用担心,因为如果您恢复到旧版本,该文件将会出现。 - drum
1
我该如何避免在合并时发生删除? - StarTrek18
1个回答

7

简短版:虽然下面的内容都很重要,但查看合并的最简单方法是进行合并。只需确保一切干净(例如,git status没有需要检查的内容),然后运行合并并查看其结果。如果不喜欢结果,则可以使用以下命令中的一个中止合并(如果失败并需要帮助)或撤消合并(如果成功但您不喜欢结果):

git merge --abort  # after merge fails and asks you for help

或者:

git reset --hard HEAD^  # after merge succeeds but you don't like the result

“Deleted”表示在比较两个文件树时,“左侧”比较的树中有某些内容,而“右侧”比较的树不再有该内容。
“模式”标识文件对象的类型,“120000”具体指“符号链接”。因此,这意味着在将版本“a”与版本“b”进行比较时,在版本“a”中曾经存在名为“flash/controllers”的符号链接,在版本“b”中已不再存在。
如果你只运行git diff origin/master,左侧的树(tree a)是您当前的工作目录(不是特定的提交!),右侧的树(tree b)是由origin/master标识的提交中的树(使用git rev-parse origin/master查看此提交的实际SHA-1)。如果您命名两个提交,例如git diff master origin/master,则左侧的树是由第一个SHA-1命名的树(再次使用git rev-parse查看原始SHA-1),右侧的树是由第二个SHA-1命名的树。
“这是否意味着如果我合并后文件会被删除?”
也许;也可能不会。确定一个文件是否会在合并时被删除的因素是合并基础和被合并项之间的差异。
假设,举例来说,上面的a代表您当前分支末端(通常命名为master),而b代表您的名为origin/master的分支末端(这是一个“远程分支”,但实际上它是您自己的存储库中的一个分支;您只是从origin复制了它)。

我们不知道masterorigin/master之间的合并基础。那将是某个提交 - 我们只是不知道是哪一个。

假设如果我们将提交图绘制出来,看起来像这样:

...-o-o-B-u   <-- HEAD=master
         \
          t   <-- origin/master

在这里,您在您的master分支上做了一个提交。 我将此标记为u,表示我们。 同时,“他们”(无论他们是谁)在他们的分支上进行了一次提交,他们称之为master,而您将其称为origin/master; 我将此提交标记为t,表示他们。 共同的起点 - 您和他们分歧的地方 - 是我在这里标记为B的提交。
您可以使用git merge-base命令找到此提交的SHA-1 ID:
git merge-base HEAD origin/master

如果你这样做:
git diff -M --name-status $(git merge-base HEAD origin/master) origin/master

假定你使用类 Unix 的命令行(例如 shbash),1 git 会告诉你在他们的提交 t 中添加、修改、删除和重命名的文件。也就是说,它比较了提交 Borigin/master 的尾端提交 t
如果你将提交 B 与当前分支的尾端提交(即 HEAD 提交)进行比较,你会看到在你的提交 u 中所做的更改。
然后你可以执行以下操作:
git merge origin/master

Git会尝试合并“你所做的”和“他们所做的”。以下是符号链接(或任何文件)发生的关键,一般有三种可能性,但只有两种是可能的:

  • 如果符号链接在B中存在,则您可能什么也没做,他们删除了它。在这种情况下,git merge将删除它:您什么也没做,他们删除了它,将它们合并很容易:删除它。

  • 如果符号链接在B中不存在,则您添加了它,他们可能什么也没做。在这种情况下,git merge将添加(保留)它。同样,这很容易组合:您添加了,他们什么也没做,两者的组合是“添加”(或者说是“保留”)。

  • 如果符号链接在B中存在,但您更改了它,而他们删除了它,git merge将声明冲突并让您自己解决问题。这对于git来说不容易组合,就像如果您和他们都以不同的方式更改了它一样,它不知道该怎么做。2

(我们知道,或者至少根据原始的git diff输出相信,与提交HEAD相关联的树具有符号链接,而与提交origin/master相关联的树没有,因此我们不必担心“两者都创建”或“两者都修改”的情况。)

这很令人困惑,因为我在gitignore中有/flash/controllers/

但是那是带有尾随斜杠的flash/controllers/(前导斜杠无论如何都不重要,我认为——尽管忽略规则有时仍会让我感到困惑)。gitignore文档包括以下内容:

   •   If the pattern ends with a slash, it is removed for the purpose of
       the following description, but it would only find a match with a
       directory. In other words, foo/ will match a directory foo and
       paths underneath it, but will not match a regular file or a
       symbolic link foo (this is consistent with the way how pathspec
       works in general in Git).
(还有一个问题,如果一个文件已经被追踪,将其添加到.gitignore中并不会使其成为未跟踪的。 .gitignore内容仅用于防止文件意外地被跟踪,并避免git status抱怨它们。)

1事实上,针对合并基准的差异具有特殊的git diff语法,重新利用了gitrevisions中的三个点A...B语法。要找到AB之间的合并基准并将其与提交B的内容进行比较,可以编写git diff A...B。一如既往,省略一个端点等同于指定HEAD,因此git diff ...origin/master将合并基准(HEADorigin/master的)与origin/master进行比较;而git diff origin/master...将合并基准(相同的两个提交)与HEAD进行比较。添加-M以检测重命名,你就得到了git merge使用的比较。

(我在这里故意省略了一些有关“递归定义”的虚拟合并基础的额外细节。如果存在两个“同样有效”的合并基础祖先,git diff将任意选择一个,但使用recursive策略的git merge将构建一个虚拟合并基础。这会变得有点复杂。)

2对于大多数常规文件,如果您和他们都修改了它们,则git会尝试通过决定哪些更改是独立的(git保留每个这样更改的副本)以及哪些更改是相同的(git也保留那些更改的一个副本,而不是放入两个副本)。git是否实际上正确地判断哪些更改是“相同的”和哪些更改是“不同的”——这是另一回事。它通常做得很好,但它只是一个计算机算法,有时会出错,因此您作为用户必须以某种方式检查结果(例如,“它仍然构建,它是否通过测试,它看起来还好”,您拥有的任何测试),以确保真正确定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接