如何将git标记为已删除的文件并将新文件作为文件移动?

pup*_*eno 453 git

我手动移动了一个文件,然后我修改了它.根据Git,它是一个新文件和一个删除文件.有没有办法强迫Git将其视为文件移动?

Bom*_*mbe 395

如果您的修改不是太严重,Git会自动检测移动/重命名.只是git add新文件和git rm旧文件.git status然后会显示它是否检测到重命名.

另外,对于目录周围的移动,您可能需要:

  1. cd到该目录结构的顶部.
  2. git add -A .
  3. 运行git status以验证"新文件"现在是"重命名"文件

如果git status仍然显示"新文件"而不是"重命名",则需要遵循Hank Gay的建议并执行移动并在两个单独的提交中进行修改.

  • 让我停顿几分钟的东西:如果重命名的文件和已删除的文件没有暂存进行提交,那么它们将显示为删除和新文件.将它们添加到暂存索引后,它会将其识别为重命名. (134认同)
  • 澄清:"不太严重"意味着新文件和旧文件基于git使用的一些相似性索引> 50%'相似'. (64认同)
  • 值得一提的是,当谈到"_Git会自动检测移动/重命名"时.当你使用`git status`,`git log`或`git diff`,****时,它会在你执行`git add`,`git mv`或`git rm`时这样做.进一步说到检测重命名,这只对分阶段文件有意义.所以`git mv`后面跟着文件的变化可能会看起来像`git status`,好像它认为它是`rename`,但当你在文件中使用`git stage`(与`git add`相同)时很明显,这种变化太大,无法被检测为重命名. (17认同)
  • 真正的关键似乎是在同一个`git add`中添加新旧位置,正如@ jrhorn424所暗示的那样,应该可能只是整个仓库. (5认同)
  • 值得一提的是,如果您深入了解已将文件移入的目录结构,请在执行`git add -A .`之前移至树的顶部以获取移动. (2认同)
  • 不过,这并不是万无一失的,尤其是在文件已更改且很小的情况下。有没有一种方法可以专门告诉git有关此举的信息? (2认同)
  • 在 git 中重命名是一个纯粹的装饰功能。从本质上讲,它仍然有一个删除和一个添加,因此你不能真正*让它*做任何事情。如果您确实需要重命名以保持(例如为了便于审查)提交重命名作为对内容更改的单独提交。 (2认同)
  • @YarekT它不是纯粹的修饰功能,因为如果git on commit已检测到重命名,那么查看该文件上的历史记录将在旧文件名中返回历史记录。对我来说,重命名与删除+添加的重点。(在git 2.20上测试) (2认同)

Han*_*Gay 96

在单独的提交中执行移动和修改.

  • 我知道Git可以同时处理移动和修改.使用Java进行编程并使用IDE时,重命名类既是修改又是移动.我知道Git甚至应该能够在移动时(移除和创建)自动识别出来. (11认同)
  • @jrockway,我有.使用小文件很容易发生,我猜他们"变得'太不同'意味着一个移动". (9认同)
  • @cdalxndr Git 没有标记重命名的方法这一事实与这个答案的质量无关。 (7认同)
  • 这似乎是一个低知识的答案。诸如 GIT 之类的高级版本控制系统应该有一种方法,可以在一次提交中将添加的文件设置为已删除文件的重命名。 (4认同)
  • 有没有办法自动化?我在索引中有很多文件,想先提交移动,然后进行更改,但手动操作很难 (2认同)
  • 要注意的一件事是,如果`git diff`在一次提交中无法识别重命名,则在两次提交中将无法识别重命名。您需要使用-M或--find-renames来完成。因此,如果导致您提出此问题的动机是在请求请求中看到重命名(从经验上来讲),将其分为两次提交将不会使您进一步实现该目标。 (2认同)

Ken*_*ric 44

这都是一种感性的东西.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(-)
Run Code Online (Sandbox Code Playgroud)

git log --stat

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

git帮助日志

   -M
       Detect renames.

   -C
       Detect copies as well as renames. See also --find-copies-harder.
Run Code Online (Sandbox Code Playgroud)

  • 对不起,如果这看起来有点迂腐,但"Git一般都很擅长识别动作,因为GIT是一个内容跟踪器"对我来说似乎是一个不合理的.这是一个内容跟踪器,是的,也许它碰巧擅长检测移动,但是一个声明并没有真正跟随另一个.仅仅因为它是一个内容跟踪器,移动检测不一定是好的.事实上,内容跟踪器可能根本没有移动检测. (69认同)
  • 不,我根本没有表现出这一点。我展示了 Git 能够*假装*添加 + 删除是由于内容相同而重命名。它无法“知道”发生了什么,而且它也不在乎。git 记住的所有内容都是“添加”和“删除”。“重命名”从未被记录。 (2认同)

小智 33

git diff -M或者git log -M应该自动检测这些更改为重命名,只要它们确实存在微小变化.如果您的微小变化不小,您可以减少相似性,例如

$ git log -M20 -p --stat
Run Code Online (Sandbox Code Playgroud)

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

  • 是否可以在配置中定义阈值? (11认同)

小智 31

这是一个快速而又脏的解决方案,用于一个或几个未重命名的已重命名和修改的文件.

假设文件已命名foo,现在命名为bar:

  1. 重命名bar为临时名称:

    mv bar side
    
    Run Code Online (Sandbox Code Playgroud)
  2. 结帐foo:

    git checkout HEAD foo
    
    Run Code Online (Sandbox Code Playgroud)
  3. 重命名foo,以bar使用Git:

    git mv foo bar
    
    Run Code Online (Sandbox Code Playgroud)
  4. 现在将您的临时文件重命名为bar.

    mv side bar
    
    Run Code Online (Sandbox Code Playgroud)

