Пример #1
0
def _commitworkingcopychanges(ui, repo, opts, tmpwctx):
    """Temporarily commit working copy changes before moving unshelve commit"""
    # Store pending changes in a commit and remember added in case a shelve
    # contains unknown files that are part of the pending change
    s = repo.status()
    addedbefore = frozenset(s.added)
    if not (s.modified or s.added or s.removed):
        return tmpwctx, addedbefore
    ui.status(_("temporarily committing pending changes "
                "(restore with 'hg unshelve --abort')\n"))
    commitfunc = getcommitfunc(extra=None, interactive=False,
                               editor=False)
    tempopts = {}
    tempopts['message'] = "pending changes temporary commit"
    tempopts['date'] = opts.get('date')
    with ui.configoverride({('ui', 'quiet'): True}):
        node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
    tmpwctx = repo[node]
    return tmpwctx, addedbefore
Пример #2
0
def _commitworkingcopychanges(ui, repo, opts, tmpwctx):
    """Temporarily commit working copy changes before moving unshelve commit"""
    # Store pending changes in a commit and remember added in case a shelve
    # contains unknown files that are part of the pending change
    s = repo.status()
    addedbefore = frozenset(s.added)
    if not (s.modified or s.added or s.removed):
        return tmpwctx, addedbefore
    ui.status(_("temporarily committing pending changes "
                "(restore with 'hg unshelve --abort')\n"))
    commitfunc = getcommitfunc(extra=None, interactive=False,
                               editor=False)
    tempopts = {}
    tempopts['message'] = "pending changes temporary commit"
    tempopts['date'] = opts.get('date')
    with ui.configoverride({('ui', 'quiet'): True}):
        node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
    tmpwctx = repo[node]
    ui.debug("temporary working copy commit: %s:%s\n" %
             (tmpwctx.rev(), nodemod.short(node)))
    return tmpwctx, addedbefore
Пример #3
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)
Пример #4
0
                hasmq = util.safehasattr(repo, 'mq')
                if hasmq:
                    saved, repo.mq.checkapplied = repo.mq.checkapplied, False

                try:
                    return repo.commit(message, 'shelve@localhost',
                                       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'))
Пример #5
0
def shelve(ui, repo, *pats, **opts):
    '''interactively select changes to set aside

    If a list of files is omitted, all changes reported by "hg status"
    will be candidates for shelving.

    You will be prompted for whether to shelve changes to each
    modified file, and for files with multiple changes, for each
    change to use.

    The shelve command works with the Color extension to display
    diffs in color.

    On each prompt, the following responses are possible:

      y - shelve this change
      n - skip this change

      s - skip remaining changes to this file
      f - shelve remaining changes to this file

      d - done, skip remaining changes and files
      a - shelve all changes to all remaining files
      q - quit, shelving no changes

      ? - display help'''

    if not ui.interactive:
        raise util.Abort(_('shelve can only be run interactively'))

    # List all the active shelves by name and return '
    if opts['list']:
        listshelves(ui,repo)
        return

    forced = opts['force'] or opts['append']
    
    # Shelf name and path
    shelfname = opts.get('name')
    shelfpath = getshelfpath(repo, shelfname)
    
    if os.path.exists(repo.join(shelfpath)) and not forced:
        raise util.Abort(_('shelve data already exists'))
            
    def shelvefunc(ui, repo, message, match, opts):
        changes = repo.status(match=match)[:5]
        modified, added, removed = changes[:3]
        files = modified + added + removed
        diffopts = mdiff.diffopts(git=True, nodates=True)
        patch_diff = ''.join(patch.diff(repo, repo.dirstate.parents()[0],
                           match=match, changes=changes, opts=diffopts))
        
        fp = cStringIO.StringIO(patch_diff)
        ac = parsepatch(fp)
        fp.close()
        
        chunks = filterpatch(ui, ac, not opts['all'])
        rc = refilterpatch(ac, chunks)

        contenders = {}
        for h in chunks:
            try: contenders.update(dict.fromkeys(h.files()))
            except AttributeError: pass

        newfiles = [f for f in files if f in contenders]

        if not newfiles:
            ui.status(_('no changes to shelve\n'))
            return 0

        modified = dict.fromkeys(changes[0])

        backupdir = repo.join('shelve-backups')

        try:
            bkfiles = [f for f in newfiles if f in modified]
            backups = makebackup(ui, repo, backupdir, bkfiles)
            
            # patch to shelve
            sp = cStringIO.StringIO()
            for c in chunks:
                if c.filename() in backups:
                    c.write(sp)
            doshelve = sp.tell()
            sp.seek(0)

            # patch to apply to shelved files
            fp = cStringIO.StringIO()
            for c in rc:
                if c.filename() in backups:
                    c.write(fp)
            dopatch = fp.tell()
            fp.seek(0)

            try:
                # 3a. apply filtered patch to clean repo (clean)
                if backups:
                    hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)

                # 3b. apply filtered patch to clean repo (apply)
                if dopatch:
                    ui.debug('applying patch\n')
                    ui.debug(fp.getvalue())
                    patch.internalpatch(fp, ui, 1, repo.root)
                del fp

                # 3c. apply filtered patch to clean repo (shelve)
                if doshelve:
                    ui.debug("saving patch to shelve\n")
                    if opts['append']:
                        f = repo.opener(shelfpath, "a")
                    else:
                        f = repo.opener(shelfpath, "w")
                    f.write(sp.getvalue())
                    del f
                del sp
            except:
                try:
                    for realname, tmpname in backups.iteritems():
                        ui.debug('restoring %r to %r\n' % (tmpname, realname))
                        util.copyfile(tmpname, repo.wjoin(realname))
                    ui.debug('removing shelve file\n')
                    os.unlink(repo.join(shelfpath))
                except OSError:
                    pass

            return 0
        finally:
            try:
                for realname, tmpname in backups.iteritems():
                    ui.debug('removing backup for %r : %r\n' % (realname, tmpname))
                    os.unlink(tmpname)
                os.rmdir(backupdir)
            except OSError:
                pass
    fancyopts.fancyopts([], commands.commitopts, opts)
    return cmdutil.commit(ui, repo, shelvefunc, pats, opts)
