[PATCH 6 of 6] record: use new mempatching system for simplicity and safety
Augie Fackler
durin42 at gmail.com
Mon Apr 19 18:16:39 CDT 2010
# HG changeset patch
# User Augie Fackler <durin42 at gmail.com>
# Date 1271716506 18000
# Node ID 6566509b770ecf361379d618da0807cb852d0290
# Parent 9b8a41043ab3634a237292cf6de53dd967f2ca2f
record: use new mempatching system for simplicity and safety
diff --git a/hgext/record.py b/hgext/record.py
--- a/hgext/record.py
+++ b/hgext/record.py
@@ -9,7 +9,7 @@
from mercurial.i18n import gettext, _
from mercurial import cmdutil, commands, extensions, hg, mdiff, patch
-from mercurial import util
+from mercurial import util, context, lock, node
import copy, cStringIO, errno, operator, os, re, tempfile
lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
@@ -382,30 +382,59 @@
? - display help'''
- dorecord(ui, repo, commands.commit, *pats, **opts)
+ dorecord(ui, repo, *pats, **opts)
-def qrecord(ui, repo, patch, *pats, **opts):
+# TODO this has some commonality with hgext mq.queue.qimport - refactor
+# The big issue is that qimport enforces the imported rev must be qparent
+# if patches are already applied, and we're always importing the child
+# of qtip.
+def qrecord(ui, repo, patchname, *pats, **opts):
'''interactively record a new patch
See 'hg help qnew' & 'hg help record' for more information and
usage.
'''
-
try:
mq = extensions.find('mq')
except KeyError:
raise util.Abort(_("'mq' extension not loaded"))
- def committomq(ui, repo, *pats, **opts):
- mq.new(ui, repo, patch, *pats, **opts)
+ opts = opts.copy()
+ repo.mq.check_toppatch(repo)
+ if len(repo[None].parents()) > 1:
+ raise util.Abort(_('cannot manage merge changesets'))
+ r = repo['tip']
+ if not patchname:
+ patchname = mq.normname('%d.diff' % r.rev()+1)
- opts = opts.copy()
- opts['force'] = True # always 'qnew -f'
- dorecord(ui, repo, committomq, *pats, **opts)
+ repo.mq.check_reserved_name(patchname)
+ if patchname in repo.mq.series:
+ raise util.Abort(_('patch %s already in the series file') % patchname)
+ if os.path.exists(repo.mq.join(patchname)):
+ raise util.Abort(_('patch "%s" alread exists') % patchname)
+ dorecord(ui, repo, *pats, **opts)
-def dorecord(ui, repo, commitfunc, *pats, **opts):
+ r = repo['tip']
+ diffopts = repo.mq.diffopts({'git': opts['git']})
+ repo.mq.full_series.insert(repo.mq.full_series_end(), patchname)
+ patchf = repo.mq.opener(patchname, 'w')
+ cmdutil.export(repo, [r.node()], fp=patchf, opts=diffopts)
+ patchf.close()
+ se = mq.statusentry(r.node(), patchname)
+ repo.mq.applied.append(se)
+ repo.mq.parse_series()
+ repo.mq.applied_dirty = 1
+ repo.mq.series_dirty = 1
+ repo.mq.save_dirty()
+ qrepo = repo.mq.qrepo()
+ if qrepo:
+ qrepo.add(patchname)
+ repo.invalidatecaches()
+
+
+def dorecord(ui, repo, *pats, **opts):
if not ui.interactive():
raise util.Abort(_('running non-interactively, use commit instead'))
@@ -449,77 +478,46 @@
modified = set(changes[0])
- # 2. backup changed files, so we can restore them in the end
- backups = {}
- backupdir = repo.join('record-backups')
+ fp = cStringIO.StringIO()
+ for c in chunks:
+ c.write(fp)
+ dopatch = fp.tell()
+ fp.seek(0)
+
+ if dopatch:
+ try:
+ ui.debug('applying patch\n')
+ ui.debug(fp.getvalue())
+ patcher = patch.memapplydiff(
+ ui, fp, repo[None].parents()[0], 1, eolmode=None)
+ except patch.PatchError, err:
+ s = str(err)
+ if s:
+ raise util.Abort(s)
+ else:
+ raise util.Abort(_('patch failed to apply'))
+ del fp
+ message = cmdutil.logmessage(opts)
+ if not message.strip():
+ raise util.Abort(_('empty commit message'))
+ ctx = context.memctx(repo, repo.dirstate.parents(),
+ message,
+ patcher.files.keys(),
+ patcher.filectxfn,
+ opts.get('user'), opts.get('date'),
+ repo[None].extra())
+ l = repo.wlock()
try:
- os.mkdir(backupdir)
- except OSError, err:
- if err.errno != errno.EEXIST:
- raise
- try:
- # backup continues
- for f in newfiles:
- if f not in modified:
- continue
- fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
- dir=backupdir)
- os.close(fd)
- ui.debug('backup %r as %r\n' % (f, tmpname))
- util.copyfile(repo.wjoin(f), tmpname)
- backups[f] = tmpname
-
- fp = cStringIO.StringIO()
- for c in chunks:
- if c.filename() in backups:
- c.write(fp)
- dopatch = fp.tell()
- fp.seek(0)
-
- # 3a. apply filtered patch to clean repo (clean)
- if backups:
- hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
-
- # 3b. (apply)
- if dopatch:
- try:
- ui.debug('applying patch\n')
- ui.debug(fp.getvalue())
- pfiles = {}
- patch.internalpatch(fp, ui, 1, repo.root, files=pfiles,
- eolmode=None)
- patch.updatedir(ui, repo, pfiles)
- except patch.PatchError, err:
- s = str(err)
- if s:
- raise util.Abort(s)
- else:
- raise util.Abort(_('patch failed to apply'))
- del fp
-
- # 4. We prepared working directory according to filtered patch.
- # Now is the time to delegate the job to commit/qrefresh or the like!
-
- # it is important to first chdir to repo root -- we'll call a
- # highlevel command with list of pathnames relative to repo root
- cwd = os.getcwd()
- os.chdir(repo.root)
- try:
- commitfunc(ui, repo, *newfiles, **opts)
- finally:
- os.chdir(cwd)
-
- return 0
+ n = repo.commitctx(ctx)
+ repo.dirstate.setparents(n)
+ for f, action in patcher.files.iteritems():
+ if action is None:
+ repo.dirstate.forget(f)
+ else:
+ repo.dirstate.normallookup(f)
finally:
- # 5. finally restore backed-up files
- try:
- for realname, tmpname in backups.iteritems():
- ui.debug('restoring %r to %r\n' % (tmpname, realname))
- util.copyfile(tmpname, repo.wjoin(realname))
- os.unlink(tmpname)
- os.rmdir(backupdir)
- except OSError:
- pass
+ l.release()
+ return 0
# wrap ui.write so diff output can be labeled/colorized
def wrapwrite(orig, *args, **kw):
@@ -561,4 +559,3 @@
}
cmdtable.update(qcmdtable)
-
More information about the Mercurial-devel
mailing list