我手动移动了一个文件,然后修改了它。根据Git,它是一个新文件和一个已删除的文件。有什么方法可以强制Git将其视为文件移动吗?


在不同的提交中执行移动和修改操作。


如果你的修改不是很严重,Git会自动检测移动/重命名。只需git添加新文件,git rm旧文件。Git状态将显示它是否检测到重命名。

此外,对于目录的移动,你可能需要:

CD到目录结构的顶部。 执行git add -A命令。 运行git status来验证“新文件”现在是一个“重命名”文件

如果git状态仍然显示“新文件”而不是“重命名”,你需要遵循Hank Gay的建议,在两次单独的提交中进行移动和修改。


这都是感性的事情。Git通常很擅长识别移动,因为Git是一个内容跟踪器

所有这一切都取决于你的“统计”如何显示它。这里唯一的区别是-M标志。

git log——stat -M

commit 9c034a76d394352134ee2f4ede8a209ebec96288
Author: Kent Fredric
Date:   Fri Jan 9 22:13:51 2009 +1300


        Category Restructure

     lib/Gentoo/Repository.pm                |   10 +++++-----
     lib/Gentoo/{ => Repository}/Base.pm     |    2 +-
     lib/Gentoo/{ => Repository}/Category.pm |   12 ++++++------
     lib/Gentoo/{ => Repository}/Package.pm  |   10 +++++-----
     lib/Gentoo/{ => Repository}/Types.pm    |   10 +++++-----
     5 files changed, 22 insertions(+), 22 deletions(-)

git撒谎

commit 9c034a76d394352134ee2f4ede8a209ebec96288
Author: Kent Fredric
Date:   Fri Jan 9 22:13:51 2009 +1300

    Category Restructure

 lib/Gentoo/Base.pm                |   36 ------------------------
 lib/Gentoo/Category.pm            |   51 ----------------------------------
 lib/Gentoo/Package.pm             |   41 ---------------------------
 lib/Gentoo/Repository.pm          |   10 +++---
 lib/Gentoo/Repository/Base.pm     |   36 ++++++++++++++++++++++++
 lib/Gentoo/Repository/Category.pm |   51 ++++++++++++++++++++++++++++++++++
 lib/Gentoo/Repository/Package.pm  |   41 +++++++++++++++++++++++++++
 lib/Gentoo/Repository/Types.pm    |   55 +++++++++++++++++++++++++++++++++++++
 lib/Gentoo/Types.pm               |   55 -------------------------------------
 9 files changed, 188 insertions(+), 188 deletions(-)

Git帮助日志

   -M
       Detect renames.

   -C
       Detect copies as well as renames. See also --find-copies-harder.

git diff -M或git log -M会自动检测这些更改,如重命名和微小的更改,只要它们确实是。 如果你的小变化不是小的,你可以降低相似度阈值,例如。

$ git log -M20 -p --stat

将其从默认的50%降低到20%。


对于一个或几个未提交的重命名和修改文件,这里有一个快速而简单的解决方案。

假设文件的名字是foo,现在它的名字是bar:

将工具条重命名为临时名称: Mv横边 结帐foo: git结帐头foo 用Git将foo重命名为bar: Git mv foo bar 现在将临时文件重命名为bar。 毫伏侧杆

最后一步是将更改后的内容返回到文件中。

虽然这可以工作,但如果移动的文件与原始git的内容差异太大,则会考虑更有效地确定这是一个新对象。让我来演示一下:

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

    new file:   .gitignore
    renamed:    README -> README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   README.md
    modified:   work.js

$ git add README.md work.js # why are the changes unstaged, let's add them.
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    deleted:    README
    new file:   README.md
    modified:   work.js

$ git stash # what? let's go back a bit
Saved working directory and index state WIP on dir: f7a8685 update
HEAD is now at f7a8685 update
$ git status
On branch workit
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .idea/

nothing added to commit but untracked files present (use "git add" to track)
$ git stash pop
Removing README
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    new file:   README.md

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:    README
    modified:   work.js

Dropped refs/stash@{0} (1ebca3b02e454a400b9fb834ed473c912a00cd2f)
$ git add work.js
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    new file:   README.md
    modified:   work.js

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:    README

$ git add README # hang on, I want it removed
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    deleted:    README
    new file:   README.md
    modified:   work.js

$ mv README.md Rmd # Still? Try the answer I found.
$ git checkout README
error: pathspec 'README' did not match any file(s) known to git.
$ git checkout HEAD README # Ok the answer needed fixing.
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    new file:   README.md
    modified:   work.js

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:    README.md
    modified:   work.js

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

    Rmd

$ git mv README README.md
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    renamed:    README -> README.md
    modified:   work.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   work.js

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

    Rmd

$ mv Rmd README.md
$ git status
On branch workit
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitignore
    renamed:    README -> README.md
    modified:   work.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   README.md
    modified:   work.js

$ # actually that's half of what I wanted; \
  # and the js being modified twice? Git prefers it in this case.