Пример #6
0
def _dounshelve(ui, repo, *shelved, **opts):
    abortf = opts.get('abort')
    continuef = opts.get('continue')
    if not abortf and not continuef:
        cmdutil.checkunfinished(repo)

    if abortf or continuef:
        if abortf and continuef:
            raise error.Abort(_('cannot use both abort and continue'))
        if shelved:
            raise error.Abort(
                _('cannot combine abort/continue with '
                  'naming a shelved change'))
        if abortf and opts.get('tool', False):
            ui.warn(_('tool option will be ignored\n'))

        try:
            state = shelvedstate.load(repo)
        except IOError as err:
            if err.errno != errno.ENOENT:
                raise
            cmdutil.wrongtooltocontinue(repo, _('unshelve'))
        except error.CorruptedState as err:
            ui.debug(str(err) + '\n')
            if continuef:
                msg = _('corrupted shelved state file')
                hint = _('please run hg unshelve --abort to abort unshelve '
                         'operation')
                raise error.Abort(msg, hint=hint)
            elif abortf:
                msg = _('could not read shelved state file, your working copy '
                        'may be in an unexpected state\nplease update to some '
                        'commit\n')
                ui.warn(msg)
                shelvedstate.clear(repo)
            return

        if abortf:
            return unshelveabort(ui, repo, state, opts)
        elif continuef:
            return unshelvecontinue(ui, repo, state, opts)
    elif len(shelved) > 1:
        raise error.Abort(_('can only unshelve one change at a time'))
    elif not shelved:
        shelved = listshelves(repo)
        if not shelved:
            raise error.Abort(_('no shelved changes to apply!'))
        basename = util.split(shelved[0][1])[1]
        ui.status(_("unshelving change '%s'\n") % basename)
    else:
        basename = shelved[0]

    if not shelvedfile(repo, basename, 'patch').exists():
        raise error.Abort(_("shelved change '%s' not found") % basename)

    oldquiet = ui.quiet
    lock = tr = None
    forcemerge = ui.backupconfig('ui', 'forcemerge')
    try:
        ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'unshelve')
        lock = repo.lock()

        tr = repo.transaction('unshelve', report=lambda x: None)
        oldtiprev = len(repo)

        pctx = repo['.']
        tmpwctx = pctx
        # The goal is to have a commit structure like so:
        # ...-> pctx -> tmpwctx -> shelvectx
        # where tmpwctx is an optional commit with the user's pending changes
        # and shelvectx is the unshelved changes. Then we merge it all down
        # to the original pctx.

        # Store pending changes in a commit and remember added in case a shelve
        # contains unknown files that are part of the pending change
        s = repo.status()
        addedbefore = frozenset(s.added)
        if s.modified or s.added or s.removed or s.deleted:
            ui.status(
                _("temporarily committing pending changes "
                  "(restore with 'hg unshelve --abort')\n"))

            def commitfunc(ui, repo, message, match, opts):
                hasmq = util.safehasattr(repo, 'mq')
                if hasmq:
                    saved, repo.mq.checkapplied = repo.mq.checkapplied, False

                backup = repo.ui.backupconfig('phases', 'new-commit')
                try:
                    repo.ui.setconfig('phases', 'new-commit', phases.secret)
                    return repo.commit(message, 'shelve@localhost',
                                       opts.get('date'), match)
                finally:
                    repo.ui.restoreconfig(backup)
                    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()

        ui.quiet = oldquiet

        shelvectx = repo['tip']

        branchtorestore = ''
        if shelvectx.branch() != shelvectx.p1().branch():
            branchtorestore = shelvectx.branch()

        # 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,
                        'tool': opts.get('tool', ''),
                    })
            except error.InterventionRequired:
                tr.close()

                stripnodes = [
                    repo.changelog.node(rev)
                    for rev in xrange(oldtiprev, len(repo))
                ]
                shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes,
                                  branchtorestore)

                util.rename(repo.join('rebasestate'),
                            repo.join('unshelverebasestate'))
                raise error.InterventionRequired(
                    _("unresolved conflicts (see 'hg resolve', then "
                      "'hg unshelve --continue')"))

            # refresh ctx after rebase completes
            shelvectx = repo['tip']

            if not shelvectx in tmpwctx.children():
                # rebase was a no-op, so it produced no child commit
                shelvectx = tmpwctx

        mergefiles(ui, repo, pctx, shelvectx)
        restorebranch(ui, repo, branchtorestore)

        # Forget any files that were unknown before the shelve, unknown before
        # unshelve started, but are now added.
        shelveunknown = shelvectx.extra().get('shelve_unknown')
        if shelveunknown:
            shelveunknown = frozenset(shelveunknown.split('\0'))
            addedafter = frozenset(repo.status().added)
            toforget = (addedafter & shelveunknown) - addedbefore
            repo[None].forget(toforget)

        shelvedstate.clear(repo)

        # The transaction aborting will strip all the commits for us,
        # but it doesn't update the inmemory structures, so addchangegroup
        # hooks still fire and try to operate on the missing commits.
        # Clean up manually to prevent this.
        repo.unfiltered().changelog.strip(oldtiprev, tr)

        unshelvecleanup(ui, repo, basename, opts)

        _aborttransaction(repo)
    finally:
        ui.quiet = oldquiet
        if tr:
            tr.release()
        lockmod.release(lock)
        ui.restoreconfig(forcemerge)
