2010-04-14 52 views
375

我读过renaming files in git时,你应该提交任何更改,执行重命名,然后重命名你的文件。 Git会从内容中识别文件,而不是将其视为新的未跟踪文件,并保留更改历史记录。在git中处理文件重命名

但是,今晚我只做了这件事,结果我回到了git mv

> $ git status 
# On branch master 
# Changes to be committed: 
# (use "git reset HEAD <file>..." to unstage) 
# 
# modified: index.html 
# 

iphone.css重命名我在Finder中样式表mobile.css

> $ git status 
# On branch master 
# Changes to be committed: 
# (use "git reset HEAD <file>..." to unstage) 
# 
# modified: index.html 
# 
# Changed but not updated: 
# (use "git add/rm <file>..." to update what will be committed) 
# (use "git checkout -- <file>..." to discard changes in working directory) 
# 
# deleted: css/iphone.css 
# 
# Untracked files: 
# (use "git add <file>..." to include in what will be committed) 
# 
# css/mobile.css 

所以现在的Git认为我已经删除了一个CSS文件,并添加了一个新的。不是我想要的,让我们撤销重命名并让git完成这项工作。

> $ git reset HEAD . 
Unstaged changes after reset: 
M css/iphone.css 
M index.html 

回到我开始的地方。

> $ git status 
# On branch master 
# Changes to be committed: 
# (use "git reset HEAD <file>..." to unstage) 
# 
# modified: index.html 
# 

让我们改用git mv

> $ git mv css/iphone.css css/mobile.css 
> $ git status 
# On branch master 
# Changes to be committed: 
# (use "git reset HEAD <file>..." to unstage) 
# 
# renamed: css/iphone.css -> css/mobile.css 
# 
# Changed but not updated: 
# (use "git add <file>..." to update what will be committed) 
# (use "git checkout -- <file>..." to discard changes in working directory) 
# 
# modified: index.html 
# 

看起来我们很好。那么为什么git在我使用Finder时第一次认识到重命名?

+26

Git跟踪内容,而不是文件,因此,如何让索引进入适当的状态 - “add + rm”或“mv” - 它会产生相同的结果并不重要。然后Git使用它的重命名/复制检测来让你知道它是一个重命名。你引用的来源也不准确。无论您是否在同一个提交中修改+重命名,都没关系。当您在修改和重命名时进行差异化时,重命名检测会将其视为重命名+修改,或者如果修改是完全重写,则会显示为已添加和删除 - 仍然无关紧要它。 – Cascabel 2010-04-14 21:53:18

+6

如果这是真的,为什么它没有使用Finder重命名检测到它? – 2010-04-14 22:45:04

+19

'git mv old new'自动更新索引。当你在Git之外重命名时,你必须执行'git add new'和'git rm old'来对索引进行更改。一旦你完成了这个'git status'就可以像你期望的那样工作。 – 2010-04-15 01:16:40

回答

305

对于git mvmanual page

指数成功完成后更新, [...]

因此,在首先你要对自己的 更新索引(通过使用git add mobile.css)。然而
git status 仍然会显示两个不同的文件

$ git status 
# On branch master 
warning: LF will be replaced by CRLF in index.html 
# Changes to be committed: 
# (use "git reset HEAD <file>..." to unstage) 
# 
#  modified: index.html 
#  new file: mobile.css 
# 
# Changed but not updated: 
# (use "git add/rm <file>..." to update what will be committed) 
# (use "git checkout -- <file>..." to discard changes in working directory) 
# 
#  deleted: iphone.css 
# 

你可以得到不同的输出通过运行 git commit --dry-run -a导致你 期望

[email protected] /d/temp/blo (master) 
$ git commit --dry-run -a 
# On branch master 
warning: LF will be replaced by CRLF in index.html 
# Changes to be committed: 
# (use "git reset HEAD <file>..." to unstage) 
# 
#  modified: index.html 
#  renamed: iphone.css -> mobile.css 
# 

我不能确切原因告诉你我们在git status
之间看到这些差异 git commit --dry-run -a,但是 这里是 Linus

的git的提示确实也不护理对整个 “重命名检测”内部,以及任何你有 与改名做是完全独立的 启发式的提交我们然后用显示的重命名。

一个dry-run使用真正的重命名机制,而 git status可能不会。

