文件消失/重新出现在git中

时间:2014-06-16 07:27:43

标签: git

我参与了下面提到的git会话。如您所见,当我返回主分支的HEAD时,git报告文件y.y已被删除。这已通过ls确认。但是当我检查出之前的提交并返回master时,文件又重新出现了。

我是git的初学者,但我不能理解为什么git checkout master只能通过只读命令分开时会给出两个不同的结果。

rwilson@855:~/ht$ git log --oneline
f065234 add y.y
04a7340 2nd commit
b6ca522 init commit
rwilson@855:~/ht$ git checkout master
D   y.y
Switched to branch 'master'
rwilson@855:~/ht$ ls
x.x  x.y
rwilson@855:~/ht$ git checkout HEAD~1
Note: checking out 'HEAD~1'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at 04a7340... 2nd commit
rwilson@855:~/ht$ ls
x.x  x.y
rwilson@855:~/ht$ git checkout HEAD~1
Previous HEAD position was 04a7340... 2nd commit
HEAD is now at b6ca522... init commit
rwilson@855:~/ht$ ls
x.x
rwilson@855:~/ht$ git checkout master
Previous HEAD position was b6ca522... init commit
Switched to branch 'master'
rwilson@855:~/ht$ ls
x.x  x.y  y.y

2 个答案:

答案 0 :(得分:2)

我猜了一下,但最初我相信你已经离开了#HEAD"提交f065234上的模式,即分支名称master指向的提交。因此git log --oneline向您展示了提交及其两个祖先。

无论出于何种原因,在提交时,您(或您做过的事情)都删除了文件y.y

首先,让我们进入那个状态。

$ cd /tmp; mkdir repo; cd repo
$ git init
Initialized empty Git repository in /tmp/repo/.git/
$ echo 'first file x.x' > x.x
$ git add x.x
$ git commit -m 'init commit'
[master (root-commit) 63ddf00] init commit
 1 file changed, 1 insertion(+)
 create mode 100644 x.x
$ echo 'second file x.y' > x.y
$ git add x.y
$ git commit -m '2nd commit'
[master 62cb693] 2nd commit
 1 file changed, 1 insertion(+)
 create mode 100644 x.y
$ echo 'third file y.y' > y.y
$ git add y.y
$ git commit -m 'add y.y'
[master b4c61d1] add y.y
 1 file changed, 1 insertion(+)
 create mode 100644 y.y
$ 

现在我们只需要"分离HEAD":

$ git checkout --detach master
Note: checking out 'master'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at b4c61d1... add y.y
$ 

接下来,我们删除y.y,并要求git切换到分支master。我们会像您一样看到文件D的{​​{1}}州:

y.y

我做了"分离HEAD"上面的步骤是,如果我没有,我会得到略有不同的输出。我现在可以通过重复$ rm y.y $ git checkout master D y.y Switched to branch 'master' $ 命令来告诉你:

git checkout master

在这两种情况下,这里的问题是git已经处于你要求的提交中。因此,它根本不必触及工作目录 - 所以它没有。

但还有其他的事情!实际上,这是我的$ git checkout master D y.y Already on 'master' $ 命令。我删除了该文件。 Git可以看到该文件丢失,因此它使用rm y.y行宣布了这一点。

接下来,您要求git检查以前的提交(D又名HEAD~1,即您的存储库中的提交master~1 - 我的是04a7340,如下所示上面的62cb693输出,因为我不是你,我的文件可能包含不同的数据,依此类推:我的存储库是不同的,这使得我的所有SHA-1都不同。)

要切换到该提交,git必须删除文件git commit:该文件位于提交y.y而不是提交f065234。该文件已经丢失,这使得git的工作变得非常简单:它"删除"无效的不存在的文件。

从这一点开始,如果你要求git签出提交04a7340 - 即提交master - git必须将文件f065234 放入你的工作目录。你通过一些额外的步骤完成了这项工作(首先检查y.y,但也缺少b6ca522,然后只检查y.y)但是你到了那里,这意味着git重新提取{来自底层存储库的{1}},将其放在工作目录中。


注意:请注意,f065234具有不同的使用模式,轻松可以擦除工作。此模式拼写为y.y文档,例如git checkout。不幸的是,它(在我看来)太容易意外地调用这种其他使用模式。下一部分仅约为git checkout commit -- path,而不是git checkout HEAD -- x.x。在此之后,我会插入一个关于 git checkout commit 版本的简短部分。


