Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: allow custom refs for patch import & export #41317

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 25 additions & 38 deletions script/lib/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
import subprocess
import sys

from .patches import PATCH_FILENAME_PREFIX, is_patch_location_line

UPSTREAM_HEAD='refs/patches/upstream-head'

def is_repo_root(path):
path_exists = os.path.exists(path)
Expand Down Expand Up @@ -77,14 +80,10 @@ def am(repo, patch_data, threeway=False, directory=None, exclude=None,
proc.returncode))


def import_patches(repo, **kwargs):
def import_patches(repo, ref=UPSTREAM_HEAD, **kwargs):
"""same as am(), but we save the upstream HEAD so we can refer to it when we
later export patches"""
update_ref(
repo=repo,
ref='refs/patches/upstream-head',
newvalue='HEAD'
)
update_ref(repo=repo, ref=ref, newvalue='HEAD')
am(repo=repo, **kwargs)


Expand All @@ -94,32 +93,18 @@ def update_ref(repo, ref, newvalue):
return subprocess.check_call(args)


def get_upstream_head(repo):
args = [
'git',
'-C',
repo,
'rev-parse',
'--verify',
'refs/patches/upstream-head',
]
def get_commit_for_ref(repo, ref):
args = ['git', '-C', repo, 'rev-parse', '--verify', ref]
return subprocess.check_output(args).decode('utf-8').strip()

def get_commit_count(repo, commit_range):
args = [
'git',
'-C',
repo,
'rev-list',
'--count',
commit_range
]
args = ['git', '-C', repo, 'rev-list', '--count', commit_range]
return int(subprocess.check_output(args).decode('utf-8').strip())

def guess_base_commit(repo):
def guess_base_commit(repo, ref):
"""Guess which commit the patches might be based on"""
try:
upstream_head = get_upstream_head(repo)
upstream_head = get_commit_for_ref(repo, ref)
num_commits = get_commit_count(repo, upstream_head + '..')
return [upstream_head, num_commits]
except subprocess.CalledProcessError:
Expand Down Expand Up @@ -151,7 +136,6 @@ def format_patch(repo, since):
'format-patch',
'--keep-subject',
'--no-stat',
'--notes',
'--stdout',

# Per RFC 3676 the signature is separated from the body by a line with
Expand Down Expand Up @@ -206,8 +190,8 @@ def get_file_name(patch):
"""Return the name of the file to which the patch should be written"""
file_name = None
for line in patch:
if line.startswith('Patch-Filename: '):
file_name = line[len('Patch-Filename: '):]
if line.startswith(PATCH_FILENAME_PREFIX):
file_name = line[len(PATCH_FILENAME_PREFIX):]
break
# If no patch-filename header, munge the subject.
if not file_name:
Expand All @@ -220,19 +204,18 @@ def get_file_name(patch):

def join_patch(patch):
"""Joins and formats patch contents"""
return ''.join(remove_patch_filename(patch)).rstrip('\n') + '\n'
return ''.join(remove_patch_location(patch)).rstrip('\n') + '\n'


def remove_patch_filename(patch):
"""Strip out the Patch-Filename trailer from a patch's message body"""
def remove_patch_location(patch):
"""Strip out the patch location lines from a patch's message body"""
force_keep_next_line = False
n = len(patch)
for i, l in enumerate(patch):
is_patchfilename = l.startswith('Patch-Filename: ')
next_is_patchfilename = i < len(patch) - 1 and patch[i + 1].startswith(
'Patch-Filename: '
)
skip_line = is_patch_location_line(l)
skip_next = i < n - 1 and is_patch_location_line(patch[i + 1])
if not force_keep_next_line and (
is_patchfilename or (next_is_patchfilename and len(l.rstrip()) == 0)
skip_line or (skip_next and len(l.rstrip()) == 0)
):
pass # drop this line
else:
Expand All @@ -248,20 +231,24 @@ def to_utf8(patch):
return unicode(patch, "utf-8")


def export_patches(repo, out_dir, patch_range=None, dry_run=False, grep=None):
def export_patches(repo, out_dir,
patch_range=None, ref=UPSTREAM_HEAD,
dry_run=False, grep=None):
if not os.path.exists(repo):
sys.stderr.write(
"Skipping patches in {} because it does not exist.\n".format(repo)
)
return
if patch_range is None:
patch_range, num_patches = guess_base_commit(repo)
patch_range, num_patches = guess_base_commit(repo, ref)
sys.stderr.write("Exporting {} patches in {} since {}\n".format(
num_patches, repo, patch_range[0:7]))
patch_data = format_patch(repo, patch_range)
patches = split_patches(patch_data)
if grep:
olen = len(patches)
patches = filter_patches(patches, grep)
sys.stderr.write("Exporting {} of {} patches\n".format(len(patches), olen))

try:
os.mkdir(out_dir)
Expand Down
16 changes: 12 additions & 4 deletions script/lib/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,27 @@
import codecs
import os

PATCH_DIR_PREFIX = "Patch-Dir: "
PATCH_FILENAME_PREFIX = "Patch-Filename: "
PATCH_LINE_PREFIXES = (PATCH_DIR_PREFIX, PATCH_FILENAME_PREFIX)


def is_patch_location_line(line):
return line.startswith(PATCH_LINE_PREFIXES)

def read_patch(patch_dir, patch_filename):
"""Read a patch from |patch_dir/filename| and amend the commit message with
metadata about the patch file it came from."""
ret = []
added_filename_line = False
added_patch_location = False
patch_path = os.path.join(patch_dir, patch_filename)
with codecs.open(patch_path, encoding='utf-8') as f:
for l in f.readlines():
line_has_correct_start = l.startswith('diff -') or l.startswith('---')
if not added_filename_line and line_has_correct_start:
ret.append('Patch-Filename: {}\n'.format(patch_filename))
added_filename_line = True
if not added_patch_location and line_has_correct_start:
ret.append('{}{}\n'.format(PATCH_DIR_PREFIX, patch_dir))
ret.append('{}{}\n'.format(PATCH_FILENAME_PREFIX, patch_filename))
added_patch_location = True
ret.append(l)
return ''.join(ret)

Expand Down