[PATCH 3 of 3] import: add --no-update option
Matt Mackall
mpm at selenic.com
Sat Jun 11 19:23:30 UTC 2011
On Sat, 2011-06-11 at 15:20 +0200, Patrick Mezard wrote:
> # HG changeset patch
> # User Patrick Mezard <pmezard at gmail.com>
> # Date 1307796661 -7200
> # Node ID 984577891b4661199808f923e184a4101fc7334e
> # Parent 231c722c1d8db6a9181a22e41272320114f87093
> import: add --no-update option
The first two patches look fine, but I can't say I'm happy with this
flag.
> This feature is more a way to test patching without a working directory than
> something people ask about. Adding a --rev option to specify the parent patch
> revision would make it a little more useful.
If we -know- we're going to introduce a --rev -like option eventually,
let's just introduce one option that combines both. ie:
--parent REV apply patch at REV, bypassing working copy
> What this change introduces is patch.repobackend class which let patches be
> applied against repository revisions. The caller must supply a filestore object
> to receive patched content, which can be turned into a memctx with
> patch.makememctx() helper.
>
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -3001,6 +3001,8 @@
> ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
> ('', 'no-commit', None,
> _("don't commit, just update the working directory")),
> + ('', 'no-update', None,
> + _("do not update the working directory")),
> ('', 'exact', None,
> _('apply patch to the nodes from which it was generated')),
> ('', 'import-branch', None,
> @@ -3049,14 +3051,19 @@
> if date:
> opts['date'] = util.parsedate(date)
>
> + update = not opts.get('no_update')
> + if not update and opts.get('no_commit'):
> + raise util.Abort(_('cannot use --no-commit with --no-update'))
> try:
> sim = float(opts.get('similarity') or 0)
> except ValueError:
> raise util.Abort(_('similarity must be a number'))
> if sim < 0 or sim > 100:
> raise util.Abort(_('similarity must be between 0 and 100'))
> -
> - if opts.get('exact') or not opts.get('force'):
> + if sim and not update:
> + raise util.Abort(_('cannot use --similarity with --no-update'))
> +
> + if (opts.get('exact') or not opts.get('force')) and update:
> cmdutil.bailifchanged(repo)
>
> d = opts["base"]
> @@ -3064,6 +3071,11 @@
> wlock = lock = None
> msgs = []
>
> + def checkexact(repo, n, nodeid):
> + if opts.get('exact') and hex(n) != nodeid:
> + repo.rollback()
> + raise util.Abort(_('patch is damaged or loses information'))
> +
> def tryone(ui, hunk):
> tmpname, message, user, date, branch, nodeid, p1, p2 = \
> patch.extract(ui, hunk)
> @@ -3102,40 +3114,60 @@
> else:
> p1, p2 = wp
>
> - if opts.get('exact') and p1 != wp[0]:
> - hg.clean(repo, p1.node())
> - if p1 != wp[0] and p2 != wp[1]:
> - repo.dirstate.setparents(p1.node(), p2.node())
> -
> - if opts.get('exact') or opts.get('import_branch'):
> - repo.dirstate.setbranch(branch or 'default')
> -
> - files = set()
> - patch.patch(ui, repo, tmpname, strip=strip, files=files,
> - eolmode=None, similarity=sim / 100.0)
> - files = list(files)
> - if opts.get('no_commit'):
> - if message:
> - msgs.append(message)
> + n = None
> + if update:
> + if opts.get('exact') and p1 != wp[0]:
> + hg.clean(repo, p1.node())
> + if p1 != wp[0] and p2 != wp[1]:
> + repo.dirstate.setparents(p1.node(), p2.node())
> +
> + if opts.get('exact') or opts.get('import_branch'):
> + repo.dirstate.setbranch(branch or 'default')
> +
> + files = set()
> + patch.patch(ui, repo, tmpname, strip=strip, files=files,
> + eolmode=None, similarity=sim / 100.0)
> + files = list(files)
> + if opts.get('no_commit'):
> + if message:
> + msgs.append(message)
> + else:
> + if opts.get('exact'):
> + m = None
> + else:
> + m = scmutil.matchfiles(repo, files or [])
> + n = repo.commit(message, opts.get('user') or user,
> + opts.get('date') or date, match=m,
> + editor=cmdutil.commiteditor)
> + checkexact(repo, n, nodeid)
> + # Force a dirstate write so that the next transaction
> + # backups an up-to-date file.
> + repo.dirstate.write()
> else:
> - if opts.get('exact'):
> - m = None
> + if opts.get('exact') or opts.get('import_branch'):
> + branch = branch or 'default'
> else:
> - m = scmutil.matchfiles(repo, files or [])
> - n = repo.commit(message, opts.get('user') or user,
> - opts.get('date') or date, match=m,
> - editor=cmdutil.commiteditor)
> - if opts.get('exact'):
> - if hex(n) != nodeid:
> - repo.rollback()
> - raise util.Abort(_('patch is damaged'
> - ' or loses information'))
> - # Force a dirstate write so that the next transaction
> - # backups an up-do-date file.
> - repo.dirstate.write()
> - if n:
> - commitid = short(n)
> -
> + branch = p1.branch()
> + store = patch.filestore()
> + try:
> + files = set()
> + try:
> + patch.patchrepo(ui, repo, p1, store, tmpname, strip, files,
> + eolmode=None)
> + except patch.PatchError, e:
> + raise util.Abort(str(e))
> + memctx = patch.makememctx(repo, (p1.node(), p2.node()), message,
> + opts.get('user') or user,
> + opts.get('date') or date,
> + branch, files, store,
> + editor=cmdutil.commiteditor)
> + repo.savecommitmessage(memctx.description())
> + n = memctx.commit()
> + checkexact(repo, n, nodeid)
> + finally:
> + store.close()
> + if n:
> + commitid = short(n)
> return commitid
> finally:
> os.unlink(tmpname)
> diff --git a/mercurial/patch.py b/mercurial/patch.py
> --- a/mercurial/patch.py
> +++ b/mercurial/patch.py
> @@ -11,7 +11,8 @@
>
> from i18n import _
> from node import hex, nullid, short
> -import base85, mdiff, scmutil, util, diffhelpers, copies, encoding
> +import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, error
> +import context
>
> gitre = re.compile('diff --git a/(.*) b/(.*)')
>
> @@ -511,6 +512,48 @@
> if self.opener:
> shutil.rmtree(self.opener.base)
>
> +class repobackend(abstractbackend):
> + def __init__(self, ui, repo, ctx, store):
> + super(repobackend, self).__init__(ui)
> + self.repo = repo
> + self.ctx = ctx
> + self.store = store
> + self.changed = set()
> + self.removed = set()
> + self.copied = {}
> +
> + def _checkknown(self, fname):
> + if fname not in self.ctx:
> + raise PatchError(_('cannot patch %s: file is not tracked') % fname)
> +
> + def getfile(self, fname):
> + try:
> + fctx = self.ctx[fname]
> + except error.LookupError:
> + raise IOError()
> + flags = fctx.flags()
> + return fctx.data(), ('l' in flags, 'x' in flags)
> +
> + def setfile(self, fname, data, mode, copysource):
> + if copysource:
> + self._checkknown(copysource)
> + if data is None:
> + data = self.ctx[fname].data()
> + self.store.setfile(fname, data, mode, copysource)
> + self.changed.add(fname)
> + if copysource:
> + self.copied[fname] = copysource
> +
> + def unlink(self, fname):
> + self._checkknown(fname)
> + self.removed.add(fname)
> +
> + def exists(self, fname):
> + return fname in self.ctx
> +
> + def close(self):
> + return self.changed | self.removed
> +
> # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
> unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
> contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
> @@ -1332,11 +1375,7 @@
> util.explainexit(code)[0])
> return fuzz
>
> -def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict',
> - similarity=0):
> - """use builtin patch to apply <patchobj> to the working directory.
> - returns whether patch was applied with fuzz factor."""
> -
> +def patchbackend(ui, backend, patchobj, strip, files=None, eolmode='strict'):
> if files is None:
> files = set()
> if eolmode is None:
> @@ -1346,7 +1385,6 @@
> eolmode = eolmode.lower()
>
> store = filestore()
> - backend = workingbackend(ui, repo, similarity)
> try:
> fp = open(patchobj, 'rb')
> except TypeError:
> @@ -1363,6 +1401,33 @@
> raise PatchError(_('patch failed to apply'))
> return ret > 0
>
> +def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict',
> + similarity=0):
> + """use builtin patch to apply <patchobj> to the working directory.
> + returns whether patch was applied with fuzz factor."""
> + backend = workingbackend(ui, repo, similarity)
> + return patchbackend(ui, backend, patchobj, strip, files, eolmode)
> +
> +def patchrepo(ui, repo, ctx, store, patchobj, strip, files=None,
> + eolmode='strict'):
> + backend = repobackend(ui, repo, ctx, store)
> + return patchbackend(ui, backend, patchobj, strip, files, eolmode)
> +
> +def makememctx(repo, parents, text, user, date, branch, files, store,
> + editor=None):
> + def getfilectx(repo, memctx, path):
> + data, (islink, isexec), copied = store.getfile(path)
> + return context.memfilectx(path, data, islink=islink, isexec=isexec,
> + copied=copied)
> + extra = {}
> + if branch:
> + extra['branch'] = encoding.fromlocal(branch)
> + ctx = context.memctx(repo, parents, text, files, getfilectx, user,
> + date, extra)
> + if editor:
> + ctx._text = editor(repo, ctx, [])
> + return ctx
> +
> def patch(ui, repo, patchname, strip=1, files=None, eolmode='strict',
> similarity=0):
> """Apply <patchname> to the working directory.
> diff --git a/tests/test-debugcomplete.t b/tests/test-debugcomplete.t
> --- a/tests/test-debugcomplete.t
> +++ b/tests/test-debugcomplete.t
> @@ -245,7 +245,7 @@
> heads: rev, topo, active, closed, style, template
> help: extension, command
> identify: rev, num, id, branch, tags, bookmarks
> - import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
> + import: strip, base, force, no-commit, no-update, exact, import-branch, message, logfile, date, user, similarity
> incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
> locate: rev, print0, fullpath, include, exclude
> manifest: rev, all
> diff --git a/tests/test-import-noupdate.t b/tests/test-import-noupdate.t
> new file mode 100644
> --- /dev/null
> +++ b/tests/test-import-noupdate.t
> @@ -0,0 +1,182 @@
> + $ echo "[extensions]" >> $HGRCPATH
> + $ echo "purge=" >> $HGRCPATH
> +
> + $ shortlog() {
> + > hg log --template '{rev}:{node|short} {author} {date|hgdate} - {branch} - {desc|firstline}\n'
> + > }
> +
> +Test --noupdate with other options
> +
> + $ hg init repo-options
> + $ cd repo-options
> + $ echo a > a
> + $ hg ci -Am adda
> + adding a
> + $ echo a >> a
> + $ hg branch foo
> + marked working directory as branch foo
> + $ hg ci -Am changea
> + $ hg export . > ../test.diff
> + $ hg up null
> + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
> +
> +Test importing an existing revision
> +
> + $ hg import --no-update --exact ../test.diff
> + applying ../test.diff
> + $ shortlog
> + 1:4e322f7ce8e3 test 0 0 - foo - changea
> + 0:07f494440405 test 0 0 - default - adda
> +
> +Test failure without --exact
> +
> + $ hg import --no-update ../test.diff
> + applying ../test.diff
> + unable to find 'a' for patching
> + abort: patch failed to apply
> + [255]
> + $ hg st
> + $ shortlog
> + 1:4e322f7ce8e3 test 0 0 - foo - changea
> + 0:07f494440405 test 0 0 - default - adda
> +
> +Test --user, --date and --message
> +
> + $ hg up 0
> + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> + $ hg import --no-update --u test2 -d '1 0' -m patch2 ../test.diff
> + applying ../test.diff
> + $ cat .hg/last-message.txt
> + patch2 (no-eol)
> + $ shortlog
> + 2:2e127d1da504 test2 1 0 - default - patch2
> + 1:4e322f7ce8e3 test 0 0 - foo - changea
> + 0:07f494440405 test 0 0 - default - adda
> + $ hg rollback
> + repository tip rolled back to revision 1 (undo commit)
> + working directory now based on revision 0
> +
> +Test --import-branch
> +
> + $ hg import --no-update --import-branch ../test.diff
> + applying ../test.diff
> + $ shortlog
> + 1:4e322f7ce8e3 test 0 0 - foo - changea
> + 0:07f494440405 test 0 0 - default - adda
> + $ hg rollback
> + repository tip rolled back to revision 1 (undo commit)
> + working directory now based on revision 0
> +
> +Test --strip
> +
> + $ hg import --no-update --strip 0 - <<EOF
> + > # HG changeset patch
> + > # User test
> + > # Date 0 0
> + > # Branch foo
> + > # Node ID 4e322f7ce8e3e4203950eac9ece27bf7e45ffa6c
> + > # Parent 07f4944404050f47db2e5c5071e0e84e7a27bba9
> + > changea
> + >
> + > diff -r 07f494440405 -r 4e322f7ce8e3 a
> + > --- a Thu Jan 01 00:00:00 1970 +0000
> + > +++ a Thu Jan 01 00:00:00 1970 +0000
> + > @@ -1,1 +1,2 @@
> + > a
> + > +a
> + > EOF
> + applying patch from stdin
> + $ hg rollback
> + repository tip rolled back to revision 1 (undo commit)
> + working directory now based on revision 0
> +
> +Test unsupported combinations
> +
> + $ hg import --no-update --no-commit ../test.diff
> + abort: cannot use --no-commit with --no-update
> + [255]
> + $ hg import --no-update --similarity 50 ../test.diff
> + abort: cannot use --similarity with --no-update
> + [255]
> +
> +Test commit editor
> +
> + $ hg diff -c 1 > ../test.diff
> + $ HGEDITOR=cat hg import --no-update ../test.diff
> + applying ../test.diff
> +
> +
> + HG: Enter commit message. Lines beginning with 'HG:' are removed.
> + HG: Leave message empty to abort commit.
> + HG: --
> + HG: user: test
> + HG: branch 'default'
> + HG: changed a
> + abort: empty commit message
> + [255]
> +
> +Test patch.eol is handled
> +
> + $ python -c 'file("a", "wb").write("a\r\n")'
> + $ hg ci -m makeacrlf
> + $ hg import -m 'should fail because of eol' --no-update ../test.diff
> + applying ../test.diff
> + patching file a
> + Hunk #1 FAILED at 0
> + abort: patch failed to apply
> + [255]
> + $ hg --config patch.eol=auto import -d '0 0' -m 'test patch.eol' --no-update ../test.diff
> + applying ../test.diff
> + $ shortlog
> + 3:d7805b4d2cb3 test 0 0 - default - test patch.eol
> + 2:872023de769d test 0 0 - default - makeacrlf
> + 1:4e322f7ce8e3 test 0 0 - foo - changea
> + 0:07f494440405 test 0 0 - default - adda
> +
> + $ cd ..
> +
> +Test complicated patch with --exact
> +
> + $ hg init repo-exact
> + $ cd repo-exact
> + $ echo a > a
> + $ echo c > c
> + $ echo d > d
> + $ echo e > e
> + $ echo f > f
> + $ chmod +x f
> + $ ln -s c linkc
> + $ hg ci -Am t
> + adding a
> + adding c
> + adding d
> + adding e
> + adding f
> + adding linkc
> + $ hg cp a aa1
> + $ echo b >> a
> + $ echo b > b
> + $ hg add b
> + $ hg cp a aa2
> + $ echo aa >> aa2
> + $ chmod +x e
> + $ chmod -x f
> + $ ln -s a linka
> + $ hg rm d
> + $ hg rm linkc
> + $ hg mv c cc
> + $ hg ci -m patch
> + $ hg export --git . > ../test.diff
> + $ hg up -C null
> + 0 files updated, 0 files merged, 7 files removed, 0 files unresolved
> + $ hg purge
> + $ hg st
> + $ hg import --no-update --exact ../test.diff
> + applying ../test.diff
> +
> +The patch should have matched the exported revision and generated no additional
> +data. If not, diff both heads to debug it.
> +
> + $ shortlog
> + 1:2978fd5c8aa4 test 0 0 - default - patch
> + 0:a0e19e636a43 test 0 0 - default - t
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
--
Mathematics is the supreme nostalgia of our time.
More information about the Mercurial-devel
mailing list