Пример #7
0
def unshelve(ui, repo, *shelved, **opts):
    """restore a shelved change to the working directory

    This command accepts an optional name of a shelved change to
    restore. If none is given, the most recent shelved change is used.

    If a shelved change is applied successfully, the bundle that
    contains the shelved changes is moved to a backup location
    (.hg/shelve-backup).

    Since you can restore a shelved change on top of an arbitrary
    commit, it is possible that unshelving will result in a conflict
    between your changes and the commits you are unshelving onto. If
    this occurs, you must resolve the conflict, then use
    ``--continue`` to complete the unshelve operation. (The bundle
    will not be moved until you successfully complete the unshelve.)

    (Alternatively, you can use ``--abort`` to abandon an unshelve
    that causes a conflict. This reverts the unshelved changes, and
    leaves the bundle in place.)

    After a successful unshelve, the shelved changes are stored in a
    backup directory. Only the N most recent backups are kept. N
    defaults to 10 but can be overridden using the ``shelve.maxbackups``
    configuration option.

    .. container:: verbose

       Timestamp in seconds is used to decide order of backups. More
       than ``maxbackups`` backups are kept, if same timestamp
       prevents from deciding exact order of them, for safety.
    """
    abortf = opts['abort']
    continuef = opts['continue']
    if not abortf and not continuef:
        cmdutil.checkunfinished(repo)

    if abortf or continuef:
        if abortf and continuef:
            raise error.Abort(_('cannot use both abort and continue'))
        if shelved:
            raise error.Abort(
                _('cannot combine abort/continue with '
                  'naming a shelved change'))

        try:
            state = shelvedstate.load(repo)
        except IOError as err:
            if err.errno != errno.ENOENT:
                raise
            raise error.Abort(_('no unshelve operation underway'))

        if abortf:
            return unshelveabort(ui, repo, state, opts)
        elif continuef:
            return unshelvecontinue(ui, repo, state, opts)
    elif len(shelved) > 1:
        raise error.Abort(_('can only unshelve one change at a time'))
    elif not shelved:
        shelved = listshelves(repo)
        if not shelved:
            raise error.Abort(_('no shelved changes to apply!'))
        basename = util.split(shelved[0][1])[1]
        ui.status(_("unshelving change '%s'\n") % basename)
    else:
        basename = shelved[0]

    if not shelvedfile(repo, basename, 'patch').exists():
        raise error.Abort(_("shelved change '%s' not found") % basename)

    oldquiet = ui.quiet
    wlock = lock = tr = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()

        tr = repo.transaction('unshelve', report=lambda x: None)
        oldtiprev = len(repo)

        pctx = repo['.']
        tmpwctx = pctx
        # The goal is to have a commit structure like so:
        # ...-> pctx -> tmpwctx -> shelvectx
        # where tmpwctx is an optional commit with the user's pending changes
        # and shelvectx is the unshelved changes. Then we merge it all down
        # to the original pctx.

        # Store pending changes in a commit
        s = repo.status()
        if s.modified or s.added or s.removed or s.deleted:
            ui.status(
                _("temporarily committing pending changes "
                  "(restore with 'hg unshelve --abort')\n"))

            def commitfunc(ui, repo, message, match, opts):
                hasmq = util.safehasattr(repo, 'mq')
                if hasmq:
                    saved, repo.mq.checkapplied = repo.mq.checkapplied, False

                backup = repo.ui.backupconfig('phases', 'new-commit')
                try:
                    repo.ui.setconfig('phases', 'new-commit', phases.secret)
                    return repo.commit(message, 'shelve@localhost',
                                       opts.get('date'), match)
                finally:
                    repo.ui.restoreconfig(backup)
                    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()

        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,
                    })
            except error.InterventionRequired:
                tr.close()

                stripnodes = [
                    repo.changelog.node(rev)
                    for rev in xrange(oldtiprev, len(repo))
                ]
                shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes)

                util.rename(repo.join('rebasestate'),
                            repo.join('unshelverebasestate'))
                raise error.InterventionRequired(
                    _("unresolved conflicts (see 'hg resolve', then "
                      "'hg unshelve --continue')"))

            # refresh ctx after rebase completes
            shelvectx = repo['tip']

            if not shelvectx in tmpwctx.children():
                # rebase was a no-op, so it produced no child commit
                shelvectx = tmpwctx

        mergefiles(ui, repo, pctx, shelvectx)
        shelvedstate.clear(repo)

        # The transaction aborting will strip all the commits for us,
        # but it doesn't update the inmemory structures, so addchangegroup
        # hooks still fire and try to operate on the missing commits.
        # Clean up manually to prevent this.
        repo.unfiltered().changelog.strip(oldtiprev, tr)

        unshelvecleanup(ui, repo, basename, opts)

        _aborttransaction(repo)
    finally:
        ui.quiet = oldquiet
        if tr:
            tr.release()
        lockmod.release(lock, wlock)
Пример #8
0
def _docreatecmd(ui, repo, pats, opts):
    wctx = repo[None]
    parents = wctx.parents()
    if len(parents) > 1:
        raise error.Abort(_('cannot shelve while merging'))
    parent = parents[0]
    origbranch = wctx.branch()

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

    if not opts.get('message'):
        opts['message'] = desc

    lock = tr = activebookmark = None
    try:
        lock = repo.lock()

        # 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)

        interactive = opts.get('interactive', False)
        includeunknown = (opts.get('unknown', False) and
                          not opts.get('addremove', False))

        name = getshelvename(repo, parent, opts)
        activebookmark = _backupactivebookmark(repo)
        extra = {}
        if includeunknown:
            _includeunknownfiles(repo, pats, opts, extra)

        if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts):
            # In non-bare shelve we don't store newly created branch
            # at bundled commit
            repo.dirstate.setbranch(repo['.'].branch())

        commitfunc = getcommitfunc(extra, interactive, editor=True)
        if not interactive:
            node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
        else:
            node = cmdutil.dorecord(ui, repo, commitfunc, None,
                                    False, cmdutil.recordfilter, *pats,
                                    **pycompat.strkwargs(opts))
        if not node:
            _nothingtoshelvemessaging(ui, repo, pats, opts)
            return 1

        _shelvecreatedcommit(repo, node, name)

        if ui.formatted():
            desc = stringutil.ellipsis(desc, ui.termwidth())
        ui.status(_('shelved as %s\n') % name)
        hg.update(repo, parent.node())
        if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts):
            repo.dirstate.setbranch(origbranch)

        _finishshelve(repo)
    finally:
        _restoreactivebookmark(repo, activebookmark)
        lockmod.release(tr, lock)
Пример #9
0
                ui.warn("removing backup failed: %s\n" % err)

    fancyopts.fancyopts([], commands.commitopts, opts)

    # wrap ui.write so diff output can be labeled/colorized
    def wrapwrite(orig, *args, **kw):
        label = kw.pop('label', '')
        if label:
            label += ' '
        for chunk, l in patch.difflabel(lambda: args):
            orig(chunk, label=label + l)

    oldwrite = ui.write
    extensions.wrapfunction(ui, 'write', wrapwrite)
    try:
        return cmdutil.commit(ui, repo, shelvefunc, pats, opts)
    finally:
        ui.write = oldwrite


def listshelves(ui, repo):
    # Check for shelve file at old location first
    if os.path.isfile(repo.join('shelve')):
        ui.status('default\n')

    # Now go through all the files in the shelves folder and list them out
    dirname = repo.join('shelves')
    if os.path.isdir(dirname):
        for filename in sorted(os.listdir(repo.join('shelves'))):
            ui.status(filename + '\n')
