清除已删除文件的git历史记录,保留重命名的文件历史记录

时间:2015-11-23 07:23:59

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

我想将一些文件提取到一个新的仓库,保留其历史记录,包括重命名的文件

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

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

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

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

1 个答案:

答案 0 :(得分:3)

Git不会存储文件名更改。

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

另一方面,如果第二个git show文件太不相同,但readmeREADME非常相似,那么git会说改变foo的方式实现1234567是将fedcba9重命名为README

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

出于您的目的,这最终意味着对于您提交复制或部分复制的每个提交,您必须确定要保留哪些路径名称以及要保留哪些路径名称丢弃。如何实现这一点主要取决于你。 foo命令有一个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(提交对信息来自git diff)。

1 只要您要求重命名检测,"完全相同"是足够相似的,就像约有50%相似的东西一样。您可以使用提供给-M builtin/var.c标记的可选值调整所需的相似度。

修改:这似乎工作正常。我在git自己的$ 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

产生:

namespace foo.blah {
    export function baz() {
        console.log('hello');
    }
}