Example #1
0
def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
    'Commit the changes and store useful information in extra'
    try:
        repo.setparents(repo[p1].node(), repo[p2].node())
        ctx = repo[rev]
        if commitmsg is None:
            commitmsg = ctx.description()
        extra = {'rebase_source': ctx.hex()}
        if extrafn:
            extrafn(ctx, extra)
        # Commit might fail if unresolved files exist
        newrev = repo.commit(text=commitmsg,
                             user=ctx.user(),
                             date=ctx.date(),
                             extra=extra,
                             editor=editor)
        repo.dirstate.setbranch(repo[newrev].branch())
        targetphase = max(ctx.phase(), phases.draft)
        # retractboundary doesn't overwrite upper phase inherited from parent
        newnode = repo[newrev].node()
        if newnode:
            phases.retractboundary(repo, targetphase, [newnode])
        return newrev
    except util.Abort:
        # Invalidate the previous setparents
        repo.dirstate.invalidate()
        raise
 def adjustphase(repo, tr, phase, node):
     # transaction argument added in Mercurial 3.2.
     try:
         phases.advanceboundary(repo, tr, phase, [node])
         phases.retractboundary(repo, tr, phase, [node])
     except TypeError:
         phases.advanceboundary(repo, phase, [node])
         phases.retractboundary(repo, phase, [node])
Example #3
0
 def adjustphase(repo, tr, phase, node):
     # transaction argument added in Mercurial 3.2.
     try:
         phases.advanceboundary(repo, tr, phase, [node])
         phases.retractboundary(repo, tr, phase, [node])
     except TypeError:
         phases.advanceboundary(repo, phase, [node])
         phases.retractboundary(repo, phase, [node])
Example #4
0
def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
    'Commit the changes and store useful information in extra'
    try:
        repo.setparents(repo[p1].node(), repo[p2].node())
        ctx = repo[rev]
        if commitmsg is None:
            commitmsg = ctx.description()
        extra = {'rebase_source': ctx.hex()}
        if extrafn:
            extrafn(ctx, extra)
        # Commit might fail if unresolved files exist
        newrev = repo.commit(text=commitmsg, user=ctx.user(),
                             date=ctx.date(), extra=extra, editor=editor)
        repo.dirstate.setbranch(repo[newrev].branch())
        targetphase = max(ctx.phase(), phases.draft)
        # retractboundary doesn't overwrite upper phase inherited from parent
        newnode = repo[newrev].node()
        if newnode:
            phases.retractboundary(repo, targetphase, [newnode])
        return newrev
    except util.Abort:
        # Invalidate the previous setparents
        repo.dirstate.invalidate()
        raise
Example #5
0
def _pullbundle(repo, rev):
    """Find the given rev in a backup bundle and pull it back into the
    repository.
    """
    other, rev = _findbundle(repo, rev)
    if not other:
        raise error.Abort("could not find '%s' in the repo or the backup"
                         " bundles" % rev)
    lock = repo.lock()
    try:
        oldtip = len(repo)
        exchange.pull(repo, other, heads=[rev])

        tr = repo.transaction("phase")
        nodes = (c.node() for c in repo.set('%d:', oldtip))
        phases.retractboundary(repo, tr, 1, nodes)
        tr.close()
    finally:
        lock.release()

    if rev not in repo:
        raise error.Abort("unable to get rev %s from repo" % rev)

    return repo[rev]