Пример #10
0
                    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
            newfiles = [repo.wjoin(n) for n in newfiles]
            commitfunc(ui, repo, *newfiles, **opts)

            return 0
        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)
                for realname, tmpname in newly_added_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
    return cmdutil.commit(ui, repo, recordfunc, pats, opts)
Пример #11
0
def unshelve(ui, repo, *shelved, **opts):
    """restore a shelved change to the working directory

    This command accepts an optional name of a shelved change to
    restore. If none is given, the most recent shelved change is used.

    If a shelved change is applied successfully, the bundle that
    contains the shelved changes is moved to a backup location
    (.hg/shelve-backup).

    Since you can restore a shelved change on top of an arbitrary
    commit, it is possible that unshelving will result in a conflict
    between your changes and the commits you are unshelving onto. If
    this occurs, you must resolve the conflict, then use
    ``--continue`` to complete the unshelve operation. (The bundle
    will not be moved until you successfully complete the unshelve.)

    (Alternatively, you can use ``--abort`` to abandon an unshelve
    that causes a conflict. This reverts the unshelved changes, and
    leaves the bundle in place.)

    After a successful unshelve, the shelved changes are stored in a
    backup directory. Only the N most recent backups are kept. N
    defaults to 10 but can be overridden using the ``shelve.maxbackups``
    configuration option.

    .. container:: verbose

       Timestamp in seconds is used to decide order of backups. More
       than ``maxbackups`` backups are kept, if same timestamp
       prevents from deciding exact order of them, for safety.
    """
    abortf = opts['abort']
    continuef = opts['continue']
    if not abortf and not continuef:
        cmdutil.checkunfinished(repo)

    if abortf or continuef:
        if abortf and continuef:
            raise error.Abort(_('cannot use both abort and continue'))
        if shelved:
            raise error.Abort(_('cannot combine abort/continue with '
                               'naming a shelved change'))

        try:
            state = shelvedstate.load(repo)
        except IOError as err:
            if err.errno != errno.ENOENT:
                raise
            raise error.Abort(_('no unshelve operation underway'))

        if abortf:
            return unshelveabort(ui, repo, state, opts)
        elif continuef:
            return unshelvecontinue(ui, repo, state, opts)
    elif len(shelved) > 1:
        raise error.Abort(_('can only unshelve one change at a time'))
    elif not shelved:
        shelved = listshelves(repo)
        if not shelved:
            raise error.Abort(_('no shelved changes to apply!'))
        basename = util.split(shelved[0][1])[1]
        ui.status(_("unshelving change '%s'\n") % basename)
    else:
        basename = shelved[0]

    if not shelvedfile(repo, basename, 'patch').exists():
        raise error.Abort(_("shelved change '%s' not found") % basename)

    oldquiet = ui.quiet
    wlock = lock = tr = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()

        tr = repo.transaction('unshelve', report=lambda x: None)
        oldtiprev = len(repo)

        pctx = repo['.']
        tmpwctx = pctx
        # The goal is to have a commit structure like so:
        # ...-> pctx -> tmpwctx -> shelvectx
        # where tmpwctx is an optional commit with the user's pending changes
        # and shelvectx is the unshelved changes. Then we merge it all down
        # to the original pctx.

        # Store pending changes in a commit
        s = repo.status()
        if s.modified or s.added or s.removed or s.deleted:
            ui.status(_("temporarily committing pending changes "
                        "(restore with 'hg unshelve --abort')\n"))
            def commitfunc(ui, repo, message, match, opts):
                hasmq = util.safehasattr(repo, 'mq')
                if hasmq:
                    saved, repo.mq.checkapplied = repo.mq.checkapplied, False

                backup = repo.ui.backupconfig('phases', 'new-commit')
                try:
                    repo.ui. setconfig('phases', 'new-commit', phases.secret)
                    return repo.commit(message, 'shelve@localhost',
                                       opts.get('date'), match)
                finally:
                    repo.ui.restoreconfig(backup)
                    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()

        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,
                })
            except error.InterventionRequired:
                tr.close()

                stripnodes = [repo.changelog.node(rev)
                              for rev in xrange(oldtiprev, len(repo))]
                shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes)

                util.rename(repo.join('rebasestate'),
                            repo.join('unshelverebasestate'))
                raise error.InterventionRequired(
                    _("unresolved conflicts (see 'hg resolve', then "
                      "'hg unshelve --continue')"))

            # refresh ctx after rebase completes
            shelvectx = repo['tip']

            if not shelvectx in tmpwctx.children():
                # rebase was a no-op, so it produced no child commit
                shelvectx = tmpwctx

        mergefiles(ui, repo, pctx, shelvectx)
        shelvedstate.clear(repo)

        # The transaction aborting will strip all the commits for us,
        # but it doesn't update the inmemory structures, so addchangegroup
        # hooks still fire and try to operate on the missing commits.
        # Clean up manually to prevent this.
        repo.unfiltered().changelog.strip(oldtiprev, tr)

        unshelvecleanup(ui, repo, basename, opts)

        _aborttransaction(repo)
    finally:
        ui.quiet = oldquiet
        if tr:
            tr.release()
        lockmod.release(lock, wlock)
