我知道我可以使用如下git
命令为指定分支提取给定文件夹的历史记录:
git filter-branch --subdirectory-filter "a sub directory" -- myBranch
不幸的是,该子目录在历史上的某个时刻被重命名为a subdirectory
toaSubdirectory
例如。不幸的是filter-branch
,重命名停止了。
有没有办法做到这一点?
我知道我可以使用如下git
命令为指定分支提取给定文件夹的历史记录:
git filter-branch --subdirectory-filter "a sub directory" -- myBranch
不幸的是,该子目录在历史上的某个时刻被重命名为a subdirectory
toaSubdirectory
例如。不幸的是filter-branch
,重命名停止了。
有没有办法做到这一点?
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
是非标准但非常有用的别名我发现这很有用,但我在 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"
}