Git:git如何决定在结账时要删除哪些文件?

时间:2014-03-06 19:18:37

标签: git git-checkout

我试图了解Git的一个问题是它如何处理未跟踪的文件,或跟踪但现在已提交的文件。

以下是一个例子:

echo "one" > one.txt
git add one.txt
git commit -m "#1"

所以,“one.txt”在第一次提交中。现在,我将再创建两个文件,但只添加一个(我将把下一个提交标记为“#2”,以便我们可以返回它):

echo "two" > two.txt
echo "three" > three.txt
git add two.txt
git commit -m "#2"
git tag "#2"

所以,“two.txt”在第二次提交中,“three.txt”就是这个额外的文件,闲逛。然后,我结账HEAD~,果然,Git删除了文件“two.txt”,因为当时不存在...

git checkout HEAD~
ls
___ one.txt three.txt

好的..回到分支的末尾,只有添加第三个​​文件。所以,我们只是跟踪它。 然后让我们向后退一个提交......

git checkout "#2"
git add three.txt
git checkout HEAD~
ls
___ one.txt three.txt

嗯......好吧......所以只是跟踪一个文件是不足以让Git管理它是否存在。所以,我们再次进入分支的末尾,然后提交,然后备份(两次,这次,回到提交#1),我们看到了什么?

git checkout "#2"
git commit -m "#3"
git checkout HEAD~~
ls
___ one.txt

这一次,Git删除了文件“three.txt”。所以,我的问题是:有人可以描述 Git决定如何做到这一点?它似乎不是简单的文件名是否出现在存储库中的任何树对象中,因为我可以创建一个 new three.txt(一个正在<的文件名) em>跟踪并在存储库中有一个已提交的版本,然后执行更多检查,Git再次像以前那样单独使用它。

有人可以解释一下Git在结账时如何决定删除可以做什么?

2 个答案:

答案 0 :(得分:0)

我没有提出我自己的解释,而是会引导你进入优秀的Pro Git book,这是免费的:)。这个链接是相关部分,并很好地解释了各个州。

你的解释涵盖了Git非常全面地关注文件的方式。忘记重置一分钟,Git会将更改应用到工作目录,将其从状态X转到状态Y(使用记录的变更集)。如果你有一个你没有提交的文件,或者自上次提交以来已经改变的文件,Git会将此视为稍后的更改,因此不会撤消未提交的工作,即使该文件不存在于先前的提交。

这是一个非常理智的策略,因为它可以降低失去未提交作品的风险。

如果您想要从工作目录中删除更改并返回干净状态(HEAD提交),您可以执行git checkout -- .或将.替换为您的文件/目录选择。

答案 1 :(得分:0)

理解这一点的一个关键是git add只是将文件放入索引中;执行git checkout比较&#34;来自&#34;的树。和&#34;到&#34; 提交(如果路径P存在于&#34;来自&#34;但不是&#34;来自&#34;路径P是理想的-removed);并且checkout通过索引写入工作目录(clobbering index-only changes)。另一个原则是&#34;不要覆盖或删除任何未提交的内容&#34;。

让我们讨论具体案例。你是&#34;在&#34;使用未跟踪的文件three.txt提交#2。然后你git add three.txt。它现在在索引中,但未提交,因此它(仍然)不在当前(HEAD)提交的树中,这是标记为#2的提交。

现在你要求git检查提交&#34;#1&#34;。比较树:HEAD解析&#34;#2&#34;,其中包含文件one.txttwo.txtHEAD~解析为&#34;#1&#34;,其中包含one.txttwo.txt。因此,转换工作树的方法是&#34;删除two.txt,如果one.txt不同,则替换它。&#34; (并且,删除需要删除索引条目以及工作树副本,而替换需要通过索引进行写入。)

&#34;确认任何工作都不会被破坏&#34;然后步骤必须检查:

  • two.txtone.txt的索引版本会发生什么?
  • 同样的工作树版本会发生什么?

虽然three.txt现在在索引中,但是没有必要删除它或更改索引版本,因此它现在只是保留在索引中,并准备好添加到新提交中#&#34; 34。

现在(HEAD指向#1),您要git checkout #2。 Git必须重复上述内容,但这次树比较结果是&#34;添加two.txt,可能会替换one.txt&#34;。 verify-no-clobbering步骤检查这些是否正常(它们是)并且git执行结帐。 three.txt 的索引条目仍为仍然为&#34;准备好添加到新提交&#34;。

一旦你执行了git commit -m '#3',git就会将索引写入树中(这比直接扫描工作目录要容易得多;实际上,索引是一种幻想,中间形式的中途在git repo树条目和工作树之间)并写一个新的提交,并将其作为树。

然后,git checkout解析为提交#3,然后从那里移动到提交#1,最后HEAD完成。比较树木,从#3到#1的变化是&#34;删除two.txt,删除three.txt,可能会替换one.txt&#34;。结帐必须验证不会破坏任何工作(它赢了),然后就可以了。

请注意,如果你在git&#39; s&#34;后面乱扔东西,可以通过更改HEAD点的提交而不改变索引(git symbolic-ref HEAD refs/heads/differentbranch或{{ 1}}例如),你可以得到一些有趣的效果。这就是我为一个例子所做的事情:

git update-ref HEAD HEAD~

$ git commit -m '#3' [detached HEAD e3465c4] #3 1 file changed, 1 insertion(+) create mode 100644 three.txt $ git tag #3 # since we're "detached", let's save #3 $ git status $ git status HEAD detached at #3 $ git update-ref HEAD '#2' $ git status HEAD detached from #3 Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: three.txt $ git update-ref HEAD HEAD^ $ git status HEAD detached from #3 Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: three.txt new file: two.txt 操作不会触及索引,但它会更改update-ref指向的提交(以及树)。因此,比较index和current-commit-tree会得到不同的结果。