Пример #12
0
def createcmd(ui, repo, pats, opts):
    """subcommand that creates a new shelve"""

    def mutableancestors(ctx):
        """return all mutable ancestors for ctx (included)

        Much faster than the revset ancestors(ctx) & draft()"""
        seen = set([nullrev])
        visit = collections.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 error.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._activebookmark 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)

    def commitfunc(ui, repo, message, match, opts):
        hasmq = util.safehasattr(repo, 'mq')
        if hasmq:
            saved, repo.mq.checkapplied = repo.mq.checkapplied, False
        backup = repo.ui.backupconfig('phases', 'new-commit')
        try:
            repo.ui. setconfig('phases', 'new-commit', phases.secret)
            editor = cmdutil.getcommiteditor(editform='shelve.shelve', **opts)
            return repo.commit(message, user, opts.get('date'), match,
                               editor=editor)
        finally:
            repo.ui.restoreconfig(backup)
            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 = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()

        # 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 error.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 error.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 error.Abort(_('shelved change names may not contain slashes'))
        if name.startswith('.'):
            raise error.Abort(_("shelved change names may not start with '.'"))
        interactive = opts.get('interactive', False)

        def interactivecommitfunc(ui, repo, *pats, **opts):
            match = scmutil.match(repo['.'], pats, {})
            message = opts['message']
            return commitfunc(ui, repo, message, match, opts)
        if not interactive:
            node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
        else:
            node = cmdutil.dorecord(ui, repo, interactivecommitfunc, None,
                                    False, cmdutil.recordfilter, *pats, **opts)
        if not node:
            stat = repo.status(match=scmutil.match(repo[None], pats, opts))
            if stat.deleted:
                ui.status(_("nothing changed (%d missing files, see "
                            "'hg status')\n") % len(stat.deleted))
            else:
                ui.status(_("nothing changed\n"))
            return 1

        bases = list(mutableancestors(repo[node]))
        shelvedfile(repo, name, 'hg').writebundle(bases, node)
        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())

        _aborttransaction(repo)
    finally:
        lockmod.release(tr, lock, wlock)
Пример #13
0
def interactiveshelve(ui, repo, name, pats, opts):
    """interactively select changes to set aside"""
    if not ui.interactive:
        raise util.Abort(_('shelve --interactive can only be run interactively'))
    try:
        record = extensions.find('record')
    except KeyError:
        raise util.Abort(_("'record' extension not loaded"))

    # monkeypatch record extension
    record.hunk.__eq__ = hunk__eq__

    def shelvefunc(ui, repo, message, match, opts):
        files = []
        if match.files():
            changes = None
        else:
            changes = repo.status(match = match)[:3]
            modified, added, removed = changes
            files = modified + added + removed
            match = cmdutil.matchfiles(repo, files)
        diffopts = repo.attic.diffopts( {'git':True, 'nodates':True})
        chunks = patch.diff(repo, repo.dirstate.parents()[0], match = match,
                            changes = changes, opts = diffopts)
        fp = cStringIO.StringIO()
        fp.write(''.join(chunks))
        fp.seek(0)

        # 1. filter patch, so we have intending-to apply subset of it
        ac = record.parsepatch(fp)
        chunks = record.filterpatch(ui, ac)
        # and a not-intending-to apply subset of it
        rc = refilterpatch(ac, chunks)
        del fp

        contenders = {}
        for h in chunks:
            try: contenders.update(dict.fromkeys(h.files()))
            except AttributeError: pass

        newfiles = [f for f in files if f in contenders]

        if not newfiles:
            ui.status(_('no changes to shelve\n'))
            return 0

        modified = dict.fromkeys(changes[0])
        backups = {}
        backupdir = repo.join('shelve-backups')

        try:
            bkfiles = [f for f in newfiles if f in modified]
            backups = makebackup(ui, repo, backupdir, bkfiles)

            # patch to shelve
            sp = cStringIO.StringIO()
            for c in chunks:
                if c.filename() in backups:
                    c.write(sp)
            doshelve = sp.tell()
            sp.seek(0)

            # patch to apply to shelved files
            fp = cStringIO.StringIO()
            for c in rc:
                if c.filename() in backups:
                    c.write(fp)
            dopatch = fp.tell()
            fp.seek(0)

            try:
                # 3a. apply filtered patch to clean repo (clean)
                if backups:
                    hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)

                # 3b. apply filtered patch to clean repo (apply)
                if dopatch:
                    ui.debug(_('applying patch\n'))
                    ui.debug(fp.getvalue())
                    patch.internalpatch(fp, ui, 1, repo.root)
                del fp

                # 3c. apply filtered patch to clean repo (shelve)
                if doshelve:
                    ui.debug(_("saving patch to %s\n") % (name))
                    s = repo.attic
                    f = s.opener(name, 'w')
                    f.write(sp.getvalue())
                    del f
                    s.currentpatch = name
                    s.persiststate()
                del sp
            except:
                try:
                    for realname, tmpname in backups.iteritems():
                        ui.debug(_('restoring %r to %r\n') % (tmpname, realname))
                        util.copyfile(tmpname, repo.wjoin(realname))
                except OSError:
                    pass

            return 0
        finally:
            try:
                for realname, tmpname in backups.iteritems():
                    ui.debug(_('removing backup for %r : %r\n') % (realname, tmpname))
                    os.unlink(tmpname)
                os.rmdir(backupdir)
            except OSError:
                pass
    fancyopts.fancyopts([], commands.commitopts, opts)
    return cmdutil.commit(ui, repo, shelvefunc, pats, opts)
