mq: create patch header class to abstract header manipulation
authorBrendan Cully <brendan@kublai.com>
Sat, 22 Nov 2008 14:04:42 -0800
changeset 7399 e71bda2d2087
parent 7398 2cd1308cb588
child 7400 409a9b442308
mq: create patch header class to abstract header manipulation
hgext/mq.py
--- a/hgext/mq.py	Sun Nov 23 00:44:31 2008 -0800
+++ b/hgext/mq.py	Sat Nov 22 14:04:42 2008 -0800
@@ -56,6 +56,67 @@
     def __str__(self):
         return self.rev + ':' + self.name
 
+class patchheader(object):
+    def __init__(self, message, comments, user, date, haspatch):
+        self.message = message
+        self.comments = comments
+        self.user = user
+        self.date = date
+        self.haspatch = haspatch
+
+    def setuser(self, user):
+        if not self.setheader(['From: ', '# User '], user):
+            try:
+                patchheaderat = self.comments.index('# HG changeset patch')
+                self.comments.insert(patchheaderat + 1,'# User ' + user)
+            except ValueError:
+                self.comments = ['From: ' + user, ''] + self.comments
+        self.user = user
+
+    def setdate(self, date):
+        if self.setheader(['# Date '], date):
+            self.date = date
+
+    def setmessage(self, message):
+        if self.comments:
+            self._delmsg()
+        self.message = [message]
+        self.comments += self.message
+
+    def setheader(self, prefixes, new):
+        '''Update all references to a field in the patch header.
+        If none found, add it email style.'''
+        res = False
+        for prefix in prefixes:
+            for i in xrange(len(self.comments)):
+                if self.comments[i].startswith(prefix):
+                    self.comments[i] = prefix + new
+                    res = True
+                    break
+        return res
+
+    def __str__(self):
+        if not self.comments:
+            return ''
+        return '\n'.join(self.comments) + '\n\n'
+
+    def _delmsg(self):
+        '''Remove existing message, keeping the rest of the comments fields.
+        If comments contains 'subject: ', message will prepend
+        the field and a blank line.'''
+        if self.message:
+            subj = 'subject: ' + self.message[0].lower()
+            for i in xrange(len(self.comments)):
+                if subj == self.comments[i].lower():
+                    del self.comments[i]
+                    self.message = self.message[2:]
+                    break
+        ci = 0
+        for mi in xrange(len(self.message)):
+            while self.message[mi] != self.comments[ci]:
+                ci += 1
+            del self.comments[ci]
+
 class queue:
     def __init__(self, ui, path, patchdir=None):
         self.basepath = path
@@ -307,7 +368,7 @@
         if format and format.startswith("tag") and subject:
             message.insert(0, "")
             message.insert(0, subject)
-        return (message, comments, user, date, diffstart > 1)
+        return patchheader(message, comments, user, date, diffstart > 1)
 
     def removeundo(self, repo):
         undo = repo.sjoin('undo')
@@ -351,13 +412,13 @@
         if n == None:
             raise util.Abort(_("repo commit failed"))
         try:
-            message, comments, user, date, patchfound = mergeq.readheaders(patch)
+            ph = mergeq.readheaders(patch)
         except:
             raise util.Abort(_("unable to read %s") % patch)
 
         patchf = self.opener(patch, "w")
+        comments = str(ph)
         if comments:
-            comments = "\n".join(comments) + '\n\n'
             patchf.write(comments)
         self.printdiff(repo, head, n, fp=patchf)
         patchf.close()
@@ -477,12 +538,13 @@
             pf = os.path.join(patchdir, patchname)
 
             try:
-                message, comments, user, date, patchfound = self.readheaders(patchname)
+                ph = self.readheaders(patchname)
             except:
                 self.ui.warn(_("Unable to read %s\n") % patchname)
                 err = 1
                 break
 
+            message = ph.message
             if not message:
                 message = _("imported patch %s\n") % patchname
             else:
@@ -512,7 +574,7 @@
 
             files = patch.updatedir(self.ui, repo, files)
             match = cmdutil.matchfiles(repo, files or [])
