summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuido Günther <agx@sigxcpu.org>2012-11-25 13:53:28 +0100
committerGuido Günther <agx@sigxcpu.org>2012-11-25 17:51:13 +0100
commitc6b7bc2967f3d7522e0932fd4ef43f1d6856aa25 (patch)
tree4994c194e53c8f6101dc8209fa4f9365d2f35e4e
parent4a5fa0d9db472eca8a3fb7a1dcd177b7addd916d (diff)
downloadgit-buildpackage-c6b7bc2967f3d7522e0932fd4ef43f1d6856aa25.tar.gz
git-buildpackage-c6b7bc2967f3d7522e0932fd4ef43f1d6856aa25.tar.bz2
git-buildpackage-c6b7bc2967f3d7522e0932fd4ef43f1d6856aa25.zip
gbp-pq: Improve patch header write out
Avoid trailing new lines in the patch header when reexporing patches. to avoid diffs when nothing changed.
-rw-r--r--gbp/scripts/common/pq.py139
1 files changed, 119 insertions, 20 deletions
diff --git a/gbp/scripts/common/pq.py b/gbp/scripts/common/pq.py
index 3f939678..708d392c 100644
--- a/gbp/scripts/common/pq.py
+++ b/gbp/scripts/common/pq.py
@@ -65,43 +65,142 @@ def pq_branch_base(pq_branch):
return pq_branch[len(PQ_BRANCH_PREFIX):]
+def patch_read_header(src):
+ """
+ Read a patches header and split it into single lines. We
+ assume the header ends at the first line starting with
+ "diff ..."
+ """
+ header = []
+
+ for line in src:
+ if line.startswith('diff '):
+ break
+ else:
+ header.append(line)
+ else:
+ raise GbpError("Failed to find patch header in %s" % src.name)
+ return header
+
+
+def patch_header_parse_topic(header):
+ """
+ Parse the topic from the patch header removing the corresponding
+ line. This mangles the header in place.
+
+ @param header: patch header
+ @type header: C{list} of C{str}
+
+ >>> h = ['foo', 'gbp-pq-topic: bar']
+ >>> patch_header_parse_topic(h)
+ 'bar'
+ >>> h
+ ['foo']
+ """
+ topic = None
+ index = -1
+
+ for line in header:
+ if line.lower().startswith("gbp-pq-topic: "):
+ index = header.index(line)
+ break
+ if index != -1:
+ topic = header[index].split(" ", 1)[1].strip()
+ del header[index]
+ return topic
+
+
+def patch_header_mangle_newline(header):
+ """
+ Look for the diff stat separator and remove
+ trailing new lines before it. This mangles
+ the header in place.
+
+ @param header: patch header
+ @type header: C{list} of C{str}
+
+ >>> h = ['foo bar\\n', '\\n', 'bar', '\\n', '\\n', '\\n', '---\\n', '\\n']
+ >>> patch_header_mangle_newline(h)
+ >>> h
+ ['foo bar\\n', '\\n', 'bar', '\\n', '---\\n', '\\n']
+ """
+ while True:
+ try:
+ index = header.index('---\n')
+ except ValueError:
+ return
+ try:
+ # Remove trailing newlines until we have at
+ # at most one left
+ if header[index-1] == header[index-2] == '\n':
+ del header[index-2]
+ else:
+ return
+ except IndexError:
+ return
+
+
+def patch_write_header(srcname, dstname):
+ """
+ Write out the patch header doing any necessary processing such
+ as detecting and removing a given topic, dropping trailing
+ new lines and skipping the first line containing the sha1.
+ """
+ topic = None
+
+ with file(srcname) as src:
+ header = patch_read_header(src)
+ header_len = len(''.join(header))
+
+ topic = patch_header_parse_topic(header)
+ patch_header_mangle_newline(header)
+
+ with file(dstname, 'w') as dst:
+ dst.write(''.join(header[1:]))
+
+ return (header_len, topic)
+
+
+def patch_write_content(srcname, dstname, header_len):
+ """
+ Write out the patch body skipping the header
+ """
+ with file(srcname) as src:
+ src.seek(header_len, 0)
+ with file(dstname, 'a') as dst:
+ dst.write(src.read())
+
+
def write_patch(patch, patch_dir, options):
"""Write the patch exported by 'git-format-patch' to it's final location
(as specified in the commit)"""
oldname = os.path.basename(patch)
- newname = oldname
tmpname = patch + ".gbp"
- old = file(patch, 'r')
- tmp = file(tmpname, 'w')
topic = None
- # Skip first line (From <sha1>)
- old.readline()
- for line in old:
- if line.lower().startswith("gbp-pq-topic: "):
- topic = line.split(" ", 1)[1].strip()
- gbp.log.debug("Topic %s found for %s" % (topic, patch))
- continue
- tmp.write(line)
- tmp.close()
- old.close()
-
- if not options.patch_numbers:
+ header_len, topic = patch_write_header(patch, tmpname)
+ patch_write_content(patch, tmpname, header_len)
+
+ if options.patch_numbers:
+ newname = oldname
+ else:
patch_re = re.compile("[0-9]+-(?P<name>.+)")
m = patch_re.match(oldname)
if m:
newname = m.group('name')
+ else:
+ raise GbpError("Can't get patch name from '%s'" % oldname)
if topic:
- topicdir = os.path.join(patch_dir, topic)
+ dstdir = os.path.join(patch_dir, topic)
else:
- topicdir = patch_dir
+ dstdir = patch_dir
- if not os.path.isdir(topicdir):
- os.makedirs(topicdir, 0755)
+ if not os.path.isdir(dstdir):
+ os.makedirs(dstdir, 0755)
os.unlink(patch)
- dstname = os.path.join(topicdir, newname)
+ dstname = os.path.join(dstdir, newname)
gbp.log.debug("Moving %s to %s" % (tmpname, dstname))
shutil.move(tmpname, dstname)