Пример #14
0
def shelve(ui, repo, *pats, **opts):
    '''interactively select changes to set aside

    If a list of files is omitted, all changes reported by "hg status"
    will be candidates for shelving.

    You will be prompted for whether to shelve changes to each
    modified file, and for files with multiple changes, for each
    change to use.

    The shelve command works with the Color extension to display
    diffs in color.

    On each prompt, the following responses are possible::

      y - shelve this change
      n - skip this change

      s - skip remaining changes to this file
      f - shelve remaining changes to this file

      d - done, skip remaining changes and files
      a - shelve all changes to all remaining files
      q - quit, shelving no changes

      ? - display help'''

    if not ui.interactive:
        raise util.Abort(_('shelve can only be run interactively'))

    # List all the active shelves by name and return '
    if opts['list']:
        listshelves(ui, repo)
        return

    forced = opts['force'] or opts['append']

    # Shelf name and path
    shelfname = opts.get('name')
    shelfpath = getshelfpath(repo, shelfname)

    if os.path.exists(repo.join(shelfpath)) and not forced:
        raise util.Abort(_('shelve data already exists'))

    def shelvefunc(ui, repo, message, match, opts):
        changes = repo.status(match=match)[:5]
        modified, added, removed = changes[:3]
        files = modified + added + removed
        diffopts = mdiff.diffopts(git=True, nodates=True)
        patch_diff = ''.join(
            patch.diff(repo,
                       repo.dirstate.parents()[0],
                       match=match,
                       changes=changes,
                       opts=diffopts))

        fp = cStringIO.StringIO(patch_diff)
        ac = parsepatch(fp)
        fp.close()

        chunks = filterpatch(ui, ac, not opts['all'])
        rc = refilterpatch(ac, chunks)

        contenders = {}
        for h in chunks:
            try:
                contenders.update(dict.fromkeys(h.files()))
            except AttributeError:
                pass

        newfiles = [f for f in files if f in contenders]

        if not newfiles:
            ui.status(_('no changes to shelve\n'))
            return 0

        modified = dict.fromkeys(changes[0])

        backupdir = repo.join('shelve-backups')

        try:
            bkfiles = [f for f in newfiles if f in modified]
            backups = makebackup(ui, repo, backupdir, bkfiles)

            # patch to shelve
            sp = cStringIO.StringIO()
            for c in chunks:
                if c.filename() in backups:
                    c.write(sp)
            doshelve = sp.tell()
            sp.seek(0)

            # patch to apply to shelved files
            fp = cStringIO.StringIO()
            for c in rc:
                if c.filename() in backups:
                    c.write(fp)
            dopatch = fp.tell()
            fp.seek(0)

            try:
                # 3a. apply filtered patch to clean repo (clean)
                if backups:
                    hg.revert(repo,
                              repo.dirstate.parents()[0], backups.has_key)

                # 3b. apply filtered patch to clean repo (apply)
                if dopatch:
                    ui.debug('applying patch\n')
                    ui.debug(fp.getvalue())
                    patch.internalpatch(fp, ui, 1, repo.root)
                del fp

                # 3c. apply filtered patch to clean repo (shelve)
                if doshelve:
                    ui.debug("saving patch to shelve\n")
                    if opts['append']:
                        f = repo.opener(shelfpath, "a")
                    else:
                        f = repo.opener(shelfpath, "w")
                    f.write(sp.getvalue())
                    del f
                del sp
            except:
                try:
                    for realname, tmpname in backups.iteritems():
                        ui.debug('restoring %r to %r\n' % (tmpname, realname))
                        util.copyfile(tmpname, repo.wjoin(realname))
                    ui.debug('removing shelve file\n')
                    os.unlink(repo.join(shelfpath))
                except OSError:
                    pass

            return 0
        finally:
            try:
                for realname, tmpname in backups.iteritems():
                    ui.debug('removing backup for %r : %r\n' %
                             (realname, tmpname))
                    os.unlink(tmpname)
                os.rmdir(backupdir)
            except OSError:
                pass

    fancyopts.fancyopts([], commands.commitopts, opts)
    return cmdutil.commit(ui, repo, shelvefunc, pats, opts)

    # wrap ui.write so diff output can be labeled/colorized
    def wrapwrite(orig, *args, **kw):
        label = kw.pop('label', '')
        for chunk, l in patch.difflabel(lambda: args):
            orig(chunk, label=label + l)

    oldwrite = ui.write
    extensions.wrapfunction(ui, 'write', wrapwrite)
    try:
        return cmdutil.commit(ui, repo, shelvefunc, pats, opts)
    finally:
        ui.write = oldwrite
Пример #15
0
def _docreatecmd(ui, repo, pats, opts):
    wctx = repo[None]
    parents = wctx.parents()
    if len(parents) > 1:
        raise error.Abort(_('cannot shelve while merging'))
    parent = parents[0]
    origbranch = wctx.branch()

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

    if not opts.get('message'):
        opts['message'] = desc

    lock = tr = activebookmark = None
    try:
        lock = repo.lock()

        # depending on whether shelve is traditional or
        # obsolescense-based, we either abort or commit this
        # transaction in the end. If we abort it, we don't
        # want to print anything to stderr
        report = None
        tr = repo.transaction('commit', report=report)

        interactive = opts.get('interactive', False)
        includeunknown = (opts.get('unknown', False) and
                          not opts.get('addremove', False))

        name = getshelvename(repo, parent, opts)
        activebookmark = _backupactivebookmark(repo)
        extra = {}
        if includeunknown:
            _includeunknownfiles(repo, pats, opts, extra)

        if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts):
            # In non-bare shelve we don't store newly created branch
            # at bundled commit
            repo.dirstate.setbranch(repo['.'].branch())

        commitfunc = getcommitfunc(extra, interactive, editor=True)
        if not interactive:
            node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
        else:
            node = cmdutil.dorecord(ui, repo, commitfunc, None,
                                    False, cmdutil.recordfilter, *pats,
                                    **opts)
        if not node:
            _nothingtoshelvemessaging(ui, repo, pats, opts)
            return 1

        _shelvecreatedcommit(ui, repo, node, name, tr)

        if ui.formatted():
            desc = util.ellipsis(desc, ui.termwidth())
        ui.status(_('shelved as %s\n') % name)
        # current wc parent may be already obsolete becuase
        # it might have been created previously and shelve just
        # reuses it
        hg.update(repo.unfiltered(), parent.node())
        if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts):
            repo.dirstate.setbranch(origbranch)

        _finishshelve(ui, repo, tr, node, activebookmark)
    finally:
        _restoreactivebookmark(repo, activebookmark)
        lockmod.release(tr, lock)
Пример #16
0
                ui.warn("removing backup failed: %s\n" % err)

    fancyopts.fancyopts([], commands.commitopts, opts)

    # wrap ui.write so diff output can be labeled/colorized
    def wrapwrite(orig, *args, **kw):
        label = kw.pop("label", "")
        if label:
            label += " "
        for chunk, l in patch.difflabel(lambda: args):
            orig(chunk, label=label + l)

    oldwrite = ui.write
    extensions.wrapfunction(ui, "write", wrapwrite)
    try:
        return cmdutil.commit(ui, repo, shelvefunc, pats, opts)
    finally:
        ui.write = oldwrite


def listshelves(ui, repo):
    # Check for shelve file at old location first
    if os.path.isfile(repo.join("shelve")):
        ui.status("default\n")

    # Now go through all the files in the shelves folder and list them out
    dirname = repo.join("shelves")
    if os.path.isdir(dirname):
        for filename in sorted(os.listdir(repo.join("shelves"))):
            ui.status(filename + "\n")
