Пример #1
0
def uncommit(ui, repo, *pats, **opts):
    """uncommit part or all of a local changeset

    This command undoes the effect of a local commit, returning the affected
    files to their uncommitted state. This means that files modified or
    deleted in the changeset will be left unchanged, and so will remain
    modified in the working directory.

    If no files are specified, the commit will be pruned, unless --keep is
    given.
    """
    opts = pycompat.byteskwargs(opts)

    cmdutil.checknotesize(ui, opts)
    cmdutil.resolvecommitoptions(ui, opts)

    with repo.wlock(), repo.lock():

        m, a, r, d = repo.status()[:4]
        isdirtypath = any(set(m + a + r + d) & set(pats))
        allowdirtywcopy = opts[
            b'allow_dirty_working_copy'] or repo.ui.configbool(
                b'experimental', b'uncommitondirtywdir')
        if not allowdirtywcopy and (not pats or isdirtypath):
            cmdutil.bailifchanged(
                repo,
                hint=_(b'requires --allow-dirty-working-copy to uncommit'),
            )
        old = repo[b'.']
        rewriteutil.precheck(repo, [old.rev()], b'uncommit')
        if len(old.parents()) > 1:
            raise error.Abort(_(b"cannot uncommit merge changeset"))

        match = scmutil.match(old, pats, opts)

        # Check all explicitly given files; abort if there's a problem.
        if match.files():
            s = old.status(old.p1(), match, listclean=True)
            eligible = set(s.added) | set(s.modified) | set(s.removed)

            badfiles = set(match.files()) - eligible

            # Naming a parent directory of an eligible file is OK, even
            # if not everything tracked in that directory can be
            # uncommitted.
            if badfiles:
                badfiles -= {f for f in util.dirs(eligible)}

            for f in sorted(badfiles):
                if f in s.clean:
                    hint = _(
                        b"file was not changed in working directory parent")
                elif repo.wvfs.exists(f):
                    hint = _(b"file was untracked in working directory parent")
                else:
                    hint = _(b"file does not exist")

                raise error.Abort(
                    _(b'cannot uncommit "%s"') % scmutil.getuipathfn(repo)(f),
                    hint=hint,
                )

        with repo.transaction(b'uncommit'):
            if not (opts[b'message'] or opts[b'logfile']):
                opts[b'message'] = old.description()
            message = cmdutil.logmessage(ui, opts)

            keepcommit = pats
            if not keepcommit:
                if opts.get(b'keep') is not None:
                    keepcommit = opts.get(b'keep')
                else:
                    keepcommit = ui.configbool(b'experimental',
                                               b'uncommit.keep')
            newid = _commitfiltered(
                repo,
                old,
                match,
                keepcommit,
                message=message,
                user=opts.get(b'user'),
                date=opts.get(b'date'),
            )
            if newid is None:
                ui.status(_(b"nothing to uncommit\n"))
                return 1

            mapping = {}
            if newid != old.p1().node():
                # Move local changes on filtered changeset
                mapping[old.node()] = (newid, )
            else:
                # Fully removed the old commit
                mapping[old.node()] = ()

            with repo.dirstate.parentchange():
                scmutil.movedirstate(repo, repo[newid], match)

            scmutil.cleanupnodes(repo, mapping, b'uncommit', fixphase=True)
Пример #2
0
def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
            newincludes, newexcludes, force):
    oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
    newmatch = narrowspec.match(repo.root, newincludes, newexcludes)

    # This is essentially doing "hg outgoing" to find all local-only
    # commits. We will then check that the local-only commits don't
    # have any changes to files that will be untracked.
    unfi = repo.unfiltered()
    outgoing = discovery.findcommonoutgoing(unfi, remote,
                                            commoninc=commoninc)
    ui.status(_('looking for local changes to affected paths\n'))
    localnodes = []
    for n in itertools.chain(outgoing.missing, outgoing.excluded):
        if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
            localnodes.append(n)
    revstostrip = unfi.revs('descendants(%ln)', localnodes)
    hiddenrevs = repoview.filterrevs(repo, 'visible')
    visibletostrip = list(repo.changelog.node(r)
                          for r in (revstostrip - hiddenrevs))
    if visibletostrip:
        ui.status(_('The following changeset(s) or their ancestors have '
                    'local changes not on the remote:\n'))
        maxnodes = 10
        if ui.verbose or len(visibletostrip) <= maxnodes:
            for n in visibletostrip:
                ui.status('%s\n' % node.short(n))
        else:
            for n in visibletostrip[:maxnodes]:
                ui.status('%s\n' % node.short(n))
            ui.status(_('...and %d more, use --verbose to list all\n') %
                      (len(visibletostrip) - maxnodes))
        if not force:
            raise error.Abort(_('local changes found'),
                              hint=_('use --force-delete-local-changes to '
                                     'ignore'))

    with ui.uninterruptable():
        if revstostrip:
            tostrip = [unfi.changelog.node(r) for r in revstostrip]
            if repo['.'].node() in tostrip:
                # stripping working copy, so move to a different commit first
                urev = max(repo.revs('(::%n) - %ln + null',
                                     repo['.'].node(), visibletostrip))
                hg.clean(repo, urev)
            overrides = {('devel', 'strip-obsmarkers'): False}
            with ui.configoverride(overrides, 'narrow'):
                repair.strip(ui, unfi, tostrip, topic='narrow')

        todelete = []
        for f, f2, size in repo.store.datafiles():
            if f.startswith('data/'):
                file = f[5:-2]
                if not newmatch(file):
                    todelete.append(f)
            elif f.startswith('meta/'):
                dir = f[5:-13]
                dirs = ['.'] + sorted(util.dirs({dir})) + [dir]
                include = True
                for d in dirs:
                    visit = newmatch.visitdir(d)
                    if not visit:
                        include = False
                        break
                    if visit == 'all':
                        break
                if not include:
                    todelete.append(f)

        repo.destroying()

        with repo.transaction("narrowing"):
            for f in todelete:
                ui.status(_('deleting %s\n') % f)
                util.unlinkpath(repo.svfs.join(f))
                repo.store.markremoved(f)

            _narrowcleanupwdir(repo, oldincludes, oldexcludes, newincludes,
                               newexcludes, oldmatch, newmatch)
            repo.setnarrowpats(newincludes, newexcludes)

        repo.destroyed()
Пример #3
0
 def _dirs(self):
     return util.dirs(self)