sdk-hwV1.3/scripts/make_patch.py

397 lines
16 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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文件可以从DIFF、CACHE、HISTORY中抽取特定格式提高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()