+1

你没有提到你做过'git add mobile.css'的步骤。没有它'git status -a'只会'看到'删除之前跟踪的'iphone.css'文件,但不会触及新的,未跟踪的'mobile.css'文件。 此外,'Git status -a'在Git 1.7.0及更高版本中无效。 “”git status“不再是”git commit --dry-run“。”http://www.kernel.org/pub/software/scm/git/docs/RelNotes-1.7.0.txt。如果你想要这个功能,使用'git commit --dry-run -a'。 正如其他人所说的,只需更新索引,'git status'就可以像OP期望的那样工作。 – 2010-04-15 01:12:04

+3

如果你做了一个普通的'git commit',它不会提交重命名文件,工作树仍然是一样的。 'git commit -a'几乎打破了git工作流程/思维模型的每一个方面 - 每一个变化都承诺着。如果您只想重命名文件,但在另一次提交中提交对“index.html”的更改会怎么样? – knittl 2010-04-15 05:52:07

+0

@Chris:是的,当然我加了'mobile.css',我应该提到。但这是我的答案的重点:手册页说当你使用'git-mv'时,'索引被更新'。感谢'status -a'的澄清,我使用了git 1.6.4 – tanascius 2010-04-15 12:37:04

9

你必须git add css/mobile.css新文件和git rm css/iphone.css,所以git知道它。然后它会显示在git status

相同的输出,您可以在状态输出清楚地看到它(该文件的新名称):

# Untracked files: 
# (use "git add <file>..." to include in what will be committed) 

和(旧名):

# Changed but not updated: 
# (use "git add/rm <file>..." to update what will be committed) 

我认为幕后的git mv不过是一个包装脚本,它完全是这样的:从索引中删除文件,并以不同的名称添加它

+0

我不认为我必须'git rm css/iphone.css',因为我认为这将删除现有的历史。也许我误解了git中的工作流程。 – 2010-04-14 22:50:10

+4

@Greg K:'git rm'不会删除历史记录。它只从索引中删除一个条目,以便下一个提交不会有条目。但是,它仍然存在于祖先的承诺中。 你可能会感到困惑的是(例如)'git log - new'会在你提交'git mv old new'的地方停下来。如果你想跟随重命名,使用'git log --follow - new'。 – 2010-04-15 01:56:26

3

您没有分级取景器移动的结果。我相信如果你通过Finder进行了移动,然后做了git add css/mobile.css ; git rm css/iphone.css,git会计算新文件的哈希值,然后才会意识到文件的哈希匹配(因此它是重命名)。

7

Git会从内容识别的文件,而不是把它看作一个新的未跟踪文件

这就是你出了问题。

这只是之后你添加文件,git会从内容中识别它。

+0

没问题。当分阶段的git会正确显示重命名。 – 2018-01-12 13:16:53

65

你必须将两个修改后的文件添加到索引之前git会将其识别为移动。

mv old newgit mv old new之间的唯一区别在于git mv也将文件添加到索引。

mv old new然后git add -A也可以工作。

请注意,您不能只使用git add .,因为这不会将删除添加到索引。

Difference between "git add -A" and "git add ."

+3

感谢'git add -A'链接,非常有用,因为我正在寻找这样的捷径! – PhiLho 2012-11-27 16:00:29

+5

请注意,使用git 2,'git add。** **会将删除添加到索引。 – 2014-07-02 19:27:24

2

在你真的要手动重命名文件,如例。使用脚本批量重命名一堆文件,然后使用git add -A .为我工作。

1

对于Xcode用户:如果您在Xcode中重命名文件,则会看到徽章图标更改为追加。如果您使用XCode进行提交,您实际上会创建一个新文件并丢失历史记录。

一种解决方法是容易的,但你必须使用commiting之前的Xcode做到这一点:

  1. 做你的文件夹上一个git状态。您应该看到的是,暂存的变更是正确的:

改名为:项目/ OldName.h - >项目/ NewName.h 改名为:项目/ OldName.m - >项目/ NewName.m

  1. 做提交-m“更名”

然后回到XCode中,你会看到M,从变化的徽章,这是保存提交furtur在现在使用的Xcode的变化。

16

最好的办法是自己试一试。

