我正在对我的仓库进行更改。
所以我有文件index.d.ts
,我想把它的所有内容移动到另一个文件file1.d.ts
,我想将所有index.d.ts
提交历史记录移动到新文件。但我仍然不想从存储库中删除"index.d.ts"。
我尝试使用git mv index.d.ts file1.d.ts
,但一旦用新内容重新创建index.d.ts
,历史记录就会自动移回index.d.ts
并从file1.d.ts
中删除
有没有办法将当前的index.d.ts
文件历史记录移动到文件file1.d.ts
?
当您想查看文件重命名后的历史记录时,可以使用
git log --follow <filename>
它将通过重命名提交不断向您提供文件的历史记录,一直到文件最初使用其原始名称创建时。
所以使用
git mv index.d.ts file1.d.ts
提交结果,然后当您想查看历史记录时,使用
git log --follow file1.d.ts
理想情况下,只使用git mv
操作进行提交,然后在重命名文件后对要在文件中进行的更改进行单独提交。如果对文件进行了一些更改,--follow
仍然可以找到重命名,但如果更改过多,它可能无法识别出这是一个重命名,而不是一个无关的新文件。
EDIT在torek的回答中,他们指出了一个重要的细节:如果您想在拥有git mv
'后将不同的index.d.ts
文件重新添加到您的repo中,请将其作为git mv
的单独提交添加回来,否则您将破坏--follow
检测重命名操作的能力。他们回答中的细节。
请参阅joanis的答案以获得一个实用的解决方案:重命名文件,提交,然后添加一个,单独的提交,在其中添加一个使用旧名称的新文件。这使得git log --follow
能够跟随重命名。
如何理解以及为什么这样做
你问过";将历史移动到另一个文件";,但是Git没有的";文件历史记录";首先。Git有提交。提交是历史记录。
在Git中,每次提交都会保存每个文件的完整快照。(每个提交也包含一些元数据,但对于这个答案,我们只关注快照。)中的文件这些快照被压缩、Git化并一直冻结,而且——这很重要——要消除其内容的重复。重复数据消除意味着,如果一行中有任何一对提交,Git会调用这些"提交";"父";以及";"孩子";承诺,但暂时考虑一下,就把它们想象成";在";以及";在"之后;,或";旧的";以及";新的"--并且在旧提交中,有一些文件读取";我是一个文件";,在新的提交中,有一个文件——任何名称——也读取";我是一个文件";,这两个文件是彼此的重复,并且是消除重复的。
Git可以非常轻松地找到一个消除重复的文件。Git可以找到类似的文件(不完全相同,但基本相同的文件),但这比找到匹配的文件要困难得多,因为它们已经被消除了重复。Git使用这种重复数据消除的主要目的是,当您进行新的提交时,您可能只更改了旧提交中的几十个、数百个、数百万个文件中的一个或两个。因此,新提交中的几乎所有文件都是旧提交中旧文件的副本。通过消除内容的重复,Git节省了大量空间:只有真正必须保存任何修改过的文件,即使两者都提交了"包含";所有文件。
现在,正如我上面所说的,存储库中的历史无非是存储库中提交。当您运行git log
时,Git会找到当前提交——通常是您通过运行git checkout mybranch
或git switch mybranch
选择的某个分支上的最新提交——然后向后工作,一次一个提交,向您显示每个提交的日志消息。这是因为提交是历史:如果我们从最后一个开始并显示其日志消息,然后跳回到其父级并显示该日志消息,再跳回到一个并显示该日志信息,然后。。。好吧,重复一遍,直到我们用完提交:我们现在已经看到了历史1
不过,有时我们想知道:在哪些提交中,我更改了文件F所以不仅仅是:
git log
我们运行:
git log -- F
如果F
是,也是分支名称,这里需要双连字符,例如,告诉git log
,我们指的是fileF
,而不是分支F
;当F
没有被错误解释时,我们可以省略--
,但每次都包含它是个好主意。
这个仍然遍历所有2提交,向后,一次一个提交。但这一次Git使用了";文件完全相同-内容被消除重复";事情假设在当前提交中,文件F存在,并且与上一次(父级)提交中的文件完全相同。Git可以加载两次提交的快照,立即看到文件被消除了重复,并且不需要打印出任何关于当前提交的。
Git然后向后移动一跳到上一次提交。Git找到它的父级,加载两个快照,并检查文件是相同的(已消除重复)还是不同的(未消除重复)。如果是一样的话,Git不会提到这个提交。如果不同的话,Git会以通常的方式提到这个提交。
然后,我们永远一次重复一次提交,检查这次提交和上一次提交是否都有相同版本的文件(或者都完全省略了文件)。如果是这样的话,提交是不感兴趣的,Git不会打印它。如果不是——如果文件由于这个提交而发生了更改、存在或删除——那么这个提交是有趣的Git会打印它。
这样做的最终结果是;参见";a";文件F"的历史;。但文件F没有历史记录:只有提交,这些就是历史记录。我们所看到的不是";F"的历史;是";所有历史,除了F没有改变的提交";。是同一件事吗?好吧,也许:输入--follow
。
假设我们处于某对提交<CCD_ 27、CCD_;。Git将提取这两个快照,并查看名为F的文件是否同时存在,或者两者都不存在。进一步假设F存在于new
中,而不存在于old
中。由于--follow
,Git现在可以查找old
中存在且new
中不存在的文件。假设有五个这样的文件。这些是我们的候选文件。我们现在检查这五个文件中是否有任何一个是";足够相似";到new
中的副本。
这张支票对";"完全相同";,所以我们让Git先检查一下。如果这五个文件中的任何一个是100%相同的,因此被消除了重复,那么为什么old
中名称为oldF的文件在new
中必须被重命名为。因此:
git log
打印提交new
,因为文件发生了更改(无论如何都会这样做),和git log --follow
更改它要查找的名称。它不再查找文件F,现在正在查找旧的F
如果100%相同的东西不起作用,git log
使用较慢的";它足够近吗;火柴您可以设置匹配阈值:默认情况下需要至少50%的匹配。如果超过一个文件满足阈值,Git将采用最高匹配文件,并将其称为";旧文件";名称如果没有文件达到阈值,Git将假设文件F已被删除,并继续查找名称F,看看它是否在早期提交中被重新创建。
为了实现这一切,旧文件名必须在某对提交之间消失,并且您必须使用出现在同一新旧提交对中的新文件名启动git log --follow
。提交仅重命名操作将确保(a)发生这种情况,并且(b)匹配是100%匹配,因此进行得很快。因此,将文件commit重命名为单独的commit可以使git log --follow
工作得很好。
(不过,git log --follow
中存在缺陷。请参阅脚注2,并注意,目前实现的--follow
只能处理一个文件名。您必须知道新的名称:--follow
不适用于git log --reverse
。)
1更准确地说,我们已经看到了可到达的提交,从当前提交开始并向后工作。有关可达性(这是Git中的另一个关键概念)的更多信息,请参阅Think Like(a)Git。
2这并不完全正确:git log -- <path>
打开git log
所称的历史简化。这使得Git丢弃某些历史路径。当使用--follow
时,这比看起来损失更小。有关这方面的(更多)信息,请参阅,例如,git log没有正确返回文件的历史记录,git log(--follow)无法显示重命名之外的历史记录以及其他提到历史简化的答案。