Пример #17
0
def interactiveshelve(ui, repo, name, pats, opts):
    """interactively select changes to set aside"""
    if not ui.interactive:
        raise util.Abort(
            _('shelve --interactive can only be run interactively'))
    try:
        record = extensions.find('record')
    except KeyError:
        raise util.Abort(_("'record' extension not loaded"))

    # monkeypatch record extension
    record.hunk.__eq__ = hunk__eq__

    def shelvefunc(ui, repo, message, match, opts):
        files = []
        if match.files():
            changes = None
        else:
            changes = repo.status(match=match)[:3]
            modified, added, removed = changes
            files = modified + added + removed
            match = cmdutil.matchfiles(repo, files)
        diffopts = repo.attic.diffopts({'git': True, 'nodates': True})
        chunks = patch.diff(repo,
                            repo.dirstate.parents()[0],
                            match=match,
                            changes=changes,
                            opts=diffopts)
        fp = cStringIO.StringIO()
        fp.write(''.join(chunks))
        fp.seek(0)

        # 1. filter patch, so we have intending-to apply subset of it
        ac = record.parsepatch(fp)
        chunks = record.filterpatch(ui, ac)
        # and a not-intending-to apply subset of it
        rc = refilterpatch(ac, chunks)
        del fp

        contenders = {}
        for h in chunks:
            try:
                contenders.update(dict.fromkeys(h.files()))
            except AttributeError:
                pass

        newfiles = [f for f in files if f in contenders]

        if not newfiles:
            ui.status(_('no changes to shelve\n'))
            return 0

        modified = dict.fromkeys(changes[0])
        backups = {}
        backupdir = repo.join('shelve-backups')

        try:
            bkfiles = [f for f in newfiles if f in modified]
            backups = makebackup(ui, repo, backupdir, bkfiles)

            # patch to shelve
            sp = cStringIO.StringIO()
            for c in chunks:
                if c.filename() in backups:
                    c.write(sp)
            doshelve = sp.tell()
            sp.seek(0)

            # patch to apply to shelved files
            fp = cStringIO.StringIO()
            for c in rc:
                if c.filename() in backups:
                    c.write(fp)
            dopatch = fp.tell()
            fp.seek(0)

            try:
                # 3a. apply filtered patch to clean repo (clean)
                if backups:
                    hg.revert(repo,
                              repo.dirstate.parents()[0], backups.has_key)

                # 3b. apply filtered patch to clean repo (apply)
                if dopatch:
                    ui.debug(_('applying patch\n'))
                    ui.debug(fp.getvalue())
                    patch.internalpatch(fp, ui, 1, repo.root)
                del fp

                # 3c. apply filtered patch to clean repo (shelve)
                if doshelve:
                    ui.debug(_("saving patch to %s\n") % (name))
                    s = repo.attic
                    f = s.opener(name, 'w')
                    f.write(sp.getvalue())
                    del f
                    s.currentpatch = name
                    s.persiststate()
                del sp
            except:
                try:
                    for realname, tmpname in backups.iteritems():
                        ui.debug(
                            _('restoring %r to %r\n') % (tmpname, realname))
                        util.copyfile(tmpname, repo.wjoin(realname))
                except OSError:
                    pass

            return 0
        finally:
            try:
                for realname, tmpname in backups.iteritems():
                    ui.debug(
                        _('removing backup for %r : %r\n') %
                        (realname, tmpname))
                    os.unlink(tmpname)
                os.rmdir(backupdir)
            except OSError:
                pass

    fancyopts.fancyopts([], commands.commitopts, opts)
    return cmdutil.commit(ui, repo, shelvefunc, pats, opts)
Пример #18
0
                hasmq = util.safehasattr(repo, 'mq')
                if hasmq:
                    saved, repo.mq.checkapplied = repo.mq.checkapplied, False

                try:
                    return repo.commit(message, 'shelve@localhost',
                                       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'))
Пример #19
0
                    os.unlink(tmpname)
                if tobackup:
                    os.rmdir(backupdir)
            except OSError:
                pass

    # wrap ui.write so diff output can be labeled/colorized
    def wrapwrite(orig, *args, **kw):
        label = kw.pop("label", "")
        for chunk, l in patch.difflabel(lambda: args):
            orig(chunk, label=label + l)

    oldwrite = ui.write
    extensions.wrapfunction(ui, "write", wrapwrite)
    try:
        return cmdutil.commit(ui, repo, recordfunc, pats, opts)
    finally:
        ui.write = oldwrite


cmdtable["qrecord"] = (qrecord, [], _("hg qrecord [OPTION]... PATCH [FILE]..."))  # placeholder until mq is available


def uisetup(ui):
    try:
        mq = extensions.find("mq")
    except KeyError:
        return

    cmdtable["qrecord"] = (
        qrecord,
Пример #20
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)
Пример #21
0
def _docreatecmd(ui, repo, pats, opts):
    def mutableancestors(ctx):
        """return all mutable ancestors for ctx (included)

        Much faster than the revset ancestors(ctx) & draft()"""
        seen = set([nodemod.nullrev])
        visit = collections.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 error.Abort(_('cannot shelve while merging'))
    parent = parents[0]
    origbranch = wctx.branch()

    # we never need the user, so we use a generic user for all shelve operations
    user = '******'
    label = repo._activebookmark 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)

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

    if not opts.get('message'):
        opts['message'] = desc

    name = opts.get('name')

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

        # 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 error.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 error.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 error.Abort(
                _('shelved change names may not contain slashes'))
        if name.startswith('.'):
            raise error.Abort(_("shelved change names may not start with '.'"))
        interactive = opts.get('interactive', False)
        includeunknown = (opts.get('unknown', False)
                          and not opts.get('addremove', False))

        extra = {}
        if includeunknown:
            s = repo.status(match=scmutil.match(repo[None], pats, opts),
                            unknown=True)
            if s.unknown:
                extra['shelve_unknown'] = '\0'.join(s.unknown)
                repo[None].add(s.unknown)

        if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts):
            # In non-bare shelve we don't store newly created branch
            # at bundled commit
            repo.dirstate.setbranch(repo['.'].branch())

        def commitfunc(ui, repo, message, match, opts):
            hasmq = util.safehasattr(repo, 'mq')
            if hasmq:
                saved, repo.mq.checkapplied = repo.mq.checkapplied, False
            backup = repo.ui.backupconfig('phases', 'new-commit')
            try:
                repo.ui.setconfig('phases', 'new-commit', phases.secret)
                editor = cmdutil.getcommiteditor(editform='shelve.shelve',
                                                 **opts)
                return repo.commit(message,
                                   user,
                                   opts.get('date'),
                                   match,
                                   editor=editor,
                                   extra=extra)
            finally:
                repo.ui.restoreconfig(backup)
                if hasmq:
                    repo.mq.checkapplied = saved

        def interactivecommitfunc(ui, repo, *pats, **opts):
            match = scmutil.match(repo['.'], pats, {})
            message = opts['message']
            return commitfunc(ui, repo, message, match, opts)

        if not interactive:
            node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
        else:
            node = cmdutil.dorecord(ui, repo, interactivecommitfunc, None,
                                    False, cmdutil.recordfilter, *pats, **opts)
        if not node:
            stat = repo.status(match=scmutil.match(repo[None], pats, opts))
            if stat.deleted:
                ui.status(
                    _("nothing changed (%d missing files, see "
                      "'hg status')\n") % len(stat.deleted))
            else:
                ui.status(_("nothing changed\n"))
            return 1

        bases = list(mutableancestors(repo[node]))
        shelvedfile(repo, name, 'hg').writebundle(bases, node)
        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())
        if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts):
            repo.dirstate.setbranch(origbranch)

        _aborttransaction(repo)
    finally:
        lockmod.release(tr, lock)