Example #6
0
    def putcommit(self, files, copies, parents, commit, source, revmap, full,
                  cleanp2):
        files = dict(files)

        def getfilectx(repo, memctx, f):
            if p2ctx and f in p2files and f not in copies:
                self.ui.debug('reusing %s from p2\n' % f)
                try:
                    return p2ctx[f]
                except error.ManifestLookupError:
                    # If the file doesn't exist in p2, then we're syncing a
                    # delete, so just return None.
                    return None
            try:
                v = files[f]
            except KeyError:
                return None
            data, mode = source.getfile(f, v)
            if data is None:
                return None
            if f == '.hgtags':
                data = self._rewritetags(source, revmap, data)
            if f == '.hgsubstate':
                data = self._rewritesubstate(source, data)
            return context.memfilectx(self.repo, f, data, 'l' in mode,
                                      'x' in mode, copies.get(f))

        pl = []
        for p in parents:
            if p not in pl:
                pl.append(p)
        parents = pl
        nparents = len(parents)
        if self.filemapmode and nparents == 1:
            m1node = self.repo.changelog.read(bin(parents[0]))[0]
            parent = parents[0]

        if len(parents) < 2:
            parents.append(nullid)
        if len(parents) < 2:
            parents.append(nullid)
        p2 = parents.pop(0)

        text = commit.desc

        sha1s = re.findall(sha1re, text)
        for sha1 in sha1s:
            oldrev = source.lookuprev(sha1)
            newrev = revmap.get(oldrev)
            if newrev is not None:
                text = text.replace(sha1, newrev[:len(sha1)])

        extra = commit.extra.copy()

        sourcename = self.repo.ui.config('convert', 'hg.sourcename')
        if sourcename:
            extra['convert_source'] = sourcename

        for label in ('source', 'transplant_source', 'rebase_source',
                      'intermediate-source'):
            node = extra.get(label)

            if node is None:
                continue

            # Only transplant stores its reference in binary
            if label == 'transplant_source':
                node = hex(node)

            newrev = revmap.get(node)
            if newrev is not None:
                if label == 'transplant_source':
                    newrev = bin(newrev)

                extra[label] = newrev

        if self.branchnames and commit.branch:
            extra['branch'] = commit.branch
        if commit.rev and commit.saverev:
            extra['convert_revision'] = commit.rev

        while parents:
            p1 = p2
            p2 = parents.pop(0)
            p1ctx = self.repo[p1]
            p2ctx = None
            if p2 != nullid:
                p2ctx = self.repo[p2]
            fileset = set(files)
            if full:
                fileset.update(self.repo[p1])
                fileset.update(self.repo[p2])

            if p2ctx:
                p2files = set(cleanp2)
                for file in self._calculatemergedfiles(source, p1ctx, p2ctx):
                    p2files.add(file)
                    fileset.add(file)

            ctx = context.memctx(self.repo, (p1, p2), text, fileset,
                                 getfilectx, commit.author, commit.date, extra)

            # We won't know if the conversion changes the node until after the
            # commit, so copy the source's phase for now.
            self.repo.ui.setconfig('phases', 'new-commit',
                                   phases.phasenames[commit.phase], 'convert')

            tr = self.repo.transaction("convert")

            try:
                node = hex(self.repo.commitctx(ctx))

                # If the node value has changed, but the phase is lower than
                # draft, set it back to draft since it hasn't been exposed
                # anywhere.
                if commit.rev != node:
                    ctx = self.repo[node]
                    if ctx.phase() < phases.draft:
                        phases.retractboundary(self.repo, tr, phases.draft,
                                               [ctx.node()])
                tr.close()
            finally:
                tr.release()

            text = "(octopus merge fixup)\n"
            p2 = node

        if self.filemapmode and nparents == 1:
            man = self.repo.manifest
            mnode = self.repo.changelog.read(bin(p2))[0]
            closed = 'close' in commit.extra
            if not closed and not man.cmp(m1node, man.revision(mnode)):
                self.ui.status(_("filtering out empty revision\n"))
                self.repo.rollback(force=True)
                return parent
        return p2
