为什么 'git mv' 不移动文件?如何让它行动"normally"?

Why does 'git mv' not move a file? How to make it act "normally"?

请原谅我的无知...背景是这样的:我创建了一个TestScritps目录来组织测试脚本。我将三个脚本从 <root dir> 移到了 <root dir>/TestScripts。我一次移动一个并在每个之后执行本地提交。然后我推送了所有更改。

我到另一台机器上执行拉取:

$ cd cryptopp/
$ git pull
remote: Counting objects: 25, done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 25 (delta 11), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (25/25), done.
From https://github.com/weidai11/cryptopp
   2ac9ea1..2a22a84  master     -> origin/master
Updating 2ac9ea1..2a22a84
Fast-forward
 TestScripts/cryptest-android.sh |   44 +
 TestScripts/cryptest-ios.sh     |   40 +
 TestScripts/cryptest.sh         | 5729 +++++++++++++++++++++++++++++++++++++++
 rijndael.cpp                    |    2 +-
 setenv-android.sh               |   85 +-
 5 files changed, 5870 insertions(+), 30 deletions(-)
 create mode 100755 TestScripts/cryptest-android.sh
 create mode 100755 TestScripts/cryptest-ios.sh
 create mode 100755 TestScripts/cryptest.sh

$ ls *.sh
cryptest-android.sh  cryptest.sh     setenv-android.sh   setenv-ios.sh
cryptest-ios.sh      rdrand-nasm.sh  setenv-embedded.sh

注意文件只是被复制了;他们没有被感动。

我检查了 git-mv man page,但错误的行为似乎没有被讨论。

我有两个问题。为什么git mv只复制文件,不移动文件?如何让 git mv 执行 "normally"?在这里,"normally" 意味着几乎每个使用过命令行的人都期望的 - 它将文件从 <target location> 移动到 <destination location>.


这里是相关的命令历史。

  994  rm -rf cryptopp/
  995  git clone https://github.com/weidai11/cryptopp
  996  cd cryptopp/
  997  mkdir TestScripts
  998  git mv cryptest.sh TestScripts/
  999  ls *.sh
 1000  git commit TestScripts/cryptest.sh -m "Organize test scripts (Issue 303)"
 1001  ls *.sh
 1002  git mv cryptest-ios.sh TestScripts/
 1003  git commit TestScripts/cryptest-ios.sh -m "Organize test scripts (Issue 303)"
 1004  ls *.sh
 1005  git commit

git pull 的输出中显示的摘要,我看到那些文件没有被删除。

当你这样做时,例如:

git commit TestScripts/cryptest.sh -m "Organize test scripts (Issue 303)"

虽然 Git 通常很适合跟踪文件移动,但在内部,移动被记录为删除一个文件并创建一个新的相同文件。您提交了 新文件 ,并且 没有 从原始位置删除文件,因此看起来 Git 只是复制了文件.

以后,从 commit 命令中省略文件名通常是个好主意:

git commit -m "Organize test scripts (Issue 303)"

您可以事先 git status 查看将要提交的内容,并根据需要进行修改。

完整的会话可能如下所示:

$ git mv cryptest.sh TestScripts/
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        renamed:    cryptest.sh -> TestScripts/cryptest.sh

$ git commit -m "Organize test scripts (Issue 303)"
$ git status
On branch master
nothing to commit, working directory clean
$ ls cryptest.sh
ls: cannot access cryptest.sh: No such file or directory
$ ls TestScripts/cryptest.sh
TestScripts/cryptest.sh

要修复,请执行:

git rm cryptes*.sh
git commit

如果您想修复您的历史记录,您可以这样做:

git rebase -i HEAD^3

并将相关提交的命令从 pick 更改为 edit。在每一站,做:

git rm <file>
git commit --amend
git rebase --continue

其中<file>为当前原始文件。

请注意,这将重写您的历史记录,因此在机器之间移动时可能会出现问题。


请注意,没有必要删除并重新克隆 Git 存储库,因此如果您将来 运行 遇到麻烦,最好尝试解决根本问题。 (Search around or ask here 如果您需要帮助!)

问题出在您(滥用)git commit 命令的使用上。

git mv 确实移动了文件。比如说,我有一个包含单个文件的回购协议,名为 a,我想将其移动为 b:

$ git mv a b
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    a -> b

$ ls
b <-- Only b exists here, no a

移动现在已记录在索引中,但未提交。要创建提交,我这样做:

$ git commit -m "Move a as b"

然而,您所做的是:

$ git commit b -m "Move a as b"
[master b275677] Move a as b
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 b

当您将文件名作为参数提供给 git commit 时,它只包含提交中列出的文件,而不是记录当前索引。

在内部,git 不理解什么是移动。它知道创建了一个文件,并删除了另一个文件。用于显示历史记录的工具(例如 git log)结合这两条信息并显示文件已移动。但是当你指定commit中只包含新创建的文件时,它并没有记录旧文件的删除,所以当你从另一台机器上拉取时,它不会显示为move。

所以你的问题的答案是:"Why does 'git mv' not move a file? How to make it act “normally”?" - 它确实移动了一个文件,但是你明确告诉git只提交新文件的创建,不是删除旧的。要使其正常运行,请不要这样做 - 相反,发出 git commit 不带文件名作为参数。