作为一般规则,无论你提交的是SHA-1,无论是分离的,还是通过分支名称 - 当你要求它检查另一个提交时git会做什么就是看看需要添加哪些文件通过比较两个提交,在工作目录中删除或修改。 (如果两个提交相同,结果很简单:没有文件需要更改。请仔细考虑下面的第一条评论:如果您已经在git checkout commit -- path和{{1}没有文件需要更改,因此path只需1234567。)

任何的工作目录文件都需要进行任何更改,git会单独留下。

对于任何需要以某种方式更改 的文件,git会检查您是否可能失去工作。失去工作的一些明显方法包括这些(还有更多,但我只列出这些):

  • git必须删除该文件,但您已对其进行了编辑(它与当前提交不匹配,更不用说目标提交了,即删除它)。
  • git必须使用其他版本替换该文件,但您已对其进行了编辑。
  • git必须创建文件(它在目标提交中但不是当前提交),但是你自己创建了它,其内容与git不同。

在这些情况下,git checkout 1234567会因错误而停止,除非您提供git checkout标记。

但是,在许多情况下,许多工作目录文件不需要更改。例如,在上文中,我可以从git status切换到git checkout--force而不更改文件master:它在所有三个版本中都是相同的

这意味着git将允许我修改 master~1,甚至完全删除它,并且还会检查其他一些提交:从master~2切换到{{1} }和/或返回不需要对x.x进行任何更改。如果我执行这些检出,git会为任何此类文件打印单行状态消息,其中字母x.x表示工作树中缺少该字符,或master表示它&# 39;在工作树中修改(关于现在检出的提交或分支)。

(不管这是不是最初的设计,人们已经开始喜欢它了。它可以让你开始更改一堆文件,然后意识到你应该在另一个分支上工作。所以你master~1其他分支。只要你的所有更改都不需要被销毁来切换,git切换,保持你的更改;所以现在这些更改已经准备好进入另一个分支。)


以上所有内容都在讨论x.x当你要求它检查特定提交("分离的HEAD"模式)或分支("非分离&#34)时的行为方式)。在此模式下,您只使用一个附加参数调用D,该参数应该是分支名称或其他提交说明符:Mgit checkout,依此类推。

无论出于何种原因,git 使用git checkout命令从提交中提取特定文件(通过索引/登台区域编写提取)。要调用此模式,您应该使用文字双短划线git checkout分隔git checkout HEAD^两个或更多参数:

git checkout master

这告诉git checkout git checkout左边的东西是提交说明符 - 即在存储库中查找的位置 - 右边的东西是文件路径,它应该丢弃你在右边命名的路径上正在进行的任何工作,用从左边的提交中提取的版本替换它们。

换句话说,如果您已开始编辑--并决定要对其进行编辑",则使其看起来像{{1}中所做的那样提交,您将git checkout HEAD -- x.x x.y 的HEAD版本签出到文件git checkout中。或者,如果您希望将--x.x版本的HEAD放入工作目录并暂存提交,则可以x.x

这个表格显然比x.x(试图避免破坏你的工作)更危险。很遗憾,HEAD^ HEAD~1部分。此外,如果您将其保留,则提交说明符默认为x.x。所以:

git checkout HEAD^ -- x.x

表示"请破坏我对文件git checkout commit所做的更改,同样如下:

git checkout

因为--不是提交或分支的名称。同样地:

HEAD

命名路径,但这次路径是"当前目录",所以这些clobbers都可以递归地处理当前目录中的所有文件或其下面的任何文件!

(如果git对&#34使用了不同的命令,那么可能会很好;检查特定文件" vs"检查给定的commit-ID",使得更难以意外地破坏工作。)


你没有问过,但我也会在这里提到这一点:"分离的HEAD"和"在一个分支上"其实很荒谬简单。窥视git checkout -- x.x 目录;您将找到名为x.x的文件。当您进入git checkout x.x 等分支时,x.x的内容为git checkout . 。当您分离时,内容是原始SHA-1,如.git(git通常会显示缩写版本)。简而言之:当你在分支上时,git从分支名称获取原始SHA-1,分支名称在HEAD文件中;当您分离时,git将原始SHA-1直接存储在master文件中。

还有一个关键区别,适用于您创建提交时。如果HEAD具有原始SHA-1,则git会将新提交的SHA-1写入ref: refs/heads/master。如果b4c61d1692f750607a821aa53788e3a7ce5d1199改为HEAD,git会将新提交的SHA-1写入分支映射文件,文件HEAD保持不变。为了提高效率,分支映射文件有时会有所不同,尽管当前 new 提交始终会创建或更新名为HEAD的文件。

答案 1 :(得分:1)

那是因为:

  • 当您签出HEAD~1时,y.y就在那里。
  • 但当您再次签出master HEAD时,y.y现在被视为HEAD中的未跟踪文件:
    • 因为y.y位于工作树中(因为HEAD~
    • 并且由于master HEAD在更新所述工作树时不必触摸y.y

这意味着在y.y的第二个结帐时HEAD仍然存在。

在第二个git checkout master之后

A git clean可以提供帮助。