在git中处理文件重命名

Gre*_*g K 422 git git-mv

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

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

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

将Finder中的样式表重命名iphone.cssmobile.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
Run Code Online (Sandbox Code Playgroud)

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

> $ git reset HEAD .
Unstaged changes after reset:
M   css/iphone.css
M   index.html
Run Code Online (Sandbox Code Playgroud)

回到我开始的地方.

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

让我们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
#
Run Code Online (Sandbox Code Playgroud)

看起来我们很好.那么为什么git不会在我使用Finder时第一次认出重命名?

tan*_*ius 339

对于git mv手册

成功完成后索引会更新,[....]

因此,首先您必须自己更新索引(通过使用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
#
Run Code Online (Sandbox Code Playgroud)

您可以通过运行获得不同的输出, 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
#
Run Code Online (Sandbox Code Playgroud)

我无法确切地告诉你为什么我们看到git status和之间存在这些差异
git commit --dry-run -a,但这是Linus的暗示

git甚至不关心内部的整个"重命名检测",你用重命名做的任何提交完全独立于我们用于显示重命名的启发式方法.

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

  • 很棒的答案!我正在撞墙,试图找出为什么`git status`没有检测到重命名.添加我的"新"文件后运行`git commit -a --dry-run`显示重命名,最后让我有信心提交! (4认同)
  • 如果你执行正常的`git commit`,它将不会提交重命名的文件,并且工作树仍然是相同的.`git commit -a`几乎击败了git的工作流/思维模型的每个方面 - 每一个变化都被提交.如果您只想重命名该文件,但在另一次提交中将更改提交到`index.html`,该怎么办? (3认同)
  • 在 git 1.9.1 中,`git status` 现在的行为类似于 `git commit`。 (2认同)

non*_*lar 76

在git将其识别为移动之前,您必须将两个已修改的文件添加到索引中.

mv old new和之间的唯一区别git mv old new是git mv还将文件添加到索引中.

mv old new然后git add -A也会工作.

请注意,您不能只使用,git add .因为它不会向索引添加删除.

请参阅"git add -A"和"git add"之间的区别.

  • 请注意,对于git 2,`git add .`**确实**将删除添加到索引中. (5认同)
  • 感谢`git add -A`链接,非常有用,因为我正在寻找这样的捷径! (3认同)

dim*_*thu 19

最好的办法是亲自尝试.

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
Run Code Online (Sandbox Code Playgroud)

现在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
#
Run Code Online (Sandbox Code Playgroud)

现在继续办理登机手续.

git commit -a -m "Rename"
Run Code Online (Sandbox Code Playgroud)

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

故事的道德:如果您不确定您的文件是否已重命名,请发出"git commit --dry-run -a".如果它显示文件已重命名,那么你很高兴.

  • 对于Git来说重要的是,*两者都是正确的*.后者正在接近你的方式,就像提交者可能会看到的那样.rename和delete + create之间的*real*差异仅在OS /文件系统级别(例如,相同的inode#与新的inode#),Git并不十分关心. (3认同)
  • 对于像我这样的菜鸟来说,这是最清晰的答案. (2认同)

小智 12

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

git mv css/iphone.css css/mobile.css
git commit -m 'Rename folder.' 
Run Code Online (Sandbox Code Playgroud)

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

  • 这个。所有其他答案都是荒谬且不必要的复杂。这样可以维护两次提交之间的文件历史记录,以便文件重命名之前/之后的合并不会中断。 (4认同)

kni*_*ttl 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)
Run Code Online (Sandbox Code Playgroud)

和(旧名):

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

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

  • @Greg K:`git rm`不会删除历史记录.它只从索引中删除一个条目,以便下一次提交不具有该条目.但是,它仍将存在于祖先的提交中.您可能会感到困惑的是(例如)`git log - new`将停止在您提交`git mv old new`的位置.如果你想跟随重命名,请使用`git log --follow - new`. (4认同)

alb*_*fan 9

让我们从git角度考虑你的文件.

请记住,git不会跟踪有关您文件的任何元数据

您的存储库(以及其他)

$ cd repo
$ ls
...
iphone.css
...
Run Code Online (Sandbox Code Playgroud)

它受git控制:

$ git ls-files --error-unmatch iphone.css &>/dev/null && echo file is tracked
file is tracked
Run Code Online (Sandbox Code Playgroud)

用以下方法测试:

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

当你这样做

$ mv iphone.css mobile.css
Run Code Online (Sandbox Code Playgroud)

从git的角度来看,

  • 没有iphone.css(它被删除了--git警告说 - ).
  • 有一个新文件mobile.css.
  • 这些文件完全不相关.

因此,git建议它已经知道的文件(iphone.css)和它检测到的新文件(mobile.css),但只有当文件在索引或HEAD中时,git才开始检查它们的内容.

此时,"iphone.css删除"和mobile.css都没有索引.

将iphone.css删除添加到索引

$ git rm iphone.css
Run Code Online (Sandbox Code Playgroud)

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

然后添加新文件mobile.css

$ git add mobile.css
Run Code Online (Sandbox Code Playgroud)

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

看到这是可以重现的git diff.既然您的文件在索引上,您必须使用--cached.稍微编辑一下mobile.css,将其添加到索引并查看之间的区别:

$ git diff --cached 
Run Code Online (Sandbox Code Playgroud)

$ git diff --cached -M
Run Code Online (Sandbox Code Playgroud)

-M是"检测重命名"选项git diff.-M代表-M50%(50%或更多的相似性会让git将其表示为重命名)但-M20%如果你编辑mobile.css,你可以将其减少到(20%).


has*_*sen 7

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

那是你出错的地方.

只有您添加文件后,git才会从内容中识别它.


Hai*_*mei 7

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

git mv #oldfile #newfile
Run Code Online (Sandbox Code Playgroud)

第2步:git提交并添加注释

git commit -m "rename oldfile to newfile"
Run Code Online (Sandbox Code Playgroud)

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

git push origin #localbranch:#remotebranch
Run Code Online (Sandbox Code Playgroud)