Example #7
0
def createcmd(ui, repo, pats, opts):
    """subcommand that creates a new shelve"""

    def publicancestors(ctx):
        """Compute the public ancestors of a commit.

        Much faster than the revset ancestors(ctx) & draft()"""
        seen = set([nullrev])
        visit = util.deque()
        visit.append(ctx)
        while visit:
            ctx = visit.popleft()
            yield ctx.node()
            for parent in ctx.parents():
                rev = parent.rev()
                if rev not in seen:
                    seen.add(rev)
                    if parent.mutable():
                        visit.append(parent)

    wctx = repo[None]
    parents = wctx.parents()
    if len(parents) > 1:
        raise util.Abort(_('cannot shelve while merging'))
    parent = parents[0]

    # we never need the user, so we use a generic user for all shelve operations
    user = '******'
    label = repo._bookmarkcurrent or parent.branch() or 'default'

    # slashes aren't allowed in filenames, therefore we rename it
    label = label.replace('/', '_')

    def gennames():
        yield label
        for i in xrange(1, 100):
            yield '%s-%02d' % (label, i)

    shelvedfiles = []

    def commitfunc(ui, repo, message, match, opts):
        # check modified, added, removed, deleted only
        for flist in repo.status(match=match)[:4]:
            shelvedfiles.extend(flist)
        hasmq = util.safehasattr(repo, 'mq')
        if hasmq:
            saved, repo.mq.checkapplied = repo.mq.checkapplied, False
        try:
            return repo.commit(message, user, opts.get('date'), match,
                               editor=cmdutil.getcommiteditor(**opts))
        finally:
            if hasmq:
                repo.mq.checkapplied = saved

    if parent.node() != nullid:
        desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
    else:
        desc = '(changes in empty repository)'

    if not opts['message']:
        opts['message'] = desc

    name = opts['name']

    wlock = lock = tr = bms = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()

        bms = repo._bookmarks.copy()
        # use an uncommitted transaction to generate the bundle to avoid
        # pull races. ensure we don't print the abort message to stderr.
        tr = repo.transaction('commit', report=lambda x: None)

        if name:
            if shelvedfile(repo, name, 'hg').exists():
                raise util.Abort(_("a shelved change named '%s' already exists")
                                 % name)
        else:
            for n in gennames():
                if not shelvedfile(repo, n, 'hg').exists():
                    name = n
                    break
            else:
                raise util.Abort(_("too many shelved changes named '%s'") %
                                 label)

        # ensure we are not creating a subdirectory or a hidden file
        if '/' in name or '\\' in name:
            raise util.Abort(_('shelved change names may not contain slashes'))
        if name.startswith('.'):
            raise util.Abort(_("shelved change names may not start with '.'"))

        node = cmdutil.commit(ui, repo, commitfunc, pats, opts)

        if not node:
            stat = repo.status(match=scmutil.match(repo[None], pats, opts))
            if stat[3]:
                ui.status(_("nothing changed (%d missing files, see "
                            "'hg status')\n") % len(stat[3]))
            else:
                ui.status(_("nothing changed\n"))
            return 1

        phases.retractboundary(repo, phases.secret, [node])

        fp = shelvedfile(repo, name, 'files').opener('wb')
        fp.write('\0'.join(shelvedfiles))

        bases = list(publicancestors(repo[node]))
        cg = changegroup.changegroupsubset(repo, bases, [node], 'shelve')
        shelvedfile(repo, name, 'hg').writebundle(cg)
        cmdutil.export(repo, [node],
                       fp=shelvedfile(repo, name, 'patch').opener('wb'),
                       opts=mdiff.diffopts(git=True))


        if ui.formatted():
            desc = util.ellipsis(desc, ui.termwidth())
        ui.status(_('shelved as %s\n') % name)
        hg.update(repo, parent.node())
    finally:
        if bms:
            # restore old bookmarks
            repo._bookmarks.update(bms)
            repo._bookmarks.write()
        if tr:
            tr.abort()
        lockmod.release(lock, wlock)
Example #8
0
                                       opts.get('date'), match)
                finally:
                    if hasmq:
                        repo.mq.checkapplied = saved

            tempopts = {}
            tempopts['message'] = "pending changes temporary commit"
            tempopts['date'] = opts.get('date')
            ui.quiet = True
            node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
            tmpwctx = repo[node]

        ui.quiet = True
        shelvedfile(repo, basename, 'hg').applybundle()
        nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
        phases.retractboundary(repo, phases.secret, nodes)

        ui.quiet = oldquiet

        shelvectx = repo['tip']

        # If the shelve is not immediately on top of the commit
        # we'll be merging with, rebase it to be on top.
        if tmpwctx.node() != shelvectx.parents()[0].node():
            ui.status(_('rebasing shelved changes\n'))
            try:
                rebase.rebase(ui, repo, **{
                    'rev' : [shelvectx.rev()],
                    'dest' : str(tmpwctx.rev()),
                    'keep' : True,
                })
