使用 git 移动文件时丢失历史记录

Losing history when moving files with git

在阅读了此处关于移动 git 跟踪的文件的 question 的回答后,如果您移动 git 正在跟踪的文件,历史记录应该不会受到影响。 但这不是我的经验,所以我做错了什么?这是我的控制台日志

C:\scripts\Python\Cyren>mkdir archive
    
C:\scripts\Python\Cyren>dir
 Volume in drive C has no label.
 Volume Serial Number is 7C59-18A2

 Directory of C:\scripts\Python\Cyren

08-May-22  22:44    <DIR>          .
08-May-22  22:44    <DIR>          ..
08-May-22  22:43    <DIR>          archive
31-Mar-22  19:11             1,878 categories.csv
31-Mar-22  17:57             1,886 categories.txt
30-Mar-22  21:19            14,557 categories.xlsx
29-Apr-22  16:23            19,274 CyrenDopplerAPI.py
06-May-22  12:35            14,585 CyrenDopplerEnv.py
29-Apr-22  16:16            17,672 CyrenSample.py

C:\scripts\Python\Cyren>git log CyrenDopplerAPI.py
commit 4f440a2c132053ebe9c76a16e90abc1dd845d262
Author: Siggi@Reba <siggi@supergeek.us>
Date:   Thu May 5 15:38:58 2022 +0000

    rename

C:\scripts\Python\Cyren>git mv CyrenDopplerAPI.py archive

C:\scripts\Python\Cyren>dir
 Volume in drive C has no label.
 Volume Serial Number is 7C59-18A2

 Directory of C:\scripts\Python\Cyren

08-May-22  22:48    <DIR>          .
08-May-22  22:48    <DIR>          ..
08-May-22  22:48    <DIR>          archive
31-Mar-22  19:11             1,878 categories.csv
31-Mar-22  17:57             1,886 categories.txt
30-Mar-22  21:19            14,557 categories.xlsx
06-May-22  12:35            14,585 CyrenDopplerEnv.py
05-May-22  18:24               109 Infile.txt
06-May-22  12:35    <DIR>          Logs
               5 File(s)         33,015 bytes
               4 Dir(s)  484,630,781,952 bytes free

C:\scripts\Python\Cyren>git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    CyrenDopplerAPI.py -> archive/CyrenDopplerAPI.py
        renamed:    CyrenSample.py -> archive/CyrenSample.py
C:\scripts\Python\Cyren>git commit -m "cleanup and archiving"
[master 1240039] cleanup and archiving
 2 files changed, 0 insertions(+), 0 deletions(-)
 rename Cyren/{ => archive}/CyrenDopplerAPI.py (100%)
 rename Cyren/{ => archive}/CyrenSample.py (100%)

C:\scripts\Python\Cyren>git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

C:\scripts\Python\Cyren>git push
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 454 bytes | 454.00 KiB/s, done.
Total 4 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To https://github.com/siggib007/python.git
   e85489f..1240039  master -> master

C:\scripts\Python\Cyren>git log archive\CyrenDopplerAPI.py
commit 1240039a7e93993027f22979908ee4a9837a5474 (HEAD -> master, origin/master)
Author: Siggi@Reba <siggi@supergeek.us>
Date:   Sun May 8 22:49:03 2022 +0000

    cleanup and archiving

git log 不显示移动之前存在的日志条目。我认为那意味着它被删除了。

顺便说一句,这是 link 对应的 git 集线器回购 https://github.com/siggib007/python/tree/master/Cyren

此外,这不是一个孤立的事件,这是我的控制台日志重现了我之前注意到几次的问题。

使用 git log --follow 跟踪重命名。

--follow

Continue listing the history of a file beyond renames (works only for a single file).

您可以将 log.follow 配置选项设置为 true:

log.follow

If true, git log will act as if the --follow option was used when a single is given. This has the same limitations as --follow, i.e. it cannot be used to follow multiple files and does not work well on non-linear history.

