Browse Source

build: allow custom refs for patch import & export (#41320)

feat: allow custom refs for patch import & export (#41306)

* feat: allow custom refs for patch import & export

feat: add Patch-Dir metainfo, a sibling to Patch-Filename

* chore: copyediting

* refactor: minor copyediting

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <[email protected]>
trop[bot] 1 year ago
parent
commit
ea680012de
2 changed files with 37 additions and 42 deletions
  1. 25 38
      script/lib/git.py
  2. 12 4
      script/lib/patches.py

+ 25 - 38
script/lib/git.py

@@ -15,6 +15,9 @@ import re
 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)
@@ -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)
 
 
@@ -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:
@@ -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
@@ -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:
@@ -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:
@@ -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)

+ 12 - 4
script/lib/patches.py

@@ -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)