sdk-hwV1.3/scripts/make_patch.py

397 lines
16 KiB
Python
Raw Normal View History

2024-05-07 10:09:20 +00:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Power by lisidong v1.0 20171102
import os, sys, getopt
import shutil
import time
import fileinput
import zipfile
class Config:
TYPE_DIFF = 1
TYPE_DIFF_WITH_NEW_FILE = 0
TYPE_CACHE = 2
TYPE_HISTORY = 3
#可手动修改如下参数,执行时可不带参数
P_TYPE = TYPE_DIFF
P_GITS = []
P_HISTORY_DIFF_BETWEEN = []
P_NAME = ""
P_MESSAGE = ""
P_PLATFORM_VERSION = ""
BASE_DIR = os.path.split(os.path.realpath(__file__))[0] + "/../"
P_OUT_ROOT_DIR = BASE_DIR + "AUTO_CREATE_PATCH/"
P_OUT_PATCH_DIR = P_OUT_ROOT_DIR + time.strftime('%Y_%m_%d', time.localtime(time.time())) + "/" + P_NAME + time.strftime('%Y_%m_%d', time.localtime(time.time())) + "/"
P_OUT_README = P_OUT_PATCH_DIR + "README.txt"
P_OUT_SOURCE_FOLDER = P_OUT_PATCH_DIR + "source/"
P_OUT_DIFF_FOLDER = P_OUT_PATCH_DIR + "diff/"
P_OUT_DIFF_PATCH_NAME = P_NAME + ".patch"
P_TEMP_FILE = P_OUT_ROOT_DIR + "temp.patch"
P_TEMP_DIR = P_OUT_ROOT_DIR + "temp/"
@staticmethod
def setname(name):
Config.P_NAME = name;
Config.P_OUT_PATCH_DIR = Config.P_OUT_ROOT_DIR + time.strftime('%Y_%m_%d',
time.localtime(time.time())) + "/" + name + time.strftime(
'%Y_%m_%d', time.localtime(time.time())) + "/"
Config.P_OUT_README = Config.P_OUT_PATCH_DIR + "README.txt"
Config.P_OUT_SOURCE_FOLDER = Config.P_OUT_PATCH_DIR + "source/"
Config.P_OUT_DIFF_FOLDER = Config.P_OUT_PATCH_DIR + "diff/"
Config.P_OUT_DIFF_PATCH_NAME = Config.P_NAME + ".patch"
class FileState:
name = ""
hashfrom = ""
hashto = ""
cachestatus = ""
diffstatus = ""
def __init__(self, name, hashfrom, hashto, cachestatus, diffstatus):
self.name = name
self.hashfrom = hashfrom
self.hashto = hashto
self.cachestatus = cachestatus
self.diffstatus = diffstatus
def isdir(self):
return os.path.isdir(self.name)
def istypecache(self):
return self.cachestatus not in [' ', '?', '']
def istypediff(self):
return self.diffstatus not in ['', ' ']
def dump(self):
return self.cachestatus + self.diffstatus + " " + self.name
def dumpall(self):
return "name=" + self.name + " hashfrom=" + self.hashfrom + " self.hashto=" + self.hashto + \
" cache=" + self.cachestatus + " diff=" + self.diffstatus
allgits = []
gitslist = []
gitsdic = {}
def parseConfig():
try:
shortargs = 'd:D:c:p:b:n:m:v:h'
longargs = ['diff=', 'DIFF=', 'cache=', 'patch=', 'between=', 'name=', 'message=', 'version=', 'help']
opts,args= getopt.getopt( sys.argv[1:], shortargs, longargs)
print 'opts=',opts
print 'args=',args
for opt,arg in opts:
#print 'prints',opt,arg
if opt in ('-d','--diff'):
Config.P_TYPE = Config.TYPE_DIFF
Config.P_GITS = arg.split(";")
if opt in ('-D', '--DIFF'):
Config.P_TYPE = Config.TYPE_DIFF_WITH_NEW_FILE
Config.P_GITS = arg.split(";")
elif opt in ('-c','--cache'):
Config.P_TYPE = Config.TYPE_CACHE
Config.P_GITS = arg.split(";")
elif opt in ('-p', '--patch'):
Config.P_TYPE = Config.TYPE_HISTORY
Config.P_GITS = arg.split(";")
elif opt in ('-b', '--between'):
Config.P_HISTORY_DIFF_BETWEEN = arg.split(";")
elif opt in ('-n', '--name'):
Config.setname(arg)
elif opt in ('-m', '--message'):
Config.P_MESSAGE = arg
elif opt in ('-v', '--version'):
Config.P_PLATFORM_VERSION = arg
elif opt in ('-h', '--help'):
usage()
sys.exit(1)
except getopt.GetoptError:
print 'getopt error!'
usage()
sys.exit(1)
def init():
global gitslist,allgits
if os.path.exists(Config.P_OUT_PATCH_DIR):
shutil.rmtree(Config.P_OUT_PATCH_DIR)
if os.path.exists(Config.P_TEMP_DIR):
shutil.rmtree(Config.P_TEMP_DIR)
makefiledirvalid(Config.P_TEMP_DIR)
os.chdir(Config.BASE_DIR)
for git in fileinput.input(".repo/project.list"):
allgits.append(git.strip())
for git in (allgits if len(Config.P_GITS) == 0 else Config.P_GITS):
gitslist.append(git[:-1] if git.endswith("/") else git)
def initgits(gitslist):
if Config.P_TYPE == Config.TYPE_HISTORY:
if len(Config.P_GITS) == 0:
for gitdir in gitslist:
gitsdic[gitdir] = initgitshistory(gitdir, Config.P_HISTORY_DIFF_BETWEEN[0].split(":")[0], Config.P_HISTORY_DIFF_BETWEEN[0].split(":")[1])
else:
for gitdir, between in zip(gitslist, Config.P_HISTORY_DIFF_BETWEEN):
gitsdic[gitdir] = initgitshistory(gitdir, between.split(":")[0], between.split(":")[1])
else:
for gitdir in gitslist:
gitsdic[gitdir] = initgitsdiff(gitdir)
return gitsdic
def initgitsdiff(gitdir):
os.chdir(Config.BASE_DIR + gitdir)
#print 'dir=', os.getcwd()
gitfiles = []
if Config.P_TYPE == Config.TYPE_DIFF or Config.P_TYPE == Config.TYPE_DIFF_WITH_NEW_FILE:
for line in os.popen("git diff --raw").readlines():
filestate = FileState(line.split()[5].strip(), line.split()[2].strip()[:-3], line.split()[3].strip()[:-3], "", line.split()[4].strip())
gitfiles.append(filestate)
if Config.P_TYPE == Config.TYPE_DIFF_WITH_NEW_FILE:
for line in os.popen("git status -s").readlines():
if line.split()[0].strip() == "??":
filestate = FileState(line.split()[1].strip(), "", "", "?", "?")
gitfiles.append(filestate)
elif Config.P_TYPE == Config.TYPE_CACHE:
for line in os.popen("git diff --cached --raw").readlines():
filestate = FileState(line.split()[5].strip(), line.split()[2].strip()[:-3], line.split()[3].strip()[:-3], line.split()[4].strip(), "")
gitfiles.append(filestate)
return gitfiles
def initgitshistory(gitdir, difffrom, diffto):
os.chdir(Config.BASE_DIR + gitdir)
#print 'dir=', os.getcwd()
gitfiles = []
if isdatetype(difffrom) and isdatetype(diffto):
os.system("git diff --after=" + difffrom + " --before=" + diffto + " ")
list = os.popen("git log --format=\"%H\" --after=\"" + difffrom + "\" --before=\"" + diffto + "\"").readlines()
difffrom = list(0)
diffto = list(list.count()-1)
for line in os.popen("git diff --raw "+ difffrom + " " + diffto):
filestate = FileState(line.split()[5].strip(), line.split()[2].strip()[:-3], line.split()[3].strip()[:-3], "",
line.split()[4].strip())
gitfiles.append(filestate)
return gitfiles
def isdatetype(datestr):
try:
if ":" in datestr:
time.strptime(datestr, "%Y-%m-%d %H:%M:%S")
else:
time.strptime(datestr, "%Y-%m-%d")
return True
except:
return False
def domakepatch(gitsdic):
makefiledirvalid(Config.P_TEMP_FILE)
if Config.P_TYPE == Config.TYPE_DIFF or Config.P_TYPE == Config.TYPE_DIFF_WITH_NEW_FILE:
for gitdir, gitfiles in gitsdic.iteritems():
if len(gitfiles) == 0:
continue
os.chdir(Config.BASE_DIR + gitdir)
os.system("git diff --binary > " + Config.P_TEMP_FILE)
#print gitdir
for file in gitfiles:
#print file.name + " " + file.diffstatus
if file.diffstatus in (['M', 'A', '?'] if Config.P_TYPE == Config.TYPE_DIFF_WITH_NEW_FILE else ['M', 'A']) :
assert copyfile(Config.BASE_DIR + gitdir + "/" + file.name, Config.P_OUT_SOURCE_FOLDER + gitdir + "/" + file.name), "copy fail"
if (file.diffstatus in ['?']):
if os.path.isdir(file.name):
os.system(
"git diff --no-index " + Config.P_TEMP_DIR + " " + file.name + " >> " + Config.P_TEMP_FILE)
else:
os.system(
"git diff --no-index /dev/null " + file.name + " >> " + Config.P_TEMP_FILE)
assert copyfile(Config.P_TEMP_FILE, Config.P_OUT_DIFF_FOLDER + gitdir + "/" + Config.P_OUT_DIFF_PATCH_NAME, True), "copy fail"
if Config.P_TYPE == Config.TYPE_CACHE:
for gitdir, gitfiles in gitsdic.iteritems():
if len(gitfiles) == 0:
continue
os.chdir(Config.BASE_DIR + gitdir)
os.system("git diff --cached --binary > " + Config.P_TEMP_FILE)
assert copyfile(Config.P_TEMP_FILE, Config.P_OUT_DIFF_FOLDER + gitdir + "/" + Config.P_OUT_DIFF_PATCH_NAME, True), "copy fail"
for file in gitfiles:
if file.cachestatus in ['M','A']:
#print file.dump() + " "+ file.hashto
os.system("git show " + file.hashto + " > " + Config.P_TEMP_FILE)
assert copyfile(Config.P_TEMP_FILE, Config.P_OUT_SOURCE_FOLDER + gitdir + "/" + file.name, True), "copy fail"
if Config.P_TYPE == Config.TYPE_HISTORY:
for gitdir, gitfiles in gitsdic.iteritems():
if len(gitfiles) == 0:
continue
os.chdir(Config.BASE_DIR + gitdir)
for file in gitfiles:
#print "git show --binary " + file.hashto
os.system("git show --binary " + file.hashto + " > " + Config.P_TEMP_FILE + "_")
assert copyfile(Config.P_TEMP_FILE + "_", Config.P_OUT_SOURCE_FOLDER + gitdir + "/" + file.name, True), "copy fail"
#print "git diff --binary " + file.hashfrom + " " + file.hashto + " >> " + Config.P_TEMP_FILE
os.system("git diff --binary " + file.hashfrom + " " + file.hashto + " >> " + Config.P_TEMP_FILE)
assert copyfile(Config.P_TEMP_FILE, Config.P_OUT_DIFF_FOLDER + gitdir + "/" + Config.P_OUT_DIFF_PATCH_NAME, True), "copy fail"
def packpatch():
os.chdir(Config.P_OUT_PATCH_DIR + "../")
patchname = Config.P_NAME + time.strftime('%Y_%m_%d', time.localtime(time.time()))
os.system("tar -czf " + patchname + ".tar.gz " + patchname)
print "OUT:" + os.path.abspath(patchname)
def makefiledirvalid(dir):
desdir = ""
if os.path.isdir(dir):
desdir = dir
else:
desdir = os.path.dirname(dir)
if not os.path.exists(desdir):
os.makedirs(desdir, mode=0o777)
return desdir
def copyfile(src, des, deletesrc=False):
try:
desdir = makefiledirvalid(des)
if os.path.isdir(src) and os.path.isdir(des):
os.rmdir(desdir)
shutil.copytree(src, desdir)
else:
shutil.copy(src, des)
if deletesrc:
os.remove(src)
#os.removedirs(src)
except Exception, e:
print "error---:" + str(e)
return False
else:
return True
def check():
assert set(gitslist).issubset(set(allgits)), '请检查.repo/project.list中是否包含有如下仓库名字%s'%gitslist
if Config.P_TYPE == Config.TYPE_HISTORY:
if (len(gitslist) == 0):
assert len(Config.P_HISTORY_DIFF_BETWEEN) == 1, "当前为HISTORY(-p)模式,请检查-p -b 对应的数组大小是否一致,仓库以’;‘分割"
else:
assert len(gitslist) == len(Config.P_HISTORY_DIFF_BETWEEN), "当前为HISTORY(-p)模式,请检查-p -b 对应的数组大小是否一致,仓库以’;‘分割"
assert Config.P_NAME != None and Config.P_NAME != "", "-n name:名字不能为空"
assert Config.P_MESSAGE != None and Config.P_MESSAGE != "", "-m message:描述不能为空"
def usage():
usage = '''
用法: make_patch [选项...]
生成标准格式的PATCH文件可以从DIFFCACHEHISTORY中抽取特定格式提高PATCH的可追溯行和可读性
Examples:
make_patch -d "android/build/;lichee/linux-3.10" -m message -n name -v "H6 v1.0" #上述两个仓库的diff抽取成补丁
make_patch -D "android/build/;lichee/linux-3.10" -m message -n name -v "H6 v1.0" #上述两个仓库的diff(包括未跟踪的文件)抽取成补丁
make_patch -c "android/build/;lichee/linux-3.10" -m message -n name -v "H6 v1.0" #上述两个仓库的暂存区(cached中)抽取成补丁
make_patch -p "android/build/;lichee/linux-3.10" -b "HEAD^:HEAD;HEAD^^:HEAD" -m message -n name -v "H6 v1.0" #上述两个仓库的历史提交中抽取补丁
make_patch -p "android/build/;lichee/linux-3.10" -b "2017-01-01:2017-02-01;hash1:hash2" -m message -n name -v "H6 v1.0" #上述两个仓库的历史提交中抽取补丁
可修改make_patch.py中的必要属性(Config中:P_TYPE,P_GITS,P_HISTORY_DIFF_BETWEEN,P_NAME,P_MESSAGE,P_PLATFORM_VERSION)而后直接执行make_patch.py即可(参数赋值)(不推荐)
主操作模式:
-d, --diff 类型1创建各仓库的diff差异补丁(git diff )各仓库以';'分割-d "android/frameworks/base;android/build/"
-D, --DIff 类型2创建各仓库的diff差异同时包括未追踪的新文体补丁各仓库以';'分割-D "android/frameworks/base;android/build/"
-c, --cache 类型3创建各仓库的已加入到暂存区的差异补丁(git diff --cache),各仓库以';'分割-D "android/frameworks/base;android/build/"
-p, --patch 类型4创建提取各仓库的历史提交内容各仓库以';'分割-D "android/frameworks/base;android/build/"需要-b参数描述抽取点
-b, --between 配合-p使用描述对应仓库的抽取点(hash值时间等),':'分割两个抽取点';'分割各仓库-b "HEAD^:HEAD;2017-01-01:2017-01-02;hash1:hash2"
-m, --message 此补丁的描述信息必选
-n, --name 此补丁的名字必选
-v, --version 此补丁的适用(或当前发布时)的平台与版本信息等必选
-h, --help 打印此帮助信息
TIPS:
1补丁抽取后必须检查是否正确同时注意log打印
2当仓库被修改得相对复杂(比如含有未跟踪的文件未暂存的工作区暂存的文件等)建议将需要抽取的补丁add到暂存区(而后用-c模式)有利于更清晰明确抽取目标diff
3当使用-p模式时针对-b参数较为严格(对参数要求较高如当仓库只有一个提交HEAD^记录会找不到)需要谨慎使用!
'''
print usage
def dumpinfo(gitsdic):
for (key,values) in gitsdic.items():
print "key=[" + key + "]"
if (len(values) == 0):
print '\033[1;31;40m'
print "Empty,maybe something wrong with this git "
print '\033[0m'
for value in values:
print " " + value.dump()
def patchreadme(gitsdic):
readme = '''
----------------------------------------------------------------------------------------------------
补丁名称''' + Config.P_NAME + '''
适用版本''' + Config.P_PLATFORM_VERSION + '''
发布日期''' + time.strftime('%Y-%m-%d', time.localtime(time.time())) + '''
文件结构:
source 所有涉及修改的源码文件
diff 所有涉及的修改
README.txt 本说明文件
使用方法:
在对应的diff_patch下的各个目录使用git apply ***.patch即可,如发生冲突请使用source目录手动移植注意查阅log信息存在删除文件时较容易忽略
补丁说明:''' + Config.P_MESSAGE + '''
----------------------------------------------------------------------------------------------------
File INFORMATION
'''
makefiledirvalid(Config.P_OUT_README)
os.mknod(Config.P_OUT_README)
file = open(Config.P_OUT_README, 'r+')
file.write(readme)
for (key,values) in gitsdic.items():
#print "key=" + key + " value=" + values
file.write(key + "\n")
for value in values:
file.write(" " + value.dump() + "\n")
file.write("\n")
file.write('''
----------------------------------------------------------------------------------------------------
File Tree List
''')
file.flush()
file.close()
os.chdir(Config.P_OUT_PATCH_DIR)
os.system("tree -a >> " + Config.P_OUT_README)
parseConfig()
init()
check()
initgits(gitslist)
dumpinfo(gitsdic)
domakepatch(gitsdic)
patchreadme(gitsdic)
packpatch()