设置运行:

$ git config --global log.follow true

一些简短的(对我来说)注意事项:

  • Is it possible to move/rename files in Git and maintain their history? is a more complete Q&A than is the one you linked (Can Git restructure my folders without losing history?).
  • 中的 --follow 标志在 Git 中已经存在很长时间了。非常不完美。

这里的基本问题是 Git 没有 “文件历史”。 Git 有 次提交 并且提交 历史;每个提交都有每个文件的 完整快照; 因此,重命名文件并提交会产生一个新的提交,与旧提交的快照相比,文件 old/path/to/file.ext 被删除,新文件 new/path/to/file.ext 突然出现。这两个 differently-named 文件的内容匹配,因此 Git de-duplicates 提交快照中的内容,并且 存储库[=中实际上只有一个文件副本106=]。但是就这对快照而言,新旧快照之间的区别是“删除旧文件,创建新文件”。

Git处理这种情况的方法是——可选地——寻找“删除旧的,创建新的”的情况,其中旧文件和新文件是完全相同或足够相似。检测到这种情况后,git diff 将调用文件 renamed.

git log 命令正常工作 commit-by-commit,当使用 git log -p 显示补丁时,每个 上的 运行s git diff一对。您可以让这个 git diff 进行重命名检测,然后它会说“将 重命名为 ”。这个方案有一些小瑕疵,但总体来说,效果还不错。

当你想运行git log -- path/to/file.ext时,摩擦就来了。它的作用是指示 git log 查看 所有 此特定历史记录中的提交,就像没有 path-name 的 git log 一样,但是然后只有 print some 这些提交。请记住,历史 提交 - 所有 - 所以这看起来与 git log 没有路径名的所有历史相同。 1 但是我们 不想要 所有的历史;我们只想要 path/to/file.ext 从旧快照更改为新快照的提交。所以 git log 只打印那些确切路径的文件发生变化的提交。

但在早些时候,path/to/file.ext 被命名为 path/file.ext 而没有中间的 to 部分。当 git log 向后遍历该提交时,文件 不再被称为 path/to/file.ext 。它现在用它的旧名称 path/file.ext 来称呼。因此 git log 忽略打印任何未更改的提交 path/to/file.ext,但它 应该 打印任何 确实 的提交改变 path/file.ext.

这就是 --follow 所做的:它告诉 git log,当它在历史中倒退时,它应该检测重命名。检测到重命名后,它应该停止寻找新名称并开始寻找旧名称

这很好用,但是有一个大问题:它只对一个文件名有效。十多年来一直如此。 Git 的 --follow 代码在内部是一个可怕的 hack,早就应该进行改进了(但是 高效和正确地 做到这一点非常棘手,这就是原因它从未被改进过)。

所以:使用 --follow 但要注意它的局限性。它仅适用于 一个文件 ,即便如此,Git 也必须 检测重命名 。 Git 通常会检测到重命名,当你自己提交时你会看到:

C:\scripts\Python\Cyren>git commit -m "cleanup and archiving"
[master 1240039] cleanup and archiving
 2 files changed, 0 insertions(+), 0 deletions(-)
 rename Cyren/{ => archive}/CyrenDopplerAPI.py (100%)
 rename Cyren/{ => archive}/CyrenSample.py (100%)

“重命名”输出表明 Git 这次检测到重命名,因此它将继续对 git log --follow 进行重命名。 (100%) 意味着它使用最快的路径进行检测(另一个优点:--follow 的情况会更快,尽管这些天你可能永远不会真正注意到)。


1实际上,具有路径规范 git log 默认情况下不会 查看所有历史记录。准确地说,这开启了 git log 所谓的“历史简化”。历史简化是棘手的;它有时做你想做的事,有时做与你想做的完全相反的事情。需要了解的主要内容是:

  • 存在;
  • 当你对路径名使用git log时它会打开;和
  • 如果它让您感到悲伤,您可以使用 --full-history 将其关闭。