git stash恢复已删除和重命名文件的索引状态

时间:2018-01-25 15:48:17

标签: git git-stash

当存储暂存的已删除或重命名的文件,然后将其取消存储时,它们将以已删除和未删除的状态进行恢复。

在以下示例状态中:

$ git status s

A  file0
D  file1
R  file2 -> file3
?? file4

运行git stash push -k -u然后git stash pop --index会让我处于以下状态:

$ git status s

A  file0
D  file1
R  file2 -> file3
?? file1
?? file2
?? file4

我希望最终处于原始状态,而pop之后未删除的文件会再次显示为未跟踪。

有什么方法吗?

编辑: 这是一个重新创建问题的脚本(在Mac OS X 10.13.2上使用git 2.16.1进行测试)

#!/usr/bin/env bash

echo -e "\nInitializing a fresh git dir..."
mkdir gitStashIssue && cd $_
rm -rf * .*
git init


echo -e "\nPreparing git state for test..."
# Create files and commit them
echo 'abc' > file1
echo 'aabbcc' > file2
echo 'aaabbbccc' > file3
echo 'aaaabbbbcccc' > file4
git add .
git commit -m 'initial commit'

# Make changes and add them to stage
echo `cat file1` >> file1
echo `cat file2` >> file2
git add .

# Make another change to a staged file without
# staging it, making it partially staged
echo `cat file1` >> file1

# Delete and rename files
git rm file3
git mv file4 fileRenamed

# Add untracked file
echo "untracked" > untrackedFile

# git status -s should now show
# MM file1
# M  file2
# D  file3
# R  file4 -> fileREnamed
# ?? untrackedFile

echo -e "\nCurrent git status is:"
git status -s

echo -e "\nStasing changes..."
git stash save -u -k

# git status -s should now show
# M  file1
# M  file2
# D  file3
# R  file4 -> fileREnamed
# ?? file3
# ?? file4

echo -e "\ngit status after stashing files is:"
git status -s

echo -e "\ncleaning up deleted and renamed files..."
git clean ./ -f

echo -e "\ngit status after cleanup:"
git status -s

echo -e "\nCommiting unstashed changes..."
git commit -m 'commit unstashed changes'

# This causes a conflict in file1
# git status -s should now show
# UU file1
# ?? untrackedFile
git stash pop --index

echo -e "\ngit status after unstashing:"
git status -s

2 个答案:

答案 0 :(得分:2)

请记住:git不会跟踪文件重命名。它跟踪文件内容。

当索引包含删除和具有相同内容的不同文件的创建时,git的重命名检测将(可能)得出结论是重命名,并有助于显示它。但这实际上并没有改变索引中的内容。 “真正”发生的确实是一个删除 - 然后创造,git只是试图更有帮助地展示它。

亲自尝试:

$ cp file1 boink
$ rm file1
$ git add .
$ git status

具有与以下相同的效果:

$ git mv file1 boink
$ git status

此重命名检测仅适用于已添加到索引的内容。现在做git reset你会明白我的意思:

$ git reset
$ git status
Changes not staged for commit:
        deleted:    file1

Untracked files:
        boink

我们可以使用git命令git ls-files列出索引中存在的文件。实际上,这些是git目前认为存在的文件。 当我们如上所述进行重命名时,其输出为:

# git ls-files
boink
file2
file3
file4

就索引而言,file1不再存在。我们删除了它,然后我们创建了boinkgit status很友好,向我们展示了重命名检测的结果,但请记住索引并不关心这一点。

现在我们运行git stash -k -u-k告诉stash不要触摸索引,所以它不会。暂时考虑stash的联机帮助页的第一段:

  

如果要记录工作目录和索引的当前状态,但想要返回干净的工作目录,请使用git stash。   该命令将保存您的本地修改并恢复工作目录以匹配HEAD提交。

所以:我们已经要求stash保存我们的本地修改(不触及索引),并恢复工作目录以匹配HEAD提交。 HEAD提交中存在file1,因此它会返回。但file1已不在索引中,因为我们删除了它。因此,file1现在是一个未跟踪的文件。由于我们没有触及索引,因此新文件boink以及它是file1重命名的结果的记录仍保留在索引中。因此,您可以从git status获得可能令人惊讶的输出:

Changes to be committed:    
        renamed:    file1 -> boink

Untracked files:
        file1

答案 1 :(得分:1)

您的脚本有输出。正如您所看到的,在卸载后没有file3file4。此文件显示在stash -k之后。这是正常行为因为stash恢复已删除的文件。并且-k使其保持索引更改,即从索引中删除file3。因此,您同时拥有D file3?? file3

Initializing a fresh git dir...
rm: refusing to remove '.' or '..' directory: skipping '.'
rm: refusing to remove '.' or '..' directory: skipping '..'
Initialized empty Git repository in /home/azzel/projects/test/bash/gitstash/gitStashIssue/.git/

Preparing git state for test...
[master (root-commit) 7e891f6] initial commit
 4 files changed, 4 insertions(+)
 create mode 100644 file1
 create mode 100644 file2
 create mode 100644 file3
 create mode 100644 file4
rm 'file3'

Current git status is:
MM file1
M  file2
D  file3
R  file4 -> fileRenamed
?? untrackedFile

Stasing changes...
Saved working directory and index state WIP on master: 7e891f6 initial commit

git status after stashing files is:
M  file1
M  file2
D  file3
R  file4 -> fileRenamed
?? file3
?? file4

cleaning up deleted and renamed files...
Removing file3
Removing file4

git status after cleanup:
M  file1
M  file2
D  file3
R  file4 -> fileRenamed

Commiting unstashed changes...
[master 1156712] commit unstashed changes
 4 files changed, 2 insertions(+), 1 deletion(-)
 delete mode 100644 file3
 rename file4 => fileRenamed (100%)
Auto-merging file1
CONFLICT (content): Merge conflict in file1
Recorded preimage for 'file1'
Index was not unstashed.

git status after unstashing:
UU file1
?? untrackedFile