Example #9
0
def createcmd(ui, repo, pats, opts):
    """subcommand that creates a new shelve"""
    def publicancestors(ctx):
        """Compute the public ancestors of a commit.

        Much faster than the revset ancestors(ctx) & draft()"""
        seen = set([nullrev])
        visit = util.deque()
        visit.append(ctx)
        while visit:
            ctx = visit.popleft()
            yield ctx.node()
            for parent in ctx.parents():
                rev = parent.rev()
                if rev not in seen:
                    seen.add(rev)
                    if parent.mutable():
                        visit.append(parent)

    wctx = repo[None]
    parents = wctx.parents()
    if len(parents) > 1:
        raise util.Abort(_('cannot shelve while merging'))
    parent = parents[0]

    # we never need the user, so we use a generic user for all shelve operations
    user = '******'
    label = repo._bookmarkcurrent or parent.branch() or 'default'

    # slashes aren't allowed in filenames, therefore we rename it
    label = label.replace('/', '_')

    def gennames():
        yield label
        for i in xrange(1, 100):
            yield '%s-%02d' % (label, i)

    shelvedfiles = []

    def commitfunc(ui, repo, message, match, opts):
        # check modified, added, removed, deleted only
        for flist in repo.status(match=match)[:4]:
            shelvedfiles.extend(flist)
        hasmq = util.safehasattr(repo, 'mq')
        if hasmq:
            saved, repo.mq.checkapplied = repo.mq.checkapplied, False
        try:
            return repo.commit(message,
                               user,
                               opts.get('date'),
                               match,
                               editor=cmdutil.getcommiteditor(**opts))
        finally:
            if hasmq:
                repo.mq.checkapplied = saved

    if parent.node() != nullid:
        desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
    else:
        desc = '(changes in empty repository)'

    if not opts['message']:
        opts['message'] = desc

    name = opts['name']

    wlock = lock = tr = bms = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()

        bms = repo._bookmarks.copy()
        # use an uncommitted transaction to generate the bundle to avoid
        # pull races. ensure we don't print the abort message to stderr.
        tr = repo.transaction('commit', report=lambda x: None)

        if name:
            if shelvedfile(repo, name, 'hg').exists():
                raise util.Abort(
                    _("a shelved change named '%s' already exists") % name)
        else:
            for n in gennames():
                if not shelvedfile(repo, n, 'hg').exists():
                    name = n
                    break
            else:
                raise util.Abort(
                    _("too many shelved changes named '%s'") % label)

        # ensure we are not creating a subdirectory or a hidden file
        if '/' in name or '\\' in name:
            raise util.Abort(_('shelved change names may not contain slashes'))
        if name.startswith('.'):
            raise util.Abort(_("shelved change names may not start with '.'"))

        node = cmdutil.commit(ui, repo, commitfunc, pats, opts)

        if not node:
            stat = repo.status(match=scmutil.match(repo[None], pats, opts))
            if stat[3]:
                ui.status(
                    _("nothing changed (%d missing files, see "
                      "'hg status')\n") % len(stat[3]))
            else:
                ui.status(_("nothing changed\n"))
            return 1

        phases.retractboundary(repo, phases.secret, [node])

        fp = shelvedfile(repo, name, 'files').opener('wb')
        fp.write('\0'.join(shelvedfiles))

        bases = list(publicancestors(repo[node]))
        cg = changegroup.changegroupsubset(repo, bases, [node], 'shelve')
        shelvedfile(repo, name, 'hg').writebundle(cg)
        cmdutil.export(repo, [node],
                       fp=shelvedfile(repo, name, 'patch').opener('wb'),
                       opts=mdiff.diffopts(git=True))

        if ui.formatted():
            desc = util.ellipsis(desc, ui.termwidth())
        ui.status(_('shelved as %s\n') % name)
        hg.update(repo, parent.node())
    finally:
        if bms:
            # restore old bookmarks
            repo._bookmarks.update(bms)
            repo._bookmarks.write()
        if tr:
            tr.abort()
        lockmod.release(lock, wlock)
