在git中处理文件重命名

时间:2010-04-14 21:23:10

标签: git git-mv

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

然而,今晚这样做我最终还是回到了git mv

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

将Finder中的样式表从iphone.css重命名为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
#

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

12 个答案:

答案 0 :(得分:327)

对于git mv manual 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导致你的结果 预期

Tanascius@H181 /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使用真正的重命名机制,而a git status可能不会。

答案 1 :(得分:72)

在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 ."

答案 2 :(得分:18)

最好的办法是亲自尝试。

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 status和git commit --dry-run -a显示两个不同的结果,其中git status显示bbb.txt作为新文件/ aaa.txt被删除,而--dry-run命令显示实际重命名

~/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 status中显示的内容是错误的。

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

答案 3 :(得分:10)

你必须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只不过是一个包装脚本,它正是这样做的:从索引中删除文件并以不同的名称添加它

答案 4 :(得分:9)

让我们从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中时git开始检查他们的内容。

此刻,&#34; iphone.css删除&#34; mobile.css 也不在索引上。

将iphone.css删除添加到索引

$ git rm iphone.css

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

然后添加新文件 mobile.css

$ git add mobile.css

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

git diff上看到此内容可以重现。既然您的文件在索引上,您必须使用--cached。稍微修改 mobile.css ,将其添加到索引并查看以下内容之间的区别:

$ git diff --cached 

$ git diff --cached -M

-M是&#34;检测重命名&#34; git diff的选项。 -M代表-M50%(50%或更高的相似度会让git将其表示为重命名)但如果您编辑mobile.css,可以将其减少到-M20%(20%)

答案 5 :(得分:9)

对于git 1.7.x,以下命令对我有用:

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

不需要git add,因为原始文件(即css / mobile.css)之前已经在提交的文件中。

答案 6 :(得分:7)

  

Git会识别内容中的文件,而不是将其视为新的未跟踪文件

那是你出错的地方。

只有之后添加文件,git才能从内容中识别它。

答案 7 :(得分:7)

步骤1:将文件从oldfile重命名为newfile

git mv #oldfile #newfile

Step2:git commit并添加评论

git commit -m "rename oldfile to newfile"

步骤3:将此更改推送到远程服务器

git push origin #localbranch:#remotebranch

答案 8 :(得分:3)

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

答案 9 :(得分:2)

如果您确实需要手动重命名文件,例如。使用脚本批量重命名一堆文件,然后使用git add -A .为我工作。

答案 10 :(得分:1)

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

解决方法很简单,但在使用Xcode提交之前必须这样做:

  1. 在您的文件夹上执行git状态。您应该看到分阶段的更改是正确的:
  2. 重命名:Project / OldName.h - &gt;项目/ NewName.h 重命名:Project / OldName.m - &gt;项目/ NewName.m

    1. 执行-m'名称更改'
    2. 然后返回XCode,您将看到徽章从A更改为M并保存,以便现在使用xcode进行更改。

答案 11 :(得分:0)

刚遇到这个问题-如果您更新了一堆文件并且不想全部git mv,那么这也可行:

  1. 将父目录从/dir/RenamedFile.js重命名为/whatever/RenamedFile.js
  2. git add -A进行更改
  3. 将父目录重命名回/dir/RenamedFile.js
  4. git add -A将再次重新执行该更改,并强制git选择文件名更改。