为什么git不理解我的文件夹移动操作?

时间:2014-08-11 00:14:16

标签: git rename move

通常当我在git中重命名/移动文件时,git extensions会在文件暂存后将其显示为重命名操作。像this one这样的其他SO问题表明无论我使用git mv还是常规移动,这都应该自动发生。但是,我今天进行了大规模的重组,准备git subtree split(基本上有一个Src文件夹,我将其分为CoreMain,并进行记录我使用Windows资源管理器移动文件夹,而不是git mv)。当我暂停更改时(只更改了几个项目文件,没有任何源代码),几乎所有更改都显示为添加+删除而不是重命名。我希望这只是Git Extensions中的一个小故障,但是当我推送到github时,它并没有任何问题。你可以看到这里的烂摊子:

https://github.com/qwertie/Loyc/commit/3eb4bd9dbe3d0023858659cb96e860921f0819e3

执行本地git subtree split --prefix=Core -b core并推送到另一个本地仓库后,Core文件夹中的所有文件的历史记录都已丢失。

发生了什么事?有没有办法保留所有这些移动文件的历史记录?

> git version
git version 1.8.3.msysgit.0

1 个答案:

答案 0 :(得分:2)

鉴于你正在使用Windows,我首先怀疑的是(除了试图阻止它之外的事情并不重要)将换行符转换为CRLF,反之亦然,这样每条线路确实是不同的。我能够克隆URL,当然,还有一些修改过的CRLF:

$ git show HEAD^ | vis | head -40
[snip]
diff --git a/Src/Baadia/ArrowheadControl.cs b/Baadia/ArrowheadControl.cs
similarity index 94%
rename from Src/Baadia/ArrowheadControl.cs
rename to Baadia/ArrowheadControl.cs
index 2e7aa5c..cdb374f 100644
--- a/Src/Baadia/ArrowheadControl.cs
+++ b/Baadia/ArrowheadControl.cs
@@ -1,18 +1,18 @@
-\M-o\M-;\M-?using System;\^M
-using System.Collections.Generic;\^M
-using System.ComponentModel;\^M
[snip]
+\M-o\M-;\M-?using System;
+using System.Collections.Generic;
[snip]

(\ Mo等是一个字节顺序标记,它没有改变。它是删除\ ^ Ms,回车,在每一行的末尾都有git确信它不仅仅是一个简单的重命名。)


编辑:既然已经(有点)跟踪了CR-LF vs LF的来源,你想要“插入”一个只进行去CR的提交......

让我们说(基于我克隆时看到的内容)你有三个提交序列:

... - A - B - C   <-- branch

其中A是具有CRLF的提交,B是提交,其中所有文件都重命名为也是CRLF到LF的转换,{{1} }是分支C的提示。

首先,您要提取提交A,并进入“分离的HEAD”模式。这很简单:

branch

接下来,您要清除所有文件以删除CR,同时保留LF。在类似Unix的盒子上,您可以使用git checkout branch~2 # branch = C, branch~1 = B, branch~2 = A 或其他任何东西,但是假设dos2unix命令以递归方式zoop执行:

-R

现在提交结果:

zoop -R .    # I'm assuming you're at the top of your work tree

提交图现在看起来像这样:

git commit -am 'CRLF -> LF only' # or whatever message

现在你只想让工作树和索引看起来像commit ... - A - B - C <-- branch \ A2 <-- HEAD ,我们可以用两个命令来完成:

B

第一个git命令完全清空树和索引,第二个命令从提交git rm -rf .; git checkout branch~1 -- . 重新填充索引和树,即提交branch~1。 (请注意,这种形式的B不会更改分支,它只是提取文件。在存储库的顶部,我们提取文件git checkout,递归提取所有文件。)使用提交结果来自.的日志消息:

B

,并提供:

git commit -C branch~1

提交... - A - B - C <-- branch \ A2 - B' <-- HEAD 的树与B'的树匹配,消息也是如此;只有父ID(和一些时间戳)是不同的。

为提交B重复步骤 - { - 1}},这次可以命名为B,而不是C

完成所有操作后,将分支branch移至指向提交branch~1,由branch命名:

C'

或:

HEAD

(这不允许您指定自定义消息),然后git update-ref -m "move to rewritten history" refs/heads/branch HEAD 重新开始,将旧提交git branch -f branch HEAD 放弃到reflogs。

(您可以使用git checkout branch复制提交Cgit cherry-pick,但这可能会更慢,如果它被CRLF混淆也会失败 - &gt; LF变化。)

当到B结果时,你将不得不使用强制推送,因为在github上更新C将不是快进操作(将放弃旧的{{} 1}}和git push提交)。