最近有个奇葩的需求,基于某些保密原因,需要比较两个时间点编译的jar包中的class文件,将没有修改的已经提交到仓库中的对应源码文件彻底删除,从所有提交历史中都抹除。经过查找,可以使用两种方法:
1、git filter-branch --tree-filter 'rm -rf ${file_path_name}' --prune-empty -f HEAD --all
这种方式已经被废弃,修改速度慢,而且修改完成后git push origin 时总是报错。(我是没有提交成功)。
2、git filter-repo --force --invert-paths --path ${file_path_name}
这种方式是filter-branch替代方式,需要先安装filter-repo(需要先安装 python 然后 pip install git-filter-repo 来安装,详见github-git-filter-repo)。
第二种方式最大的问题是执行后,不在与原仓库有关联,必须提交到新的一个仓库,目前尚未研究怎么提交到原仓库的方法。
注:${file_path_name}为你想要删除的文件路径和文件名称,路径是相对于仓库的目录,例如:WEB-INF/src/util/FileUtil.java
上面方法如果单个文件操作时,非常简单,当存在大量文件时,就比较繁琐,因此用python整理了一个脚本,实现对两个文件进行比较,并自动对比较结果中文件相同的源文件(没有修改),从仓库中抹除,最后提交到新的仓库中。可以保留了提交历史的仓库。
因此编写了一个python批量脚本:dir_compare_rm_file.py内容如下:
#coding=utf-8
#!/usr/bin/env python
#import git_filter_repo
import sys
import os
import re
from filecmp import dircmp
import subprocess
dir_old=r'D:\your_project_old\WEB-INF\classes'
dir_new=r'D:\your_project_new\WEB-INF\classes'
repos_path=r'D:\your_project_repos'
##存放比较结果
same_files_list=[]
#递归比较文件夹函数
def print_diff_files(dcmp):
for name in dcmp.diff_files:
print("diff_file %s found in %s and %s" % (name, dcmp.left, dcmp.right))
if dcmp.left_only:
print('%s: %s ' % (dcmp.left, dcmp.left_only))
if dcmp.right_only:
print('%s: %s ' % (dcmp.right, dcmp.right_only))
for same_files in dcmp.same_files:
if(same_files.endswith(".class") and not "$" in same_files):
same_files_list.append(os.path.join(dcmp.right, same_files))
for sub_dcmp in dcmp.subdirs.values():
print_diff_files(sub_dcmp)
#执行递归文件夹比较
dcmp = dircmp(dir_old, dir_new)
print_diff_files(dcmp)
#输出比较后,相同的文件
for index,same_files in enumerate(same_files_list):
rm_file=same_files[same_files.index('WEB-INF\\'):].replace("classes\\","src\\")
#从右替换一次扩展名为java
rm_file=re.sub(r'(.*).class',r'\1.java',rm_file)
#执行命令行cd到仓库目录
p = subprocess.Popen(' cd ' +repos_path,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
encoding='utf-8'
)
#stdout
print(p.communicate()[0])
#执行命令行filter-repo 抹除文件
print('[%s/%s] remove file: git filter-repo --force --invert-paths --path %s ' % (index,len(same_files_list),rm_file))
p = subprocess.Popen(' git filter-repo --force --invert-paths --path '+rm_file,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
encoding='utf-8'
)
print(p.communicate()[0])
#stdout
最后提交到远程仓库:
#最后提交到新的远程仓库:
# git remote add origin https://gitlab_URL/your_project.git
#推送仓库
# git push -u origin master