3

我知道我可以使用如下git命令为指定分支提取给定文件夹的历史记录:

git filter-branch --subdirectory-filter "a sub directory" -- myBranch

不幸的是,该子目录在历史上的某个时刻被重命名为a subdirectorytoaSubdirectory例如。不幸的是filter-branch,重命名停止了。

有没有办法做到这一点?

4

2 回答 2

2
git filter-branch --prune-empty --index-filter '
  git ls-files -z |
    egrep --invert-match --null-data "^(a subdirectory|aSubdirectory)/" |
      xargs -0 --no-run-if-empty git rm --cached -q

  git ls-files -s | sed -re "s-\t(a subdirectory|aSubdirectory)/-\t-" |
    git update-index --index-info

  git ls-files -z |
    egrep --null-data "^(a subdirectory|aSubdirectory)/" |
      xargs -0 git rm --cached -q
' -- myBranch

重命名文件是一项高级操作(哈!),因此我们将其分解为删除和添加组件。

脚本中的第一个命令会删除a subdirectory或之外的所有内容aSubdirectory。第二个将这些目录中的所有内容添加到存储库根目录中。最后,第三个通过删除这些目录中的所有文件来完成移动。

例如,从历史开始

$ git lol --name-status
* 27c7275 (HEAD, myBranch) file2
| 一个子目录/file2
* 39d7e75 MV
| D 一个子目录/file1
| 一个子目录/file1
* c710654 文件1
  一个子目录/file1

运行git filter-branch上面的命令会导致历史

$ git lola --name-status
* da6c7ae (HEAD, myBranch) file2
| 一个文件2
* d94110a 文件 1
  一个文件1
* 27c7275 (refs/original/refs/heads/myBranch) 文件 2
| 一个子目录/file2
* 39d7e75 MV
| D 一个子目录/file1
| 一个子目录/file1
* c710654 文件1
  一个子目录/file1

refs/original/refs/heads/myBranch是一个备份,在验证结果后,您可以丢弃

git update-ref -d refs/original/refs/heads/myBranch

离开

$ 混帐萝拉
* da6c7ae (HEAD, myBranch) file2
* d94110a 文件 1

笔记:

  • 由于--prune-empty.
  • 我不知道这将如何处理重命名冲突。
  • git lol并且git lola是非标准但非常有用的别名
于 2013-11-12T19:12:57.150 回答
0

我发现这很有用,但我在 OSX 上并且对@Greg Bacon 的解决方案有一些麻烦,因为 xargs 和 egrep 缺乏一些功能。我最终编写了一个 perl 脚本,我确定它在 $PATH 上

git filter-branch --prune-empty --tag-name-filter cat --index-filter 'git_prune_non_proxy_dirs.pl' -- --all

perl 脚本看起来像这样(我想你可以修改它来完成所有三个步骤,这只是“第一部分”确保在这种情况下使用 system 而不是 exec 来调用 git)

#!/usr/bin/perl 

open(LS,"git ls-files|") || die "Failed to git ls-files: $!\n";
my %to_remove=();
while ( <LS> ) {
    chomp;
    if( $_ =~ m#(^adserver-proxy(|-api)/)#) {
    }else{
        my @components = split('/',$_, 2);
        my $path_to_delete = $components[0];
        $to_remove{$path_to_delete}=1;
    }
}
if (%to_remove) { 
    my @cmd=(
        "git","rm","-rfq","--cached","--", keys %to_remove);
    exec(@cmd) || die "Failed to invoke git rm"
}
于 2015-11-18T20:32:38.483 回答