mkdir test 
cd test 
git init 
touch aaa.txt 
git add . 
git commit -a -m "New file" 
mv aaa.txt bbb.txt 
git add . 
git status 
git commit --dry-run -a 

现在git的地位和git的承诺--dry运行-a显示了两种不同的结果,其中git的状态显示bbb.txt作为一个新的文件/ aaa.txt被删除,--dry运行命令显示实际重命名。

~/test$ git status 

# On branch master 
# Changes to be committed: 
# (use "git reset HEAD <file>..." to unstage) 
# 
# new file: bbb.txt 
# 
# Changes not staged for commit: 
# (use "git add/rm <file>..." to update what will be committed) 
# (use "git checkout -- <file>..." to discard changes in working directory) 
# 
# deleted: aaa.txt 
# 


/test$ git commit --dry-run -a 

# On branch master 
# Changes to be committed: 
# (use "git reset HEAD <file>..." to unstage) 
# 
# renamed: aaa.txt -> bbb.txt 
# 

现在继续并办理登机手续。

git commit -a -m "Rename" 

现在您可以看到该文件实际上已被重命名,并且git状态中显示的内容是错误的。

故事的道德:如果您不确定文件是否被重命名,请发出“git commit --dry-run -a”。如果它显示该文件被重命名,那么你很好。

+2

对于Git而言,*两者都是正确的。不过,后者正在接近你,因为承诺者可能会看到它。重命名和删除+创建之间的* real *区别仅限于操作系统/文件系统级别(例如,相同的inode#与新的inode#),Git并不十分关心。 – 2014-04-04 08:08:04

+2

这是像我这样的noob最清晰的答案。 – Wok 2015-04-22 11:18:32

7

第一步:重新命名的oldfile的文件NEWFILE

git mv #oldfile #newfile 

第二步:git的承诺,并添加注释

git commit -m "rename oldfile to newfile" 

第三步:推动这种变化给远程服务器

git push origin #localbranch:#remotebranch 
+1

请添加一些注释,以便对OP – Devrath 2014-05-27 18:49:42

+0

有帮助步骤2是不必要的。在'git mv'之后,新文件已经在索引中。 – 2014-05-28 22:59:59

+0

当然,我会修改它。谢谢 – Haimei 2014-05-29 17:32:46

7

让我们从git的角度思考你的文件。

记住Git并不跟踪有关文件的任何元数据

你的档案库有(其中包括)

$ cd repo 
$ ls 
... 
iphone.css 
... 

,这是git的控制之下:

$ git ls-files --error-unmatch iphone.css &>/dev/null && echo file is tracked 
file is tracked 

用以下项进行测试:

$ touch newfile 
$ git ls-files --error-unmatch newfile &>/dev/null && echo file is tracked 
(no output, it is not tracked) 
$ rm newfile 

当你

$ mv iphone.css mobile.css 

从git的角度来看,

  • 没有iphone.css(它被删除-git发出警告但─)。
  • 有一个新文件mobile.css
  • 这些文件是完全不相关的。

所以,git的建议有关,它已经知道文件(iphone.css)和新文件检测(mobile.css),但只有当文件在索引或HEAD混帐开始检查它们的内容。

此时,既没有“iphone.css删除”也没有mobile.css在索引上。

添加iphone.css删除索引

$ git rm iphone.css 

混帐告诉你到底发生了什么:(iphone.css被删除没有更多的事。)

再添加新的文件移动。 css

$ git add mobile.css 

这次删除和新文件都在索引上。现在git检测上下文是相同的,并将其公开为重命名。事实上,如果文件是50%相似,它会检测到作为重命名,可以让您更改mobile.css一点点,同时保持作为重命名的操作。

看到这是在git diff可重现。现在你的文件在索引上,你必须使用--cached。编辑mobile.css了一下,添加到索引和看到的区别:

$ git diff --cached 

$ git diff --cached -M 

-Mgit diff “检测重命名” 选项。-M代表-M50%(50%或更多的相似性会使git表示为重命名),但如果您编辑mobile.css,则可以将其降低为-M20%(20%)。

5

对于git的1.7.x以下命令为我工作:

git mv css/iphone.css css/mobile.css 
git commit -m 'Rename folder.' 

有没有必要添加的git,因为原来的文件(即CSS/mobile.css)已经在提交的文件之前。