def makepatch(ui, repo, name=None, pats=[], opts={}): """sets up the call for attic.createpatch and makes the call""" s = repo.attic force = opts.get('force') if name and s.exists(name) and name != s.applied and not force: raise util.Abort(_('attempting to overwrite existing patch')) if name and s.applied and name != s.applied and not force: raise util.Abort(_('a different patch is active')) if not name: name = s.applied if not name: raise util.Abort(_('you need to supply a patch name')) date, user, message = None, None, '' if s.applied: data = patch.extract(ui, open(s.join(s.applied), 'r')) tmpname, message, user, date, branch, nodeid, p1, p2 = data os.unlink(tmpname) msg = cmdutil.logmessage(opts) if not msg: msg = message if opts.get('edit'): msg = ui.edit(msg, ui.username()) setupheaderopts(ui, opts) if opts.get('user'): user = opts['user'] if not user: user = ui.username() if opts.get('date'): date = opts['date'] if not date: date = util.makedate() date = util.parsedate(date) s.createpatch(repo, name, msg, user, date, pats, opts)
def makepatch(ui, repo, name=None, pats=[], opts={}): """sets up the call for attic.createpatch and makes the call""" s = repo.attic force = opts.get('force') if name and s.exists(name) and name != s.applied and not force: raise util.Abort(_('attempting to overwrite existing patch')) if name and s.applied and name != s.applied and not force: raise util.Abort(_('a different patch is active')) if not name: name = s.applied if not name: raise util.Abort(_('you need to supply a patch name')) date, user, message = None, None, '' if s.applied: data = patch.extract(ui, open(s.join(s.applied), 'r')) tmpname, message, user, date, branch, nodeid, p1, p2 = data os.unlink(tmpname) msg = cmdutil.logmessage(opts) if not msg: msg = message if opts.get('edit'): msg = ui.edit(msg, ui.username()) setupheaderopts(ui, opts) if opts.get('user'): user=opts['user'] if not user: user = ui.username() if opts.get('date'): date=opts['date'] if not date: date = util.makedate() date = util.parsedate(date) s.createpatch(repo, name, msg, user, date, pats, opts)
def apply(self, repo, patchfile, sim, force=False, **opts): """applies a patch and manages repo and attic state""" self.check_localchanges(repo, force) data = patch.extract(self.ui, open(self.join(patchfile), 'r')) tmpname, message, user, date, branch, nodeid, p1, p2 = data merge = False if repo.ui.configbool('attic', 'trymerge', default=True): try: if p1 and repo[p1]: merge = True except error.RepoError: pass if merge: success = self._applymerge(repo, patchfile, sim, patchfile, p1, force=force, **opts) else: success = self._applypatch(repo, patchfile, sim, force=force, **opts) os.unlink(tmpname) if success: if opts.get('reverse'): self.applied = '' self.currentpatch = '' else: self.applied = patchfile self.currentpatch = patchfile self.persiststate() return success
def headerinfo(ui, repo, name): name = repo.attic.join(name) data = patch.extract(ui, open(name, 'r')) tmpname, message, user, date, branch, nodeid, p1, p2 = data os.unlink(tmpname) if not isinstance(message, str): message = '\n'.join(message) if not message or message.strip() == '': message = 'None\n' else: message = '\n' + message ui.write(_('user: %s\ndate: %s\nparent: %s\nmessage: %s') % (user, date, p1, message))
def detectPatches(self, paths): filepaths = [] for p in paths: if not os.path.isfile(p): continue try: pf = open(p, 'rb') filename, message, user, date, branch, node, p1, p2 = \ patch.extract(self.repo.ui, pf) if filename: filepaths.append(p) os.unlink(filename) except Exception, e: pass
def commitwrapper(orig, ui, repo, *args, **opts): s = repo.attic name = s.applied if not name: orig(ui, repo, *args, **opts) else: makepatch(ui, repo, name, [], opts) data = patch.extract(ui, open(s.join(name), 'r')) tmpname, m, u, d, branch, nodeid, p1, p2 = data os.unlink(tmpname) orig(ui, repo, message=m, logfile=None, user=u, date=d) if not opts.get('keep'): s.remove(name) s.resetdefault()
def commitwrapper(orig, ui, repo, *args, **opts): s = repo.attic name = s.applied if not name: orig(ui, repo, *args, **opts) else: makepatch(ui, repo, name, [], opts) data = patch.extract(ui, open(s.join(name), 'r')) tmpname, m, u, d, branch, nodeid, p1, p2 = data os.unlink(tmpname) orig(ui, repo, message = m, logfile = None, user = u, date = d) if not opts.get('keep'): s.remove(name) s.resetdefault()
def __init__(self, patchpath, repo, patchHandle=None): """ Read patch context from file :param patchHandle: If set, then the patch is a temporary. The provided handle is used to read the patch and the patchpath contains the name of the patch. The handle is NOT closed. """ self._path = patchpath self._patchname = os.path.basename(patchpath) self._repo = repo if patchHandle: pf = patchHandle pf_start_pos = pf.tell() else: pf = open(patchpath) try: data = patch.extract(self._repo.ui, pf) tmpfile, msg, user, date, branch, node, p1, p2 = data if tmpfile: os.unlink(tmpfile) finally: if patchHandle: pf.seek(pf_start_pos) else: pf.close() if not msg and hasattr(repo, "mq"): # attempt to get commit message from hgext import mq msg = mq.patchheader(repo.mq.join(self._patchname)).message if msg: msg = "\n".join(msg) self._node = node self._user = user and hglib.toutf(user) or "" self._date = date and util.parsedate(date) or None self._desc = msg and msg or "" self._branch = branch and hglib.toutf(branch) or "" self._parents = [] for p in (p1, p2): if not p: continue try: self._parents.append(repo[p]) except (error.LookupError, error.RepoLookupError, error.RepoError): self._parents.append(p)
def __init__(self, bug, node): Attachment.__init__(self, bug, node) self.flags = list(sorted(Flag(bug, n) for n in node.findall('flag'))) rawtext = base64.b64decode(node.find('data').text) filename, message, user, date, branch, nodeid, p1, p2 = \ patch.extract(bug.settings.ui, StringIO.StringIO(rawtext)) # for some reason, patch.extract writes a temporary file with the diff hunks if filename: fp = file(filename) try: # BugZilla is not explicit about patch encoding. We need to check it's utf-8. # utf-8: convert from 8-bit encoding to internal (16/32-bit) Unicode. self.data = fp.read().decode('utf-8') except UnicodeDecodeError: # Ftr, this could be due to the (included) message part: see later message block. bug.settings.ui.warn("Patch id=%s desc=\"%s\" diff data were discarded:\n" % (self.id, self.desc)) # Print the exception without its traceback. sys.excepthook(sys.exc_info()[0], sys.exc_info()[1], None) # Can't do better than discard data: # trying |.decode('utf-8', 'replace')| as a fallback would be too risky # if user imports the result then forgets to fix it. self.data = '' fp.close() os.remove(filename) else: self.data = '' # Remove seconds (which are always ':00') and timezone from the patch date: # keep 'yyyy-mm-dd hh:mn' only. self.date = date or node.find('date').text[:16] if user: try: # See previous self.data block about utf-8 handling. self.author = user.decode('utf-8') except UnicodeDecodeError: bug.settings.ui.warn("Patch id=%s desc=\"%s\" user data were discarded:\n" % (self.id, self.desc)) sys.excepthook(sys.exc_info()[0], sys.exc_info()[1], None) user = None if not user: # Bugzilla v3.4.1+: "Email Addresses Hidden From Logged-Out Users" patchAttacherEmail = node.find('attacher').text # 'patchAttacherEmail' may not be enough, compare date too to be as precise as possible... posts = [p for p in self.bug.comments if p.date == self.date and p.who_email == patchAttacherEmail] who = posts[0].who for p in posts: if p.who == who: continue print "Warning: could not figure out exact author (multiple names for same date and email address)!" who = "" break # Email domain may need to be retrieved/added manually... self.author = "%s <%s>" % ( # Scrub the :cruft and any '[...]' or '(...)' too from the username. re.sub("\[.*?\]|\(.*?\)|:\S+", "", who).strip(), patchAttacherEmail) self.commit_message = None # (Mercurial v1.4.3(-!?)) "No message" is extracted as '\n' :-/ # Want to strip the message anyway. if message: try: # See previous self.data block about utf-8 handling. self.commit_message = message.decode('utf-8') \ .strip() except UnicodeDecodeError: bug.settings.ui.warn("Patch id=%s desc=\"%s\" message data were discarded too:\n" % (self.id, self.desc)) sys.excepthook(sys.exc_info()[0], sys.exc_info()[1], None) message = None if not self.commit_message: self.commit_message = self.bug.settings.msg_format % self.metadata
def __init__(self, bug, node): Attachment.__init__(self, bug, node) self.flags = list(sorted(Flag(bug, n) for n in node.findall('flag'))) rawtext = base64.b64decode(node.find('data').text) data = patch.extract(bug.settings.ui, StringIO.StringIO(rawtext)) # Mercurial 3.6 returns a dict. Previous versions a tuple. if isinstance(data, dict): filename = data.get('filename') message = data.get('message') user = data.get('user') date = data.get('date') branch = data.get('branch') nodeid = data.get('node') p1 = data.get('p1') p2 = data.get('p2') else: assert isinstance(data, tuple) filename, message, user, date, branch, nodeid, p1, p2 = data # for some reason, patch.extract writes a temporary file with the diff hunks if filename: fp = file(filename) try: # BugZilla is not explicit about patch encoding. We need to check it's utf-8. # utf-8: convert from 8-bit encoding to internal (16/32-bit) Unicode. self.data = fp.read().decode('utf-8') # Attempt to detect the start of the diff. Borrowed from: # http://selenic.com/hg/file/79e5de2bfa8c/mercurial/patch.py#l163 diffre = re.compile( r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' r'retrieving revision [0-9]+(\.[0-9]+)*$|' r'---[ \t].*?^\+\+\+[ \t]|' r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE | re.DOTALL) m = diffre.search(self.data) if m: # Remove the patch header, since we'll be re-adding a cleaned up version later. self.data = self.data[m.start(0):] except UnicodeDecodeError: # Ftr, this could be due to the (included) message part: see later message block. bug.settings.ui.warn( "Patch id=%s desc=\"%s\" diff data were discarded:\n" % (self.id, self.desc)) # Print the exception without its traceback. sys.excepthook(sys.exc_info()[0], sys.exc_info()[1], None) # Can't do better than discard data: # trying |.decode('utf-8', 'replace')| as a fallback would be too risky # if user imports the result then forgets to fix it. self.data = '' fp.close() os.remove(filename) else: self.data = '' # Remove seconds (which are always ':00') and timezone from the patch date: # keep 'yyyy-mm-dd hh:mn' only. self.date = date or node.find('date').text[:16] if user: try: # See previous self.data block about utf-8 handling. self.author = user.decode('utf-8') except UnicodeDecodeError: bug.settings.ui.warn( "Patch id=%s desc=\"%s\" user data were discarded:\n" % (self.id, self.desc)) sys.excepthook(sys.exc_info()[0], sys.exc_info()[1], None) user = None if not user: # Bugzilla v3.4.1+: "Email Addresses Hidden From Logged-Out Users" patchAttacherEmail = node.find('attacher').text # 'patchAttacherEmail' may not be enough, compare date too to be as precise as possible... posts = [ p for p in self.bug.comments if p.date == self.date and p.who_email == patchAttacherEmail ] who = posts[0].who for p in posts: if p.who == who: continue print "Warning: could not figure out exact author (multiple names for same date and email address)!" who = "" break # Email domain may need to be retrieved/added manually... self.author = "%s <%s>" % ( # Scrub the :cruft and any '[...]' or '(...)' too from the username. re.sub("\[.*?\]|\(.*?\)|:\S+", "", who).strip(), patchAttacherEmail) self.commit_message = None # (Mercurial v1.4.3(-!?)) "No message" is extracted as '\n' :-/ # Want to strip the message anyway. if message: try: # See previous self.data block about utf-8 handling. self.commit_message = message.decode('utf-8').strip() except UnicodeDecodeError: bug.settings.ui.warn( "Patch id=%s desc=\"%s\" message data were discarded too:\n" % (self.id, self.desc)) sys.excepthook(sys.exc_info()[0], sys.exc_info()[1], None) message = None if not self.commit_message: self.commit_message = self.bug.settings.msg_format % self.metadata