Пример #22
0
def _dounshelve(ui, repo, *shelved, **opts):
    abortf = opts.get('abort')
    continuef = opts.get('continue')
    if not abortf and not continuef:
        cmdutil.checkunfinished(repo)

    if abortf or continuef:
        if abortf and continuef:
            raise error.Abort(_('cannot use both abort and continue'))
        if shelved:
            raise error.Abort(_('cannot combine abort/continue with '
                               'naming a shelved change'))
        if abortf and opts.get('tool', False):
            ui.warn(_('tool option will be ignored\n'))

        try:
            state = shelvedstate.load(repo)
        except IOError as err:
            if err.errno != errno.ENOENT:
                raise
            cmdutil.wrongtooltocontinue(repo, _('unshelve'))
        except error.CorruptedState as err:
            ui.debug(str(err) + '\n')
            if continuef:
                msg = _('corrupted shelved state file')
                hint = _('please run hg unshelve --abort to abort unshelve '
                         'operation')
                raise error.Abort(msg, hint=hint)
            elif abortf:
                msg = _('could not read shelved state file, your working copy '
                        'may be in an unexpected state\nplease update to some '
                        'commit\n')
                ui.warn(msg)
                shelvedstate.clear(repo)
            return

        if abortf:
            return unshelveabort(ui, repo, state, opts)
        elif continuef:
            return unshelvecontinue(ui, repo, state, opts)
    elif len(shelved) > 1:
        raise error.Abort(_('can only unshelve one change at a time'))
    elif not shelved:
        shelved = listshelves(repo)
        if not shelved:
            raise error.Abort(_('no shelved changes to apply!'))
        basename = util.split(shelved[0][1])[1]
        ui.status(_("unshelving change '%s'\n") % basename)
    else:
        basename = shelved[0]

    if not shelvedfile(repo, basename, 'patch').exists():
        raise error.Abort(_("shelved change '%s' not found") % basename)

    oldquiet = ui.quiet
    lock = tr = None
    forcemerge = ui.backupconfig('ui', 'forcemerge')
    try:
        ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'unshelve')
        lock = repo.lock()

        tr = repo.transaction('unshelve', report=lambda x: None)
        oldtiprev = len(repo)

        pctx = repo['.']
        tmpwctx = pctx
        # The goal is to have a commit structure like so:
        # ...-> pctx -> tmpwctx -> shelvectx
        # where tmpwctx is an optional commit with the user's pending changes
        # and shelvectx is the unshelved changes. Then we merge it all down
        # to the original pctx.

        # Store pending changes in a commit and remember added in case a shelve
        # contains unknown files that are part of the pending change
        s = repo.status()
        addedbefore = frozenset(s.added)
        if s.modified or s.added or s.removed or s.deleted:
            ui.status(_("temporarily committing pending changes "
                        "(restore with 'hg unshelve --abort')\n"))
            def commitfunc(ui, repo, message, match, opts):
                hasmq = util.safehasattr(repo, 'mq')
                if hasmq:
                    saved, repo.mq.checkapplied = repo.mq.checkapplied, False

                backup = repo.ui.backupconfig('phases', 'new-commit')
                try:
                    repo.ui.setconfig('phases', 'new-commit', phases.secret)
                    return repo.commit(message, 'shelve@localhost',
                                       opts.get('date'), match)
                finally:
                    repo.ui.restoreconfig(backup)
                    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()

        ui.quiet = oldquiet

        shelvectx = repo['tip']

        branchtorestore = ''
        if shelvectx.branch() != shelvectx.p1().branch():
            branchtorestore = shelvectx.branch()

        # 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,
                    'tool' : opts.get('tool', ''),
                })
            except error.InterventionRequired:
                tr.close()

                stripnodes = [repo.changelog.node(rev)
                              for rev in xrange(oldtiprev, len(repo))]
                shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes,
                                  branchtorestore)

                util.rename(repo.join('rebasestate'),
                            repo.join('unshelverebasestate'))
                raise error.InterventionRequired(
                    _("unresolved conflicts (see 'hg resolve', then "
                      "'hg unshelve --continue')"))

            # refresh ctx after rebase completes
            shelvectx = repo['tip']

            if not shelvectx in tmpwctx.children():
                # rebase was a no-op, so it produced no child commit
                shelvectx = tmpwctx

        mergefiles(ui, repo, pctx, shelvectx)
        restorebranch(ui, repo, branchtorestore)

        # Forget any files that were unknown before the shelve, unknown before
        # unshelve started, but are now added.
        shelveunknown = shelvectx.extra().get('shelve_unknown')
        if shelveunknown:
            shelveunknown = frozenset(shelveunknown.split('\0'))
            addedafter = frozenset(repo.status().added)
            toforget = (addedafter & shelveunknown) - addedbefore
            repo[None].forget(toforget)

        shelvedstate.clear(repo)

        # The transaction aborting will strip all the commits for us,
        # but it doesn't update the inmemory structures, so addchangegroup
        # hooks still fire and try to operate on the missing commits.
        # Clean up manually to prevent this.
        repo.unfiltered().changelog.strip(oldtiprev, tr)

        unshelvecleanup(ui, repo, basename, opts)

        _aborttransaction(repo)
    finally:
        ui.quiet = oldquiet
        if tr:
            tr.release()
        lockmod.release(lock)
        ui.restoreconfig(forcemerge)