repo的patch转换成git am能打的patch

安卓sdk项目小的几十G,大的上百G,有的人会把sdk分成许多个小的git然后统一用repo来管理,还有的人会选择把sdk放在一个git中管理. 那么repo中修改后生成的patch是无法直接给git用的,因为路径不一样,patch内容也不相符

比如repo中device子目录是一个git,那么它生成的patch放到另一个git项目中只能cd到device路径,然后再用patch指令打,无法用git am xx.patch来打. 两边同步代码会很麻烦.

需要用脚本进行一个转换.

repo生成manifest快照

repo比如要生成0801到0805之间的修改的patch

8月1号用如下指令生成快照

./repo sync

./repo manifest -r -o manifest_0801.xml

中间做过修改.

8月5号再生成快照

./repo sync

./repo manifest -r -o manifest_0805.xml

得有两个快照, 才能生成这两个快照之间的diff提交.

生成快照后,接下来用脚本来生成两个快照间的修改的patch

生成repo快照间的提交的patch并转换成git am的patch

python 复制代码
# 作者帅得不敢出门
# -*- coding: utf-8 -*-

import sys
import os
import re
import subprocess

def do_popen(cmd):
    #popen
    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True)
    stdout, stderr = process.communicate()
    return stdout.decode('utf-8')


def auto_detect_manifests():
    """遍历repo项目根目录,找到两个快照,快照以.xml结尾"""
    manifests = []
    for item in os.listdir('./'):
        if item.startswith('manifest') and item.endswith('.xml'):
            manifests.append(item)

    if len(manifests) != 2:
        print('Error:the number of manifest_xx.xml files are not equal to 2, abort!')
        sys.exit(1)

    manifests.sort()
    print(manifests)
    return manifests


def get_diffmanifests():
    """
    查看两个repo快照间的差异, 得到类似如下
    changed projects :

            bootable/bootloader/lk changed from e5f360f35a6d3848475ba4920f40c1609b1348b0 to ca0100b2da6ca7831348cb5fcfe8d23c529e8a57
                    [+] ca0100b2 edit 1

            device/sprd/sepolicy changed from f15d0189283f767bd315f0900a59256323c071c2 to 64c586a362cb19a290280e79b2e20e8aab7d94cc
                    [+] 64c586a hello

            kernel/msm-3.18 changed from f36f37a42a94fe78d5d7cbe2e8a4ab6477de74ab to 6b372c76fc0874d19822d978ad557a4aeec0ebee
                    [+] 6b372c76fc08 word



    """
    manifests = auto_detect_manifests()
    cmd = './repo diffmanifests ' + manifests[0] + ' ' + manifests[1]
    print(cmd)
    return do_popen(cmd)


def get_diff_git_log():
    """
    得到两个repo快照间的git修改的log
    [{'path': 'bootable/bootloader/lk', 'from': 'e5f360f35a6d3848475ba4920f40c1609b1348b0', 'to': 'ca0100b2da6ca7831348cb5fcfe8d23c529e8a57'}, {'path': 'device/sprd/sepolicy', 'from': 'f15d0189283f767bd315f0900a59256323c071c2', 'to': '64c586a362cb19a290280e79b2e20e8aab7d94cc'}, {'path': 'kernel/msm-3.18', 'from': 'f36f37a42a94fe78d5d7cbe2e8a4ab6477de74ab', 'to': '6b372c76fc0874d19822d978ad557a4aeec0ebee'}]
    """
    data = get_diffmanifests()
    print(data)
    # 只保留\t开头的有效行
    line = [ x for x in data.split('\n') if x.startswith('\t')]

    ret = []
    for i in range(len(line)):
        if i % 2 == 0:
            #print(line[i])
            log = line[i].split()
            ret.append({'path':log[0], 'from':log[3], 'to':log[5]})

    #print(ret)
    return ret


def get_format_patch(log, outdir):
    try:
        do_popen('rm ./*.patch ')
    except Exception as e:
        print('')

    # 生成从from开始的提交的git patch,生成的git是在子目录中生成的,所以需要在patch内容中的文件路径添加父目录
    do_popen('git format-patch ' + log['from'])
    for file in os.listdir('./'):
        if file.endswith('.patch'):
            print(file)
            with open(file, 'r') as fp:
                new_lines = []
                lines = fp.readlines()
                fp.close()
                for line in lines:
                    #查找文件路径,并在前面拼接上目录, 把repo补丁转换成可以直接在git上打的patch
                    if line.startswith('diff --git') or line.startswith('--- ') or line.startswith('+++ '):
                        #print(line)
                        new_line = re.sub(' a/', ' a/' + log['path'] + '/', line)
                        new_line = re.sub(' b/', ' b/' + log['path'] + '/', new_line)
                        #print(new_line)
                        new_lines.append(new_line)
                    else:
                        new_lines.append(line)

                patch_name = os.path.join(outdir, file)
                with open(patch_name, 'w') as new_fp:
                    print(patch_name)
                    new_fp.writelines(new_lines)
                    new_fp.close()

    try:
        do_popen('rm ./*.patch ')
    except Exception as e:
        print('')



if __name__ == '__main__':
    """
    通过repo获取到两个时间段的提交git log, 再通过git log获取patch
    """

    # 生成的patch放在当前目录的patchs中
    patch_dir = os.path.join(os.getcwd(), 'patchs')
    if not os.path.exists(patch_dir):
        os.mkdir(patch_dir)
    else:
        try:
            # 先删除旧的patch
            do_popen('rm ' + patch_dir + './*.patch ')
        except Exception as e:
            print('')


    #得到两个repo快照间的git修改的log
    logs = get_diff_git_log()
    for log in logs:
        # 遍历git log进入到实际的子git目录中生成patch
        old_dir = os.getcwd()
        os.chdir(log['path'])
        print(log['path'])

        # 把所有的子git中的修改制作成patch放到patchs目录下
        get_format_patch(log, patch_dir)
        os.chdir(old_dir)

作者:帅得不敢出门 csdn原创谢绝转载

相关推荐
悟空20164 小时前
001、Git开发流程规范
git
Li小李同学Li4 小时前
git学习【持续更新中。。。】
git·学习·elasticsearch
晨春计5 小时前
【git】
android·linux·git
念幽6 小时前
Git常用命令
git
神技圈子6 小时前
【git系列】git中的那些迷惑的术语以及概念详解
git
benben0447 小时前
Photoshop使用方法大全
git
ou.cs8 小时前
git 删除远程分支的几种写法
git
atlanteep8 小时前
Linux·权限与工具-git与gdb
linux·git
胆小鬼~18 小时前
【DAY20240918】03教你轻松配置 Git 远程仓库并高效推送代码!
git
哆啦安全21 小时前
git常用命令(patch补丁和解决冲突)
git