If you're using TortoiseGit it's important to note that Git's automatic rename detection happens during commit but the fact that this is going to happen isn't always displayed by the software beforehand. I had moved two files to a different directory and performed some slight edits. I use TortoiseGit as my commit tool and the Changes made list showed the files being deleted and added, not moved. Running git status from the command line showed a similar situation. However after committing the files, they showed up as being renamed in the log. So the answer to your question is, as long as you haven't done anything too drastic, Git should pick up the rename automatically.

编辑:显然,如果你添加新文件,然后从命令行执行git状态,重命名应该在提交前显示。

编辑2:另外,在TortoiseGit中,在提交对话框中添加新文件,但不要提交它们。然后,如果您进入Show Log命令并查看工作目录,您将看到Git是否在提交前检测到重命名。

同样的问题在这里被提出:https://tortoisegit.org/issue/1389,并已被记录为一个bug来修复这里:https://tortoisegit.org/issue/1440。原来这是一个显示问题与TortoiseGit的提交对话框,也存在于git状态,如果你没有添加新文件。


可能有一个更好的“命令行”方法来实现这一点,我知道这是一种hack,但我从来没有找到一个好的解决方案。

使用TortoiseGIT:如果你有一个GIT提交,其中一些文件移动操作显示为加载添加/删除,而不是重命名,即使文件只有很小的变化,那么这样做:

检查你在当地做了什么 在第二次提交中检入一个小的单行更改 去GIT登录乌龟GIT 选择两个提交,右键单击,选择“merge into one commit”

新的提交现在将正确地显示文件重命名……这将有助于维护正确的文件历史。


如果你说git状态不显示重命名,试试git commit——dry-run -a


对我来说,在提交之前保存所有更改并再次弹出它们是有效的。这使得git重新分析添加/删除的文件,并正确地将它们标记为已移动。


使用git mv命令来移动文件,而不是操作系统的移动命令: https://git-scm.com/docs/git-mv

请注意,git mv命令只存在于git 1.8.5及以上版本。因此,您可能必须更新Git才能使用此命令。


我最近在移动(但不是修改)一些文件时遇到了这个问题。

问题是,当我移动文件时,Git更改了一些行结束符,然后无法判断文件是否相同。

使用git mv解决了这个问题,但它只能在单个文件/目录上工作,而且我在存储库的根目录下有很多文件要做。

解决这个问题的一种方法是使用bash /批处理魔法。

另一种方法如下

移动文件,git提交。这将更新行结束符。 将文件移回原来的位置,现在它们有了新的行结束符,git提交—修改 再次移动文件,git提交—修改。这次行结束符没有变化,所以Git很高兴


当我同时编辑、重命名和移动文件时,这些解决方案都不起作用。解决方案是分两次提交(分别编辑和重命名/移动),然后通过git rebase -i修复第二次提交,使其在一次提交中完成。


或者你也可以听听Amber的回答! 再引用一下:

首先,取消手动移动文件的阶段性添加:

$ git reset path/to/newfile
$ mv path/to/newfile path/to/oldfile

然后,使用Git移动文件:

$ git mv path/to/oldfile path/to/newfile

当然,如果你已经提交了手动移动,你可能想要重置到移动之前的修订,然后简单地从那里git mv。


我理解这个问题的方式是“如何让git识别删除旧文件和创建新文件作为文件移动”。

是的,在工作目录中,一旦你删除了一个旧文件并插入了一个旧文件,git状态会显示“deleted: old_file”和“Untracked files:…”new_file”

但是在分段索引/关卡中,一旦你使用git添加和删除文件,它将被识别为文件移动。要做到这一点,假设您已经使用操作系统完成了删除和创建,请给出以下命令:

git add new_file
git rm old_file

如果文件的内容是50%或更相似,运行git status命令应该给你:

renamed: old_file -> new_file

其他的答案已经涵盖了,你可以简单地git添加新&& git rm OLD,以使git识别移动。

然而,如果你已经在工作目录中修改了文件,add+rm方法将把修改添加到索引中,这在某些情况下可能是不希望的(例如,在大量修改的情况下,Git可能不再识别它是一个文件重命名)。

让我们假设您想要将重命名添加到索引,而不是任何修改。显而易见的方法来实现这一点,是做一个来回重命名mv NEW OLD && git mv OLD NEW。

但是也有一种(稍微复杂一点的)方法可以直接在索引中做这件事,而不需要在工作树中重命名文件:

info=$(git ls-files -s -- "OLD" | cut -d' ' -f-2 | tr ' ' ,)
git update-index --add --cacheinfo "$info,NEW" &&
  git rm --cached "$old"

这也可以放在~/.gitconfig中作为别名:

[alias]
    mv-index = "!f() { \
      old=\"$1\"; \
      new=\"$2\"; \
      info=$(git ls-files -s -- \"$old\" | cut -d' ' -f-2 | tr ' ' ,); \
      git update-index --add --cacheinfo \"$info,$new\" && \
      git rm --cached \"$old\"; \
    }; f"