-            n = repo.commit(files, message, user, date, match=match,
+            n = repo.commit(files, message, ph.user, ph.date, match=match,
                             force=True)
 
             if n == None:
@@ -522,7 +584,7 @@
                 self.applied.append(statusentry(revlog.hex(n), patchname))
 
             if patcherr:
-                if not patchfound:
+                if not ph.haspatch:
                     self.ui.warn(_("patch %s is empty\n") % patchname)
                     err = 0
                 else:
@@ -1015,6 +1077,8 @@
         if len(self.applied) == 0:
             self.ui.write(_("No patches applied\n"))
             return 1
+        msg = opts.get('msg', '').rstrip()
+        newuser = opts.get('user')
         newdate = opts.get('date')
         if newdate:
             newdate = '%d %d' % util.parsedate(newdate)
@@ -1027,7 +1091,7 @@
                 raise util.Abort(_("cannot refresh a revision with children"))
             cparents = repo.changelog.parents(top)
             patchparent = self.qparents(repo, top)
-            message, comments, user, date, patchfound = self.readheaders(patchfn)
+            ph = self.readheaders(patchfn)
 
             patchf = self.opener(patchfn, 'r+')
 
@@ -1037,59 +1101,18 @@
                     self.diffopts().git = True
                     break
 
-            msg = opts.get('msg', '').rstrip()
-            if msg and comments:
-                # Remove existing message, keeping the rest of the comments
-                # fields.
-                # If comments contains 'subject: ', message will prepend
-                # the field and a blank line.
-                if message:
-                    subj = 'subject: ' + message[0].lower()
-                    for i in xrange(len(comments)):
-                        if subj == comments[i].lower():
-                            del comments[i]
-                            message = message[2:]
-                            break
-                ci = 0
-                for mi in xrange(len(message)):
-                    while message[mi] != comments[ci]:
-                        ci += 1
-                    del comments[ci]
-
-            def setheaderfield(comments, prefixes, new):
-                # Update all references to a field in the patch header.
-                # If none found, add it email style.
-                res = False
-                for prefix in prefixes:
-                    for i in xrange(len(comments)):
-                        if comments[i].startswith(prefix):
-                            comments[i] = prefix + new
-                            res = True
-                            break
-                return res
-
-            newuser = opts.get('user')
+            if msg:
+                ph.setmessage(msg)
             if newuser:
-                if not setheaderfield(comments, ['From: ', '# User '], newuser):
-                    try:
-                        patchheaderat = comments.index('# HG changeset patch')
-                        comments.insert(patchheaderat + 1,'# User ' + newuser)
-                    except ValueError:
-                        comments = ['From: ' + newuser, ''] + comments
-                user = newuser
-
+                ph.setuser(newuser)
             if newdate:
-                if setheaderfield(comments, ['# Date '], newdate):
-                    date = newdate
-
-            if msg:
-                comments.append(msg)
+                ph.setdate(newdate)
 
             patchf.seek(0)
             patchf.truncate()
 
+            comments = str(ph)
             if comments:
-                comments = "\n".join(comments) + '\n\n'
                 patchf.write(comments)
 
             if opts.get('git'):
@@ -1204,22 +1227,21 @@
                     repo.dirstate.forget(f)
 
                 if not msg:
-                    if not message:
+                    if not ph.message:
                         message = "[mq]: %s\n" % patchfn
                     else:
-                        message = "\n".join(message)
+                        message = "\n".join(ph.message)
                 else:
                     message = msg
 
-                if not user:
-                    user = changes[1]
+                user = ph.user or changes[1]
 
                 self.applied.pop()
                 self.applied_dirty = 1
                 self.strip(repo, top, update=False,
                            backup='strip')
-                n = repo.commit(match.files(), message, user, date, match=match,
-                                force=1)
+                n = repo.commit(match.files(), message, user, ph.date,
+                                match=match, force=1)
                 self.applied.append(statusentry(revlog.hex(n), patchfn))
                 self.removeundo(repo)
             else:
@@ -1273,7 +1295,8 @@
                 summary=False):
         def displayname(patchname):
             if summary:
-                msg = self.readheaders(patchname)[0]
+                ph = self.readheaders(patchname)
+                msg = ph.message
                 msg = msg and ': ' + msg[0] or ': '
             else:
                 msg = ''
@@ -1828,8 +1851,8 @@
         if message:
             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
         patch = q.applied[-1].name
-        (message, comment, user, date, hasdiff) = q.readheaders(patch)
-        message = ui.edit('\n'.join(message), user or ui.username())
+        ph = q.readheaders(patch)
+        message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
     setupheaderopts(ui, opts)
     ret = q.refresh(repo, pats, msg=message, **opts)
     q.save_dirty()
@@ -1887,7 +1910,8 @@
 
     for p in patches:
         if not message:
-            messages.append(q.readheaders(p)[0])
+            ph = q.readheaders(p)
+            messages.append(ph.message)
         pf = q.join(p)
         (patchsuccess, files, fuzz) = q.patch(repo, pf)
         if not patchsuccess:
@@ -1895,7 +1919,8 @@
         patch.updatedir(ui, repo, files)
 
     if not message:
-        message, comments, user = q.readheaders(parent)[0:3]
+        ph = q.readheaders(parent)
+        message, user = ph.message, ph.user
         for msg in messages:
             message.append('* * *')
             message.extend(msg)
@@ -1978,9 +2003,9 @@
             ui.write('No patches applied\n')
             return 1
         patch = q.lookup('qtip')
-    message = repo.mq.readheaders(patch)[0]
+    ph = repo.mq.readheaders(patch)
 
-    ui.write('\n'.join(message) + '\n')
+    ui.write('\n'.join(ph.message) + '\n')
 
 def lastsavename(path):
     (directory, base) = os.path.split(path)