【问题标题】:clean git history of deleted files, keeping renamed files history清除已删除文件的 git 历史记录,保留重命名文件的历史记录
【发布时间】:2015-11-23 07:23:59
【问题描述】:

我想将一些文件提取到新的存储库中,保留它们的历史记录,包括文件重命名。

我能找到的最佳和最接近的答案是new-repo-with-copied-history-of-only-currently-tracked-files,使用git filter-branch --index-filter。它成功地保留了现有文件的历史记录,但它不保留重命名文件的历史记录。

(我能找到的另一个答案是使用git filter-branch --subdirectory-filter。但它有两个问题:似乎不适用于整个 repo(文件夹'.')并且不保留重命名文件的历史记录。)

(另一个答案是使用git subtree。但它根本不保留历史记录。)

所以我可能正在寻找一种方法来改进git ls-files > keep-these.txt 命令,使其从最接近的答案也列出所有以前的文件名。也许是脚本?

【问题讨论】:

    标签: git version-control git-filter-branch


    【解决方案1】:

    Git 不存储文件名更改。

    每个提交存储一个完整的树,例如,可能提交 1234567... 有文件 README 和 foo.txt 并且提交 fedcba9... 有文件 readme.txt 和 foo。如果您要求 git 比较提交 1234567 和提交 fedcba9,并且 README 与readme.txt 足够相似1,git 会说将一个提交转换为另一个提交的方式是重命名文件。 (如果一个提交是另一个提交的父提交,则子提交的git show 将显示重命名,因为git show 在git show 时间计算此更改。)

    另一方面,如果第二个readme文件差别太大,但README与foo足够相似,git会说改1234567实现fedcba9的方法是重命名README 到 foo。

    关键是 git 会在您要求比较时 计算,而不是更早。提交之间没有任何内容显示“重命名某些文件”。 Git 只是比较提交并决定然后文件是否足够相似。

    出于您的目的,这最终意味着对于提交到复制或部分复制序列中的每个提交,您必须决定保留哪些路径名以及丢弃哪些路径名。如何实现这一点主要取决于您。 git log 命令确实有一个 --follow 标志来激活有限数量的重命名检测,因为它从子提交到其父级向后工作,git blame 自动尝试做同样的事情;您可以使用这些(一次一个路径名)来提出表单的映射:

          in:   commits A..B    C..D             E..F
    use path:   dir/file.ext    dir/frill.txt    lib/frill.next
    

    例如。但是没有内置的东西可以做到这一点,而且不会特别容易。我首先将git log --follow 与--raw 或--name-status 输出结合起来,看看是否检测到任何有趣的重命名。如果有的话,这些是提交边界,您希望在提交时更改保留和丢弃的路径(无论是使用filter-branch 还是其他方法)。

    如果这不起作用,或者您需要更多控制,请考虑在各个提交对之间运行 git diff --name-status(提交对信息来自 git rev-list)。


    1只要您要求重命名检测,“完全相同”就足够相似,任何东西都低至“50% 相似”。您可以使用提供给git diff 的-M 标志的可选值来调整所需的相似度。


    编辑:这似乎工作正常。我在 git 自己的 builtin/var.c 上使用过,它曾经有两个以前的名字,根据这个:

    $ git log --follow --raw --diff-filter=R --pretty=format:%H builtin/var.c
    81b50f3ce40bfdd66e5d967bf82be001039a9a98
    :100644 100644 2280518... 2280518... R100       builtin-var.c   builtin/var.c
    
    55b6745d633b9501576eb02183da0b0fb1cee964
    :100644 100644 d9892f8... 2280518... R096       var.c   builtin-var.c
    

    --diff-filter 抑制除了重命名输出之外的所有内容,以便我们可以看到似乎是哪个提交重命名了文件。把它变成更有用的东西需要更多的工作,但这可能会让你走得很远:

    git log --follow --raw --diff-filter=R --pretty=format:%H builtin/var.c |
    while true; do
        if ! read hash; then break; fi
        IFS=$'\t' read mode_etc oldname newname
        read blankline
        echo in $hash, rename $oldname to $newname
    done
    

    生产的:

    in 81b50f3ce40bfdd66e5d967bf82be001039a9a98, rename builtin-var.c to builtin/var.c
    in 55b6745d633b9501576eb02183da0b0fb1cee964, rename var.c to builtin-var.c
    

    【讨论】:

    • 你能提供一个脚本来列出以前的文件名吗?我不知道该怎么做,而且我有数百个文件要处理,所以不能一次手动分析一个文件的日志。默认 50% 相似度对我来说很好。
    • 谢谢你,我得到了带有git ls-files | while read -r line; do (git log --follow --raw --diff-filter=R --pretty=format:%H "$line" | while true; do if ! read hash; then break; fi; IFS=$'\t' read mode_etc oldname newname; read blankline; echo $oldname; done); done的旧文件名列表
    猜你喜欢
    • 2021-07-28
    • 1970-01-01
    • 2015-12-10
    • 2015-04-20
    • 2010-09-28
    • 2014-06-19
    • 2020-03-20
    • 1970-01-01
    相关资源
    最近更新 更多