最后一步是将更改的内容恢复到文件中.

虽然这可以工作,但如果移动的文件与原始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.
Run Code Online (Sandbox Code Playgroud)

  • 这个过程没有任何意义.git不会为重命名添加任何元数据,`git mv`只是一个方便的`git rm` /`git add`对.如果你已经完成'mv bar foo',那么你所要做的就是确保在提交之前你已经'git add foo`和`git rm bar`.这可以像单个`git add -A`命令一样完成,也可以像`git add foo; git commit -a` sequence. (27认同)
  • 我所知道的是,在我做之前,Git并不认为这是一个举动.在我这样做之后,Git认为这是一个举动. (16认同)
  • 它认为这是一个举动.但它现在也有变化作为一个未分阶段的变化.再次"添加"文件后,git会将移动/修改分解为已删除/再次添加. (6认同)
  • 如果您在步骤3之后"git commit",则此过程将起作用,否则它将不正确.另外,@ CharlesBailey是正确的,你可以在步骤3轻松做一个正常的`mv blah foo`然后提交并获得相同的结果. (4认同)
  • "这个过程没有用处." - 当git感到困惑并且认为文件已被删除并且添加了另一个文件时,它就有用了.这清除了Git的混淆并将文件标记为显式移动而无需进行中间提交. (4认同)
  • 这个过程是没有用的,因为 Git 不会变得“混乱”。Git 决定移动是删除/添加对还是根据文件中内容的更改量进行移动。我认为人们发现的是,这个过程导致 Git 显示分阶段更改的移动,但内容更改却未分阶段。一旦您暂存内容发生更改,暂存移动就会再次更改为删除/添加对。抱歉,你无法颠覆 Git 的力量。Git 做出决定,你对此无能为力。吉特就是上帝。 (4认同)

BoD*_*BoD 17

如果您正在谈论git status不显示重命名,请尝试git commit --dry-run -a改为

  • 真棒.为什么git status没有显示但是-dry-run呢? (6认同)

col*_*fix 11

其他答案已经涵盖了您可以简单地git add NEW && git rm OLD让 git 识别这一举动。

但是,如果您已经修改了工作目录中的文件,add+rm方法会将修改添加到索引中,这在某些情况下可能是不需要的(例如,在进行大量修改的情况下,Git 可能不再识别它是一个文件)改名)。

假设您要将重命名添加到索引,但不进行任何修改。实现此目的的明显方法是来回重命名mv NEW OLD && git mv OLD NEW

但还有一种(稍微复杂的)方法可以直接在索引中执行此操作,而无需重命名工作树中的文件:

# Step 1.
info=$(git ls-files -s -- "OLD" | cut -d' ' -f-2 | tr ' ' ,)

# Step 2.
git update-index --add --cacheinfo "$info,NEW"

# Step 3.
git rm --cached "$old"
Run Code Online (Sandbox Code Playgroud)

这也可以作为别名放入您的~/.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"
Run Code Online (Sandbox Code Playgroud)

细节

  • 步骤 1:存储 OLD 文件模式和 OLD 文件 sha1 信息,以逗号分隔,以供以后使用(例如info=100644,aa27a98714247db199d86d44d523dddf084a6051)。

    为此,HEAD必须在 OLD 文件存在的提交上进行,并且 OLD 文件的删除甚至不应该暂存(但从文件系统中删除的文件并不重要)。

  • 步骤2:使用所述信息模拟OLD在给定路径添加文件NEW

    警告:此阶段将撤消任何已阶段化的新路径更改。

  • 最后,分阶段删除OLD文件。

全做完了!


Gil*_*rts 10

如果你正在使用TortoiseGit,重要的是要注意Git的自动重命名检测在提交期间发生,但事实上这种情况并不总是由软件预先显示.我已将两个文件移动到另一个目录并执行了一些轻微的编辑.我使用TortoiseGit作为我的提交工具,更改制作列表显示文件被删除和添加,而不是移动.从命令行运行git status显示了类似的情况.但是在提交文件后,它们显示为在日志中重命名.所以你的问题的答案是,只要你没有做过任何太激烈的事情,Git就应该自动获取重命名.

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

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

这里提出了同样的问题:https://tortoisegit.org/issue/1389并且已被记录为修复此处的错误:https://tortoisegit.org/issue/1440 事实证明这是TortoiseGit提交的显示问题如果您尚未添加新文件,则对话框以及存在于git状态的类型.

  • 你是对的,即使TortoiseGit显示delete + add,即使git status显示delete + add,即使git commit --dry-run显示delete + add,git commit之后我看到rename而不是delete + add. (2认同)

Zin*_*gam 7

或者您可以在这里尝试Amber回答这个问题的方法!再次引用:

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

$ git reset path/to/newfile
$ mv path/to/newfile path/to/oldfile
Run Code Online (Sandbox Code Playgroud)

然后,使用Git移动文件:

$ git mv path/to/oldfile path/to/newfile
Run Code Online (Sandbox Code Playgroud)

当然,如果您已经进行了手动移动,则可能需要在移动之前重置为修订版,然后从那里简单地git mv即可。

  • 请注意,如果您还进行了重大更改(通常发生在小文件上),则此方法不起作用。它被提交为“删除”和“创建”。在这种情况下,我想唯一的选择是“在单独的提交中进行移动和修改”,正如另一个答案所建议的那样。 (12认同)

mos*_*shi 6

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

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