微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

`git add` 如何处理文件<->目录之类的变化? 1.用目录替换文件2.用文件替换目录3.索引文件不一致

如何解决`git add` 如何处理文件<->目录之类的变化? 1.用目录替换文件2.用文件替换目录3.索引文件不一致

这是一个很长的问题。我正在尝试对一些基本的 Git 功能进行逆向工程,但在理解 git add 真正在幕后所做的事情时遇到了一些麻烦。我已经熟悉 Git 的三棵树,而且索引文件并不是真正的树,而是树的排序数组表示。

我原来的假设是这样的:当运行git add <pathspec>时,

  • 如果工作目录中存在 <pathspec>
    1. 根据工作目录中的状态创建一个索引文件
    2. 用这个(子)索引覆盖索引文件的相关部分。
  • 如果 <pathspec> 只存在于当前索引文件中:
    1. 这意味着已在工作目录中删除,所以...
    2. 删除对应的索引文件的相关部分。
  • 如果工作目录或索引文件中不存在 <pathspec>
    1. fatal: pathspec <...> did not match any files

这个假设反映了“做你被告知要做的事情”git add,它只查看路径并将更改在此路径下注册到索引文件。在大多数情况下,这就是实际 git add 的工作方式。

但有些情况似乎不太简单:

1.用目录替换文件

git init

touch somefile
git add . && git commit

rm somefile
mkdir somefile && touch somefile/file

此时,索引文件仅包含我刚刚删除somefile 文件的单个条目,正如预期的那样。现在我执行git add我有两种方法可以做到这一点:git add somefilegit add somefile/file。 (显然我在这里排除了琐碎的 git add .

我的预期:

  • git add somefile:相当于 git add . - 删除旧条目并添加新条目
  • git add somefile/file:只为新的 somefile/file 添加索引条目。

实际发生的情况: 上述任何一个命令都直接导致 somefile/file 具有单个索引条目的最终状态 - 即,两者都等效于 git add .

在这里,感觉 git add 不是您直接的“按照您的吩咐去做”的命令。 git add somefile/file 似乎在提供的路径内部和周围窥视,意识到 somefile 不再存在并自动删除索引条目。

2.用文件替换目录

git init

mkdir somefile && touch somefile/file
git add . && git commit

rm -r somefile && touch somefile

此时,索引文件按预期包含旧 somefile/file 的单个条目。同样,我在相同的两个变体中执行 git add

我的预期:

  • git add somefile/file:通常,删除somefile/file 的条目。但如果它四处查看,它还应该为 somefile 添加新条目。
  • git add somefile:相当于 git add .

实际发生的事情:

  • git add somefile/file:导致一个空的索引文件 - 所以,它做了我通常期望它做的事情!
  • git add somefile:相当于 git add .,如预期

此处,git add 的行为类似于“按照您的要求执行操作”命令。它只选取路径并用工作目录反映的内容覆盖索引文件的适当部分。 git add somefile/file 不会四处游荡,因此不会自动somefile 添加索引条目。

3.索引文件不一致

到目前为止,一个可能的理论可能是 git add 试图避免索引文件不一致的情况 - 即不代表有效工作树的索引文件。但额外一层嵌套会导致这种情况。

git init

touch file1
git add . && git commit

rm file1 && mkdir file1 && mkdir file1/subdir
touch file1/subdir/something
git add file1/subdir/something

这和情况1类似,只是这里的目录多了一层嵌套。此时,索引文件如预期的那样仅包含旧 file1 的条目。同样,现在我们运行 git add,但具有三个变体:git add file1git add file1/subdirgit add file1/subdir/something

我的预期:

  • git add file1:相当于 git add .,导致 file1/subdir/something 的单个索引条目。
  • git add file1/subdirgit add file1/subdir/something:通常,应该只为 file1/subdir/something 添加一个条目(导致索引文件不一致)。但是如果上面的“无不一致索引”理论是正确的,这些也应该删除旧的file1索引条目,因此相当于git add .

实际发生的事情:

  • git add file1:按预期工作,相当于 git add .
  • git add file1/subdirgit add file1/subdir/something:只为 file1/subdir/something 添加单个条目,导致无法提交的不一致索引文件

我所指的不一致索引文件是:

100644 <object addr> 0  file1
100644 <object addr> 0  file1/subdir/something

因此,仅添加另一层嵌套似乎可以阻止 git add 像在案例 1 中那样四处窥视!请注意,提供给 git add 的路径也无关紧要 - file1/subdirfile1/subdir/something 都会导致索引文件不一致。

以上案例描绘了一个非常复杂的 git add 实现。我在这里遗漏了什么,还是 git add 真的不像看起来那么简单?

解决方法

实际上,这只是意味着您在(至少某些版本的)Git 中发现了一个错误。

Git 明白操作系统不能支持两个实体,一个是文件,另一个是同名的目录/文件夹。也就是说,我们不能同时让 file1 作为 file file1 作为 目录1

现在,Git 索引的问题在于它根本无法在其中保存目录。2 唯一允许的实体是文件。因此,要么 file1 存在,要么 file1/subdir/something 存在,但永远不会同时存在。 Git内部有一堆相当复杂的代码,用于索引本身以及在git checkoutgit reset等期间处理操作系统级文件,这是假设 处理“D/F”(目录/文件)冲突。 Git 需要在执行 git checkout 的提交时处理这些问题,其中 somefile 是一个文件,然后是不同提交的 git checkout,其中 somefile/file 是一个文件,所以 {必须删除 {1}} 并插入一个目录。它需要能够处理我们回到第一种情况的结账,因此必须删除somefile,然后必须将somefile/file rmdir-ed,然后才能创建somefile/作为文件。并且,它必须处理合并,其中 somefile 是三个提交中的一个或两个中的文件,但 somefile 存在于其他两个或一个提交中。

显然,有人错过了一个角落案例。我能够使用您的步骤自己重现此内容,并且:

somefile/file

这种状态不应该存在。添加 $ git ls-files --stage 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 file1 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 file1/subdir/something $ git write-tree You have both file1 and file1/subdir/something fatal: git-write-tree: error building trees -as-a-directory 擦除包含 file1 的索引槽:

file1

因为这会触发删除现在不需要的条目的代码。

(很明显,这需要一个修复程序和一个测试套件测试用例。幸运的是,Git 在构建树的过程中会自我检测到错误的情况,因此它不会进行错误的提交。)


1我认为也许我们应该能够做到这一点,但目前POSIX规则禁止这样做,并且没有任何类Unix文件系统支持它。它也会让 $ git add file1 $ git ls-files --stage 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 file1/subdir/something 之类的归档器变得一团糟。

2严格来说这并不完全正确:出于各种加速目的,索引包含“不规则”(非缓存)条目以及描述提议的下一次提交的正常缓存条目。它是不保存目录存在的缓存条目;未提交的条目可以包含各种辅助信息。但这些都没有被 tar 显示。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?