Example #10
0
                                       opts.get('date'), match)
                finally:
                    if hasmq:
                        repo.mq.checkapplied = saved

            tempopts = {}
            tempopts['message'] = "pending changes temporary commit"
            tempopts['date'] = opts.get('date')
            ui.quiet = True
            node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
            tmpwctx = repo[node]

        ui.quiet = True
        shelvedfile(repo, basename, 'hg').applybundle()
        nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
        phases.retractboundary(repo, phases.secret, nodes)

        ui.quiet = oldquiet

        shelvectx = repo['tip']

        # If the shelve is not immediately on top of the commit
        # we'll be merging with, rebase it to be on top.
        if tmpwctx.node() != shelvectx.parents()[0].node():
            ui.status(_('rebasing shelved changes\n'))
            try:
                rebase.rebase(
                    ui, repo, **{
                        'rev': [shelvectx.rev()],
                        'dest': str(tmpwctx.rev()),
                        'keep': True,
Example #11
0
    def putcommit(self, files, copies, parents, commit, source, revmap, full,
                  cleanp2):
        files = dict(files)

        def getfilectx(repo, memctx, f):
            if p2ctx and f in p2files and f not in copies:
                self.ui.debug('reusing %s from p2\n' % f)
                try:
                    return p2ctx[f]
                except error.ManifestLookupError:
                    # If the file doesn't exist in p2, then we're syncing a
                    # delete, so just return None.
                    return None
            try:
                v = files[f]
            except KeyError:
                return None
            data, mode = source.getfile(f, v)
            if data is None:
                return None
            if f == '.hgtags':
                data = self._rewritetags(source, revmap, data)
            if f == '.hgsubstate':
                data = self._rewritesubstate(source, data)
            return context.memfilectx(self.repo, f, data, 'l' in mode,
                                      'x' in mode, copies.get(f))

        pl = []
        for p in parents:
            if p not in pl:
                pl.append(p)
        parents = pl
        nparents = len(parents)
        if self.filemapmode and nparents == 1:
            m1node = self.repo.changelog.read(nodemod.bin(parents[0]))[0]
            parent = parents[0]

        if len(parents) < 2:
            parents.append(nodemod.nullid)
        if len(parents) < 2:
            parents.append(nodemod.nullid)
        p2 = parents.pop(0)

        text = commit.desc

        sha1s = re.findall(sha1re, text)
        for sha1 in sha1s:
            oldrev = source.lookuprev(sha1)
            newrev = revmap.get(oldrev)
            if newrev is not None:
                text = text.replace(sha1, newrev[:len(sha1)])

        extra = commit.extra.copy()

        sourcename = self.repo.ui.config('convert', 'hg.sourcename')
        if sourcename:
            extra['convert_source'] = sourcename

        for label in ('source', 'transplant_source', 'rebase_source',
                      'intermediate-source'):
            node = extra.get(label)

            if node is None:
                continue

            # Only transplant stores its reference in binary
            if label == 'transplant_source':
                node = nodemod.hex(node)

            newrev = revmap.get(node)
            if newrev is not None:
                if label == 'transplant_source':
                    newrev = nodemod.bin(newrev)

                extra[label] = newrev

        if self.branchnames and commit.branch:
            extra['branch'] = commit.branch
        if commit.rev and commit.saverev:
            extra['convert_revision'] = commit.rev

        while parents:
            p1 = p2
            p2 = parents.pop(0)
            p1ctx = self.repo[p1]
            p2ctx = None
            if p2 != nodemod.nullid:
                p2ctx = self.repo[p2]
            fileset = set(files)
            if full:
                fileset.update(self.repo[p1])
                fileset.update(self.repo[p2])

            if p2ctx:
                p2files = set(cleanp2)
                for file in self._calculatemergedfiles(source, p1ctx, p2ctx):
                    p2files.add(file)
                    fileset.add(file)

            ctx = context.memctx(self.repo, (p1, p2), text, fileset,
                                 getfilectx, commit.author, commit.date, extra)

            # We won't know if the conversion changes the node until after the
            # commit, so copy the source's phase for now.
            self.repo.ui.setconfig('phases', 'new-commit',
                                   phases.phasenames[commit.phase], 'convert')

            with self.repo.transaction("convert") as tr:
                node = nodemod.hex(self.repo.commitctx(ctx))

                # If the node value has changed, but the phase is lower than
                # draft, set it back to draft since it hasn't been exposed
                # anywhere.
                if commit.rev != node:
                    ctx = self.repo[node]
                    if ctx.phase() < phases.draft:
                        phases.retractboundary(self.repo, tr, phases.draft,
                                               [ctx.node()])

            text = "(octopus merge fixup)\n"
            p2 = node

        if self.filemapmode and nparents == 1:
            man = self.repo.manifest
            mnode = self.repo.changelog.read(nodemod.bin(p2))[0]
            closed = 'close' in commit.extra
            if not closed and not man.cmp(m1node, man.revision(mnode)):
                self.ui.status(_("filtering out empty revision\n"))
                self.repo.rollback(force=True)
                return parent
        return p2
Example #12
0
def fold(ui, repo, *revs, **opts):
    """fold multiple revisions into a single one

    With --from, folds all the revisions linearly between the given revisions
    and the parent of the working directory.

    With --exact, folds only the specified revisions while ignoring the
    parent of the working directory. In this case, the given revisions must
    form a linear unbroken chain.

    .. container:: verbose

     Some examples:

     - Fold the current revision with its parent::

         hg fold --from .^

     - Fold all draft revisions with working directory parent::

         hg fold --from 'draft()'

       See :hg:`help phases` for more about draft revisions and
       :hg:`help revsets` for more about the `draft()` keyword

     - Fold revisions between 3 and 6 with the working directory parent::

         hg fold --from 3::6

     - Fold revisions 3 and 4:

        hg fold "3 + 4" --exact

     - Only fold revisions linearly between foo and @::

         hg fold foo::@ --exact
    """
    revs = list(revs)
    revs.extend(opts['rev'])
    if not revs:
        raise error.Abort(_('no revisions specified'))

    revs = scmutil.revrange(repo, revs)

    if opts.get('no_rebase'):
        torebase = ()
    else:
        torebase = repo.revs('descendants(%ld) - (%ld)', revs, revs)

    if opts['from'] and opts['exact']:
        raise error.Abort(_('cannot use both --from and --exact'))
    elif opts['from']:
        # Try to extend given revision starting from the working directory
        extrevs = repo.revs('(%ld::.) or (.::%ld)', revs, revs)
        discardedrevs = [r for r in revs if r not in extrevs]
        if discardedrevs:
            msg = _("cannot fold non-linear revisions")
            hint = _("given revisions are unrelated to parent of working"
                     " directory")
            raise error.Abort(msg, hint=hint)
        revs = extrevs
    elif opts['exact']:
        # Nothing to do; "revs" is already set correctly
        pass
    else:
        raise error.Abort(_('must specify either --from or --exact'))

    if not revs:
        raise error.Abort(_('specified revisions evaluate to an empty set'),
                          hint=_('use different revision arguments'))
    elif len(revs) == 1:
        ui.write_err(_('single revision specified, nothing to fold\n'))
        return 1

    with repo.wlock(), repo.lock(), ui.formatter('fold', opts) as fm:
        fm.startitem()
        root, head = _foldcheck(repo, revs)

        with repo.transaction('fold') as tr:
            commitopts = opts.copy()
            allctx = [repo[r] for r in revs]
            targetphase = max(c.phase() for c in allctx)

            if commitopts.get('message') or commitopts.get('logfile'):
                commitopts['edit'] = False
            else:
                msgs = ["HG: This is a fold of %d changesets." % len(allctx)]
                msgs += ["HG: Commit message of changeset %s.\n\n%s\n" %
                         (c.rev(), c.description()) for c in allctx]
                commitopts['message'] = "\n".join(msgs)
                commitopts['edit'] = True

            newid, unusedvariable = common.rewrite(repo, root, allctx, head,
                                                   [root.p1().node(),
                                                    root.p2().node()],
                                                   commitopts=commitopts)
            phases.retractboundary(repo, tr, targetphase, [newid])

            replacements = {ctx.node(): (newid,) for ctx in allctx}
            nodechanges = {fm.hexfunc(ctx.node()): [fm.hexfunc(newid)]
                           for ctx in allctx}
            fm.data(nodechanges=fm.formatdict(nodechanges))
            scmutil.cleanupnodes(repo, replacements, 'fold')
            fm.condwrite(not ui.quiet, 'count',
                         '%i changesets folded\n', len(revs))
            if repo['.'].rev() in revs:
                hg.update(repo, newid)

            if torebase:
                folded = repo.revs('allsuccessors(%ld)', revs).last()
                common.restackonce(ui, repo, folded)
Example #13
0
def metaedit(ui, repo, *revs, **opts):
    """edit commit information

    Edits the commit information for the specified revisions. By default, edits
    commit information for the working directory parent.

    With --fold, also folds multiple revisions into one if necessary. In this
    case, the given revisions must form a linear unbroken chain.

    .. container:: verbose

     Some examples:

     - Edit the commit message for the working directory parent::

         hg metaedit

     - Change the username for the working directory parent::

         hg metaedit --user 'New User <*****@*****.**>'

     - Combine all draft revisions that are ancestors of foo but not of @ into
       one::

         hg metaedit --fold 'draft() and only(foo,@)'

       See :hg:`help phases` for more about draft revisions, and
       :hg:`help revsets` for more about the `draft()` and `only()` keywords.
    """
    revs = list(revs)
    revs.extend(opts['rev'])
    if not revs:
        if opts['fold']:
            raise error.Abort(_('revisions must be specified with --fold'))
        revs = ['.']

    wlock = lock = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()

        revs = scmutil.revrange(repo, revs)

        if opts['fold']:
            root, head = fold._foldcheck(repo, revs)
        else:
            if repo.revs("%ld and public()", revs):
                raise error.Abort(_('cannot edit commit information for public '
                                    'revisions'))
            root = head = repo[revs.first()]

        wctx = repo[None]
        p1 = wctx.p1()
        tr = repo.transaction('metaedit')
        newp1 = None
        try:
            commitopts = opts.copy()
            allctx = [repo[r] for r in revs]

            if commitopts.get('message') or commitopts.get('logfile'):
                commitopts['edit'] = False
            else:
                if opts['fold']:
                    msgs = [_("HG: This is a fold of %d changesets.")
                            % len(allctx)]
                    msgs += [_("HG: Commit message of changeset %s.\n\n%s\n")
                             % (c.rev(), c.description()) for c in allctx]
                else:
                    msgs = [head.description()]
                commitopts['message'] = "\n".join(msgs)
                commitopts['edit'] = True

            if root == head:
                # fast path: use metarewrite
                replacemap = {}
                # we need topological order
                allctx = sorted(allctx, key=lambda c: c.rev())
                # all descendats that can be safely rewritten
                newunstable = common.newunstable(repo, revs)
                newunstablectx = sorted([repo[r] for r in newunstable],
                                        key=lambda c: c.rev())

                def _rewritesingle(c, _commitopts):
                    if _commitopts.get('edit', False):
                        _commitopts['message'] = \
                            "HG: Commit message of changeset %s\n%s" %\
                            (str(c), c.description())
                    bases = [
                        replacemap.get(c.p1().node(), c.p1().node()),
                        replacemap.get(c.p2().node(), c.p2().node()),
                    ]
                    newid, created = common.metarewrite(repo, c, bases,
                                                        commitopts=_commitopts)
                    if created:
                        replacemap[c.node()] = newid
                for c in allctx:
                    _rewritesingle(c, commitopts)

                if _histediting(repo):
                    ui.note(_('during histedit, the descendants of '
                              'the edited commit weren\'t auto-rebased\n'))
                else:
                    for c in newunstablectx:
                        _rewritesingle(c,
                                       {'date': commitopts.get('date') or None})

                if p1.node() in replacemap:
                    repo.setparents(replacemap[p1.node()])
                if len(replacemap) > 0:
                    mapping = dict(map(lambda oldnew: (oldnew[0], [oldnew[1]]),
                                       replacemap.iteritems()))
                    scmutil.cleanupnodes(repo, mapping, 'metaedit')
                    # TODO: set poroper phase boundaries (affects secret
                    # phase only)
                else:
                    ui.status(_("nothing changed\n"))
                    return 1
            else:
                # slow path: create a new commit
                targetphase = max(c.phase() for c in allctx)

                # TODO: if the author and message are the same, don't create a
                # new hash. Right now we create a new hash because the date can
                # be different.
                newid, created = common.rewrite(
                    repo, root, allctx, head,
                    [root.p1().node(), root.p2().node()],
                    commitopts=commitopts)
                if created:
                    if p1.rev() in revs:
                        newp1 = newid
                    phases.retractboundary(repo, tr, targetphase, [newid])
                    mapping = dict(
                        [(repo[rev].node(), [newid]) for rev in revs])
                    scmutil.cleanupnodes(repo, mapping, 'metaedit')
                else:
                    ui.status(_("nothing changed\n"))
                    return 1
            tr.close()
        finally:
            tr.release()

        if opts['fold']:
            ui.status(_('%i changesets folded\n') % len(revs))
        if newp1 is not None:
            hg.update(repo, newp1)
    finally:
        lockmod.release(lock, wlock)
Example #14
0
def trychooser_command(ui, repo, *args, **opts):
    from mercurial import extensions, commands, util
    try:
        from mercurial import phases
    except:
        pass # older version with no phases

    if not sys.stdin.isatty():
        # rxvt on Windows disables ui.prompt, but installing our own seems to work
        def ui_prompt(prompt, default):
            sys.stdout.write(prompt)
            sys.stdout.write(" ")
            sys.stdout.flush()
            response = sys.stdin.readline().rstrip()
            if response == '':
                return default
            return response
        ui.prompt = ui_prompt

    display_only = opts['nopush']
    try:
        mq = extensions.find('mq')
    except KeyError:
        display_only = True
        ui.warn("Warning: mq hasn't been found, this is going to print the syntax only.\n")

    if repo[None].dirty():
        raise util.Abort("local changes found, refresh first")

    if opts.get('message'):
        msg = opts.get('message')
        if msg.find('try:') == -1:
            msg = 'try: ' + msg
    elif not menu or opts['question']:
        question(ui, headers, data['questions'])
        msg = "try: " + generateTrySyntax(headers, data['syntax'])
    else:
        syntaxGenerator = lambda h: generateTrySyntax(h, data['syntax'])
        msg = menu({ "syntaxGenerator": syntaxGenerator }, headers, ui)

    if opts['desc']:
        msg = opts['desc'] + ", " + msg

    #bugnum = suggest_results_bug(ui, repo)
    #if bugnum:
    #  msg += ' --post-to-bugzilla Bug %s' % bugnum

    if display_only:
        ui.write("Use the following try message:\n%s\n" % msg)
        return

    ui.write("The following try message is going to be used:\n%s\n" % msg)

    ui.write("Create the trychooser mq entry...\n")
    mq.new(ui, repo, 'trychooser', message=msg)

    ui.write("Push to try server...\n")
    try:
        commands.push(ui, repo, opts['server'], force=True,
                      rev=[repo['.'].rev()])

        # try server is running an old mercurial version that doesn't support
        # phases, so is assumed to be publishing. Undo the effects.
        lock = repo.lock()
        try:
            draft = phases.phasenames.index('draft')
            phases.retractboundary(repo, draft, [ repo.mq.applied[0].node ])
        except:
            pass # probably running under pre-phase version
        finally:
            lock.release()

    finally:
        mq.pop(ui, repo)
        mq.delete(ui, repo, 'trychooser')