示例#1
0
 def _count(m):
     g = m.groups()
     if g[1]:
         query = [g[1][1:]]
     else:
         query = ['all()']
     return _with_groups(g, str(len(cmdutil.revrange(repo, query))))
示例#2
0
 def _count(m):
     g = m.groups()
     if g[1]:
         query = [g[1][1:]] 
     else:
         query = ['all()']
     return _with_groups(g, str(len(cmdutil.revrange(repo, query))))
示例#3
0
 def update_by_row(self, row):
     branch = row[M_NAME]
     rev = cmdutil.revrange(self.repo, [branch])
     parents = [x.node() for x in self.repo.parents()]
     dialog = update.UpdateDialog(rev[0])
     self.show_dialog(dialog)
     self.update_completed(parents)
示例#4
0
def get_revs(repo, rev_opt):
    if rev_opt:
        revs = revrange(repo, rev_opt)
        if len(revs) == 0:
            return (nullrev, nullrev)
        return (max(revs), min(revs))
    else:
        return (len(repo) - 1, 0)
示例#5
0
 def getpatches(revs):
     for r in cmdutil.revrange(repo, revs):
         output = cStringIO.StringIO()
         patch.export(repo, [r], fp=output,
                      opts=patch.diffopts(ui, opts))
         yield output.getvalue().split('\n')
示例#6
0
 def _count(m):
     g = m.groups()
     query = [g[1][1:]] if g[1] else ["all()"]
     return _with_groups(g, str(len(cmdutil.revrange(repo, query))))
示例#7
0
def collapse(ui, repo, **opts):
    """collapse multiple revisions into one

    Collapse combines multiple consecutive changesets into a single changeset,
    preserving any descendants of the final changeset. The commit messages for
    the collapsed changesets are concatenated and may be edited before the
    collapse is completed.
    """

    rng = cmdutil.revrange(repo, opts['rev'])
    if not rng:
        raise util.Abort(_('no revisions specified'))

    first = rng[0]
    last = rng[-1]
    revs = inbetween(repo, first, last)

    if not revs:
        raise util.Abort(_('revision %s is not an ancestor of revision %s\n') %
                            (first, last))
    elif len(revs) == 1:
        raise util.Abort(_('only one revision specified'))

    ui.debug(_('Collapsing revisions %s\n') % revs)

    for r in revs:
        if repo[r].user() != ui.username() and not opts['force']:
            raise util.Abort(_('revision %s does not belong to %s\n') %
                (r, ui.username()))
        if r != last:
            children = repo[r].children()
            if len(children) > 1:
                for c in children:
                    if not c.rev() in revs:
                        raise util.Abort(_('revision %s has child %s not '
                            'being collapsed, please rebase\n') % (r, c.rev()))
        if r != first:
            parents = repo[r].parents()
            if len(parents) > 1:
                for p in parents:
                    if not p.rev() in revs:
                        raise util.Abort(_('revision %s has parent %s not '
                            'being collapsed.') % (r, p.rev()))

    if len(repo[first].parents()) > 1:
        raise util.Abort(_('start revision %s has multiple parents, '
            'won\'t collapse.') % first)

    cmdutil.bail_if_changed(repo)

    parent = repo[first].parents()[0]
    tomove = list(repo.changelog.descendants(last))
    movemap = dict.fromkeys(tomove, nullrev)
    ui.debug(_('will move revisions: %s\n') % tomove)

    origparent = repo['.'].rev()
    collapsed = None

    try:
        branch = repo[last].branch()
        collapsed = makecollapsed(ui, repo, parent, revs, branch, opts)
        movemap[max(revs)] = collapsed
        movedescendants(ui, repo, collapsed, tomove, movemap)
    except:
        merge.update(repo, repo[origparent].rev(), False, True, False)
        if collapsed:
            repair.strip(ui, repo, collapsed.node(), "strip")
        raise

    if not opts['keep']:
        ui.debug(_('stripping revision %d\n') % first)
        repair.strip(ui, repo, repo[first].node(), "strip")

    ui.status(_('collapse completed\n'))
示例#8
0
文件: paste.py 项目: dangra/dotfiles
def paste(ui, repo, *fnames, **opts):
    '''send diffs from Mercurial to various pastebin websites
    
    Send a diff of the specified files to a pastebin website to easily
    share with other people.  If no files are specified all files will
    be included.
    
    To paste a diff of all uncommitted changes in the working directory:
    
        hg paste
    
    To paste the changes that revision REV made:
    
        hg paste -r REV
    
    To paste the changes between revisions REV1 and REV2:
    
        hg paste -r REV1:REV2
    
    Several options can be used to specify more metadata about the paste:
    
        hg paste --user Steve --title 'Progress on feature X' --keep
    
    The pastebin website to use can be specified with --dest.  See
    'hg help pastebins' for more information.
    
    '''
    dest = opts.pop('dest')
    dry = opts.pop('dry_run')
    if not dest:
        dest = 'dpaste'

    handler = globals().get('_paste_' + dest.replace('.', '_'))
    if not handler:
        raise util.Abort('unknown pastebin (see "hg help pastebins")!')

    if not opts['user']:
        opts['user'] = ui.username().replace('<', '').replace('>', '')

    if opts['rev'] and opts['stdin']:
        raise util.Abort('--rev and --stdin options are mutually exclusive')

    if opts['stdin']:
        content = sys.stdin.read()
    else:
        ui.pushbuffer()
        if opts['rev']:
            rev = opts.pop('rev')
            revs = cmdutil.revrange(repo, rev)

            if len(revs) == 1:
                opts['change'] = revs[0]
            else:
                opts['rev'] = rev

            commands.diff(ui, repo, *fnames, **opts)
        else:
            commands.diff(ui, repo, *fnames, **opts)
        content = ui.popbuffer()

    if not content.strip():
        raise util.Abort('nothing to paste!')

    if ui.verbose:
        ui.status('Pasting:\n%s\n' % content)

    if not dry:
        url = _paste(handler, content, opts)
        ui.write('%s\n' % url)
示例#9
0
文件: tasks.py 项目: Evanlec/config
def task(ui, repo, task=None, rev=None, force=False, delete=False, info=None,
        all=False, complete=None, resume=None, delete_all=False,
        delete_complete=False, rename=None, trim=None, append=None,
        purge_stash=False):
    '''mercurial tasks

    Tasks are collections of contiguous changesets.  A task can be short or long
    lived, but its information is only available locally.  This extension
    overrides the behaviour of push so that it will not push incomplete tasks by
    default. A task can be in one of 3 states:
        * new - this task has no changesets only a parent node
        * active - a task with some changesets but not complete
        * complete - a task with some changesets and marked as complete

    Use 'hg tasks' to list all new and active tasks.  'hg tasks --all' will show
    completed tasks along with those that are new and active.

    This extension adds two new options to push:
            --all-tasks        will push all taks even incomplete ones
            --completed-tasks  will only push completed tasks

    Use 'hg update [TASK]' to update to the tip node of a task and set thate
    task as current.  When a task is set as current, all subsequent commits will
    be assoticated with the task.

    Use 'hg log [TASK]' to view a log of all changesets associated with the
    given task.

    Use 'hg export [TASK]' to export all changesets associated with the given
    task.

    Use 'hg email -t [TASK]' to create a patchbomb containing all changesets
    associated with the given task.

    Use 'hg transplant -t [TASK]' to transplant all associated changsets in a
    task.

    If the auto.track.new config option is True, newly created tasks will
    automatically be set as current.

    If the auto.stash config option is True, your working copy will
    automatically be stashed when you update away from the current task.  When
    returning to a previously stashed task by updating to it, your working copy
    will be restored from the stash.
    '''
    tasks = parse(repo)

    # all these options need a valid task name
    if delete or info or complete or resume or trim or append \
        or (purge_stash and not all):
        if task == None:
            raise util.Abort(_('task name required'))
        if task not in tasks:
            raise util.Abort(_('a task of this name does not exist'))

    # all these options don't want a task
    if delete_all or delete_complete or (purge_stash and all):
        if task:
            raise util.Abort(_('no task name required for this option'))

    # check for force when deleting tasks with stash
    if delete_all or delete_complete or delete:
        if not force:
            if delete_complete:
                deltasks = [t for t in tasks if tasks[t]['state'] == 2]
            elif delete_all:
                deltasks = tasks
            else:
                deltasks = [ task ]

            stashedtasks = [ t for t in deltasks if hasstash(repo, t) ]
            if len(stashedtasks):
                if delete:
                    raise util.Abort(
                        _('task has stash info, use -f to lose stash'))
                else:
                    raise util.Abort(_('deleting tasks with stash info, use -f'
                        ' to lose stashes\ntasks with stash info: %s' %
                        ' '.join(stashedtasks)))

    # delete all
    if delete_all:
        for t in tasks:
            removestashfiles(repo, t)
        write(ui, repo, {})
        return

    # delete complete
    if delete_complete:
        deltasks = [t for t in tasks if tasks[t]['state'] == 2]
        update = False
        for t in deltasks:
            removestashfiles(repo, t)
            del tasks[t]
            update = True
        if update:
            write(ui, repo, tasks)
        return

    # rename a task
    if rename:
        if rename not in tasks:
            raise util.Abort(_('a task of this name does not exist'))
        if task in tasks and not force:
            raise util.Abort(_('a task of the same name already exists'))
        if task is None:
            raise util.Abort(_('new task name required'))
        oldstash = stashfiles(repo, rename)
        newstash = stashfiles(repo, task)
        for i in range(len(oldstash)):
            if os.path.exists(repo.join(oldstash[i])):
                shutil.move(repo.join(oldstash[i]), repo.join(newstash[i]))

        tasks[task] = tasks[rename]
        del tasks[rename]
        if current(repo) == rename:
            setcurrent(ui, repo, task)
        write(ui, repo, tasks)
        return

    # delete a task
    if delete:
        if task == current(repo):
            setcurrent(ui, repo, None)
        removestashfiles(repo, task)
        del tasks[task]
        write(ui, repo, tasks)
        return

    # complete a task
    if complete:
        if tasks[task]['state'] == 2:
            raise util.Abort(_('task is already completed'))
        if tasks[task]['state'] == 0:
            raise util.Abort(_('no changesets in task'))
        if not force and hasstash(repo, task):
            raise util.Abort(_('task has stash, use -f to lose stash'))
        # when checking for working copy changes here, we are bit more lax and
        # not worried about unknown files...
        if not force and task == current(repo) and trackingcurrent(repo) \
            and bool(filter(None, repo.status())):
            raise util.Abort(_('uncommited changes in current task, use -f to '
                'complete anyway'))

        tasks[task]['state'] = 2
        if task == current(repo):
            setcurrent(ui, repo, None)
        write(ui, repo, tasks)
        removestashfiles(repo, task)
        return

    # resume a task  (should we set to current and update to it???)
    if resume:
        if not tasks[task]['state'] == 2:
            raise util.Abort(_('task is not completed'))
        tasks[task]['state'] = 1
        write(ui, repo, tasks)
        return

    # show info on a given task
    if info:
        tinfo = tasks[task]
        ui.write('task:            %s\n' % task)
        ui.write('parent:          %s\n' % prettynode(ui, repo,
            tinfo['parent']))

        if task == current(repo) and trackingcurrent(repo):
            stashstr = bool(filter(None, repo.status())) and ' + changes' or ''
        else:
            stashstr = hasstash(repo, task) and ' + stash' or ''

        if tinfo['state'] == 0: state = 'new'
        if tinfo['state'] == 1: state = 'active'
        if tinfo['state'] == 2: state = 'completed'
        ui.write('state:           %s%s\n' % (state, stashstr))
        if tinfo['state'] != 0:
            nodes = tasknodes(repo, tinfo)
            nodes.reverse()
            changesets = 'changesets (%d):' % len(nodes)
            ui.write('%-16s %s\n' % (changesets, prettynode(ui, repo,
                nodes[0])))
            for n in nodes[1:]:
                ui.write('                 %s\n' % prettynode(ui, repo, n))
        return

    # trim a task
    if trim:
        if not rev:
            raise util.Abort(_('revision required'))
        else:
            trimnode = repo.lookup(rev)
            tnodes = tasknodes(repo, tasks[task])
            if trimnode not in tnodes:
                raise util.Abort(_('revision not in task'))
            if trimnode == tasks[task]['start']:
                tasks[task]['start'] = None
                tasks[task]['end'] = None
                tasks[task]['state'] = 0
            else:
                newend = tnodes[tnodes.index(trimnode) - 1]
                if newend == tasks[task]['start']:
                    tasks[task]['end'] = None
                else:
                    tasks[task]['end'] = newend
                tasks[task]['state'] = 1
            write(ui, repo, tasks)
        return

    # append to task
    if append:
        if not rev:
            raise util.Abort(_('revision required'))
        anode = repo.lookup(rev)
        nodes = repo.changelog.nodesbetween([tasknode(repo, tasks[task])], [anode])[0]
        if len(nodes) < 2:
            raise util.Abort(_('no new revs to add'))
        if len ([n for n in nodes if repo.changelog.parents(n)[1] != nullid]):
            raise util.Abort(
                _('tasks cannot include merged nodes (nodes with two parents)'))

        # now actually append
        if tasks[task]['state'] == 0: # new
            tasks[task]['start'] = nodes[1]
            if len(nodes) > 2:
                tasks[task]['end'] = nodes[-1]
            tasks[task]['state'] = 1
        else:
            tasks[task]['end'] = nodes[-1]
        write(ui, repo, tasks)
        return

    # purge stash
    if purge_stash:
        purgetasks = all and tasks or [ task ]
        for t in purgetasks:
            removestashfiles(repo, t)
        return

    # create a task
    if task != None:
        if '\n' in task or ' ' in task or ',' in task:
            raise util.Abort(
                _('task name cannot contain newlines, spaces, or commas'))
        task = task.strip()
        if task in tasks and not force:
            raise util.Abort(_('a task of the same name already exists'))
        if ((task in repo.branchtags() or task == repo.dirstate.branch()) and
                not force):
            raise util.Abort(
                _('a task cannot have the name of an existing branch'))
        if (task in repo.tags() and not force):
            raise util.Abort(_('a tag of the same name already exists'))
        if rev:
            nodes = [ repo.lookup(r) for r in cmdutil.revrange(repo, [rev]) ]
            parentnode = nodes[0]
            endnode = None
            startnode = None
            state = 0
            if len(nodes) > 1:
                if len ([n for n in nodes[1:]
                    if repo.changelog.parents(n)[1] != nullid]):
                    raise util.Abort(_('tasks cannot include merged nodes '
                        '(nodes with two parents)'))
                startnode = nodes[1]
                state = 1
            if len(nodes) > 2:
                endnode = nodes[-1]
            tasks[task] = {'parent': parentnode, 'start': startnode, 'end':
                    endnode, 'state': state }
        else:
            tasks[task] = {'parent': repo.changectx('.').node(), 'start': None,
                    'end': None, 'state': 0 }
        write(ui, repo, tasks)

        # handle the auto tracking of newly created tasks on current node
        if ui.configbool('tasks', 'auto.track.new'):
            try:
                opts = {'rev':None, 'clean':False, 'date':None }
                tasksupdate(commands.update, ui, repo, *[ task ], **opts)
            except Exception, exception:
                raise type(exception)( str(exception)
                    + "\nwarning: new task '%s' created but not set to current"
                    %task)
        return
示例#10
0
def collapse(ui, repo, **opts):
    """collapse multiple revisions into one

    Collapse combines multiple consecutive changesets into a single changeset,
    preserving any descendants of the final changeset. The commit messages for
    the collapsed changesets are concatenated and may be edited before the
    collapse is completed.
    """

    hg_vsn = re.match(r"[0-9.]+", util.version()).group(0)
    vsn_list = [int(x) for x in hg_vsn.split(".")]
    if vsn_list < [2, 0]:
        raise util.Abort(
            _('Mercurial version to low (%s), '
              'you need at least 2.0') % hg_vsn)

    try:
        from mercurial import scmutil
        rng = scmutil.revrange(repo, opts['rev'])
    except ImportError:
        rng = cmdutil.revrange(repo, opts['rev'])

    # run collapse in the repository root
    olddir = os.getcwd()
    os.chdir(repo.root)
    try:
        if opts['movelog']:
            movelog = open(opts['movelog'], 'a')
        else:
            movelog = False

        if opts['timedelta']:
            timedelta = float(opts['timedelta'])
        else:
            timedelta = float('inf')

        if not opts['auto']:
            if not rng:
                raise util.Abort(_('no revisions specified'))

            if opts['timedelta']:
                raise util.Abort(_('-t or --timedelta only valid with --auto'))
            if opts['userchange']:
                raise util.Abort(
                    _('-u or --userchange only valid with --auto'))
            # FIXME add more options that don't work
            # FIXME FIXME: rework ui to make the distinction between auto
            # and not unnecessary.  Integrate revsets (event disjoint)

            first = rng[0]
            last = rng[-1]
            revs = inbetween(repo, first, last)

            if not revs:
                raise util.Abort(
                    _('revision %s is not an ancestor '
                      'of revision %s\n') % (first, last))
            elif len(revs) == 1:
                raise util.Abort(_('only one revision specified'))
            do_collapse(ui, repo, first, last, revs, movelog, timedelta, opts)

        else:  # auto mode
            if len(rng) == 0:
                start = 0
            elif len(rng) == 1:
                start = rng[0]
            else:
                util.Abort(_('multiple revisions specified with auto mode'))

            count = 0
            while count < 1 or opts['repeat']:
                if opts['usefirst']:
                    revs = find_first_chunk(ui, repo, start, timedelta, opts)
                else:
                    revs = find_last_chunk(ui, repo, start, timedelta, opts)

                if not revs:
                    if count == 0:
                        raise util.Abort(_('no revision chunk found\n'))
                    else:
                        break

                first = min(revs)
                last = max(revs)

                assert len(revs) > 1
                do_collapse(ui, repo, first, last, revs, movelog, timedelta,
                            opts)
                count += 1
    finally:
        os.chdir(olddir)
示例#11
0
def collapse(ui, repo, **opts):
    """collapse multiple revisions into one

    Collapse combines multiple consecutive changesets into a single changeset,
    preserving any descendants of the final changeset. The commit messages for
    the collapsed changesets are concatenated and may be edited before the
    collapse is completed.
    """

    hg_vsn = util.version()
    vsn_list = [ int(x) for x in hg_vsn.split(".") ]
    if vsn_list < [2,0]:
        raise util.Abort(_('Mercurial version to low (%s), '
                           'you need at least 2.0') % hg_vsn)

    try:
        from mercurial import scmutil
        rng = scmutil.revrange(repo, opts['rev'])
    except ImportError:
        rng = cmdutil.revrange(repo, opts['rev'])

    if opts['movelog']:
        movelog = open(opts['movelog'], 'a')
    else:
        movelog = False
            
    if opts['timedelta']:
        timedelta = float(opts['timedelta'])
    else:
        timedelta = float('inf')

    if not opts['auto']:
        if not rng:
            raise util.Abort(_('no revisions specified'))

        if opts['timedelta']:
            raise util.Abort(_('-t or --timedelta only valid with --auto'))
        if opts['userchange']:
            raise util.Abort(_('-u or --userchange only valid with --auto'))
        # FIXME add more options that don't work
        # FIXME FIXME: rework ui to make the distinction between auto
        # and not unnecessary.  Integrate revsets (event disjoint)

        first = rng[0]
        last = rng[-1]
        revs = inbetween(repo, first, last)

        if not revs:
            raise util.Abort(_('revision %s is not an ancestor '
                               'of revision %s\n') % (first, last))
        elif len(revs) == 1:
            raise util.Abort(_('only one revision specified'))
        do_collapse(ui, repo, first, last, revs, movelog, timedelta,
            opts)

    else:                       # auto mode
        if len(rng) == 0:
            start = 0
        elif len(rng) == 1:
            start = rng[0]
        else:
            util.Abort(_('multiple revisions specified with auto mode'))

        count = 0
        while count < 1 or opts['repeat']:
            if opts['usefirst']:
                revs = find_first_chunk(ui, repo, start, timedelta, opts)
            else:
                revs = find_last_chunk(ui, repo, start, timedelta, opts)

            if not revs:
                if count == 0:
                    raise util.Abort(_('no revision chunk found\n'))
                else:
                    break

            first = min(revs)
            last = max(revs)

            assert len(revs) > 1
            do_collapse(ui, repo, first, last, revs, movelog, timedelta, opts)
            count += 1
示例#12
0
def paste(ui, repo, *fnames, **opts):
    '''send diffs from Mercurial to various pastebin websites
    
    Send a diff of the specified files to a pastebin website to easily
    share with other people.  If no files are specified all files will
    be included.
    
    To paste a diff of all uncommitted changes in the working directory:
    
        hg paste
    
    To paste the changes that revision REV made:
    
        hg paste -r REV
    
    To paste the changes between revisions REV1 and REV2:
    
        hg paste -r REV1:REV2
    
    Several options can be used to specify more metadata about the paste:
    
        hg paste --user Steve --title 'Progress on feature X' --keep
    
    The pastebin website to use can be specified with --dest.  See
    'hg help pastebins' for more information.
    
    '''
    dest = opts.pop('dest')
    dry = opts.pop('dry_run')
    if not dest:
        dest = 'dpaste'
    if dest not in pastebins:
        raise util.Abort('unknown pastebin (see "hg help pastebins")!')

    if not opts['user']:
        opts['user'] = ui.username().replace('<', '').replace('>', '')

    if opts['rev'] and opts['stdin']:
        raise util.Abort('--rev and --stdin options are mutually exclusive')

    if opts['stdin']:
        content = sys.stdin.read()
    else:
        ui.pushbuffer()
        if opts['rev']:
            rev = opts.pop('rev')
            revs = cmdutil.revrange(repo, rev)

            if len(revs) == 1:
                opts['change'] = revs[0]
            else:
                opts['rev'] = rev

            commands.diff(ui, repo, *fnames, **opts)
        else:
            commands.diff(ui, repo, *fnames, **opts)
        content = ui.popbuffer()

    if not content.strip():
        raise util.Abort('nothing to paste!')

    if ui.verbose:
        ui.status('Pasting:\n%s\n' % content)
    
    if not dry:
        url = pastebins[dest]['handler'](content=content, **opts)
        ui.write('%s\n' % url)
示例#13
0
文件: churn.py 项目: carlgao/lenga
        return aliases

    amap = {}
    aliases = opts.get('aliases')
    if aliases:
        try:
            f = open(aliases,"r")
        except OSError, e:
            print "Error: " + e
            return

        amap = get_aliases(f)
        f.close()

    revs = [int(r) for r in cmdutil.revrange(repo, opts['rev'])]
    revs.sort()
    stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))

    # make a list of tuples (name, lines) and sort it in descending order
    ordered = stats.items()
    if not ordered:
        return
    ordered.sort(lambda x, y: cmp(y[1], x[1]))
    max_churn = ordered[0][1]

    tty_width = get_tty_width()
    ui.note(_("assuming %i character terminal\n") % tty_width)
    tty_width -= 1

    max_user_width = max([len(user) for user, churn in ordered])
示例#14
0
 def getpatches(revs):
     for r in cmdutil.revrange(repo, revs):
         output = cStringIO.StringIO()
         cmdutil.export(repo, [r], fp=output, opts=patch.diffopts(ui, opts))
         yield output.getvalue().split('\n')
示例#15
0
def task(ui,
         repo,
         task=None,
         rev=None,
         force=False,
         delete=False,
         info=None,
         all=False,
         complete=None,
         resume=None,
         delete_all=False,
         delete_complete=False,
         rename=None,
         trim=None,
         append=None,
         purge_stash=False):
    '''mercurial tasks

    Tasks are collections of contiguous changesets.  A task can be short or long
    lived, but its information is only available locally.  This extension
    overrides the behaviour of push so that it will not push incomplete tasks by
    default. A task can be in one of 3 states:
        * new - this task has no changesets only a parent node
        * active - a task with some changesets but not complete
        * complete - a task with some changesets and marked as complete

    Use 'hg tasks' to list all new and active tasks.  'hg tasks --all' will show
    completed tasks along with those that are new and active.

    This extension adds two new options to push:
            --all-tasks        will push all taks even incomplete ones
            --completed-tasks  will only push completed tasks

    Use 'hg update [TASK]' to update to the tip node of a task and set thate
    task as current.  When a task is set as current, all subsequent commits will
    be assoticated with the task.

    Use 'hg log [TASK]' to view a log of all changesets associated with the
    given task.

    Use 'hg export [TASK]' to export all changesets associated with the given
    task.

    Use 'hg email -t [TASK]' to create a patchbomb containing all changesets
    associated with the given task.

    Use 'hg transplant -t [TASK]' to transplant all associated changsets in a
    task.

    If the auto.track.new config option is True, newly created tasks will
    automatically be set as current.

    If the auto.stash config option is True, your working copy will
    automatically be stashed when you update away from the current task.  When
    returning to a previously stashed task by updating to it, your working copy
    will be restored from the stash.
    '''
    tasks = parse(repo)

    # all these options need a valid task name
    if delete or info or complete or resume or trim or append \
        or (purge_stash and not all):
        if task == None:
            raise util.Abort(_('task name required'))
        if task not in tasks:
            raise util.Abort(_('a task of this name does not exist'))

    # all these options don't want a task
    if delete_all or delete_complete or (purge_stash and all):
        if task:
            raise util.Abort(_('no task name required for this option'))

    # check for force when deleting tasks with stash
    if delete_all or delete_complete or delete:
        if not force:
            if delete_complete:
                deltasks = [t for t in tasks if tasks[t]['state'] == 2]
            elif delete_all:
                deltasks = tasks
            else:
                deltasks = [task]

            stashedtasks = [t for t in deltasks if hasstash(repo, t)]
            if len(stashedtasks):
                if delete:
                    raise util.Abort(
                        _('task has stash info, use -f to lose stash'))
                else:
                    raise util.Abort(
                        _('deleting tasks with stash info, use -f'
                          ' to lose stashes\ntasks with stash info: %s' %
                          ' '.join(stashedtasks)))

    # delete all
    if delete_all:
        for t in tasks:
            removestashfiles(repo, t)
        write(ui, repo, {})
        return

    # delete complete
    if delete_complete:
        deltasks = [t for t in tasks if tasks[t]['state'] == 2]
        update = False
        for t in deltasks:
            removestashfiles(repo, t)
            del tasks[t]
            update = True
        if update:
            write(ui, repo, tasks)
        return

    # rename a task
    if rename:
        if rename not in tasks:
            raise util.Abort(_('a task of this name does not exist'))
        if task in tasks and not force:
            raise util.Abort(_('a task of the same name already exists'))
        if task is None:
            raise util.Abort(_('new task name required'))
        oldstash = stashfiles(repo, rename)
        newstash = stashfiles(repo, task)
        for i in range(len(oldstash)):
            if os.path.exists(repo.join(oldstash[i])):
                shutil.move(repo.join(oldstash[i]), repo.join(newstash[i]))

        tasks[task] = tasks[rename]
        del tasks[rename]
        if current(repo) == rename:
            setcurrent(ui, repo, task)
        write(ui, repo, tasks)
        return

    # delete a task
    if delete:
        if task == current(repo):
            setcurrent(ui, repo, None)
        removestashfiles(repo, task)
        del tasks[task]
        write(ui, repo, tasks)
        return

    # complete a task
    if complete:
        if tasks[task]['state'] == 2:
            raise util.Abort(_('task is already completed'))
        if tasks[task]['state'] == 0:
            raise util.Abort(_('no changesets in task'))
        if not force and hasstash(repo, task):
            raise util.Abort(_('task has stash, use -f to lose stash'))
        # when checking for working copy changes here, we are bit more lax and
        # not worried about unknown files...
        if not force and task == current(repo) and trackingcurrent(repo) \
            and bool(filter(None, repo.status())):
            raise util.Abort(
                _('uncommited changes in current task, use -f to '
                  'complete anyway'))

        tasks[task]['state'] = 2
        if task == current(repo):
            setcurrent(ui, repo, None)
        write(ui, repo, tasks)
        removestashfiles(repo, task)
        return

    # resume a task  (should we set to current and update to it???)
    if resume:
        if not tasks[task]['state'] == 2:
            raise util.Abort(_('task is not completed'))
        tasks[task]['state'] = 1
        write(ui, repo, tasks)
        return

    # show info on a given task
    if info:
        tinfo = tasks[task]
        ui.write('task:            %s\n' % task)
        ui.write('parent:          %s\n' %
                 prettynode(ui, repo, tinfo['parent']))

        if task == current(repo) and trackingcurrent(repo):
            stashstr = bool(filter(None, repo.status())) and ' + changes' or ''
        else:
            stashstr = hasstash(repo, task) and ' + stash' or ''

        if tinfo['state'] == 0: state = 'new'
        if tinfo['state'] == 1: state = 'active'
        if tinfo['state'] == 2: state = 'completed'
        ui.write('state:           %s%s\n' % (state, stashstr))
        if tinfo['state'] != 0:
            nodes = tasknodes(repo, tinfo)
            nodes.reverse()
            changesets = 'changesets (%d):' % len(nodes)
            ui.write('%-16s %s\n' %
                     (changesets, prettynode(ui, repo, nodes[0])))
            for n in nodes[1:]:
                ui.write('                 %s\n' % prettynode(ui, repo, n))
        return

    # trim a task
    if trim:
        if not rev:
            raise util.Abort(_('revision required'))
        else:
            trimnode = repo.lookup(rev)
            tnodes = tasknodes(repo, tasks[task])
            if trimnode not in tnodes:
                raise util.Abort(_('revision not in task'))
            if trimnode == tasks[task]['start']:
                tasks[task]['start'] = None
                tasks[task]['end'] = None
                tasks[task]['state'] = 0
            else:
                newend = tnodes[tnodes.index(trimnode) - 1]
                if newend == tasks[task]['start']:
                    tasks[task]['end'] = None
                else:
                    tasks[task]['end'] = newend
                tasks[task]['state'] = 1
            write(ui, repo, tasks)
        return

    # append to task
    if append:
        if not rev:
            raise util.Abort(_('revision required'))
        anode = repo.lookup(rev)
        nodes = repo.changelog.nodesbetween([tasknode(repo, tasks[task])],
                                            [anode])[0]
        if len(nodes) < 2:
            raise util.Abort(_('no new revs to add'))
        if len([n for n in nodes if repo.changelog.parents(n)[1] != nullid]):
            raise util.Abort(
                _('tasks cannot include merged nodes (nodes with two parents)')
            )

        # now actually append
        if tasks[task]['state'] == 0:  # new
            tasks[task]['start'] = nodes[1]
            if len(nodes) > 2:
                tasks[task]['end'] = nodes[-1]
            tasks[task]['state'] = 1
        else:
            tasks[task]['end'] = nodes[-1]
        write(ui, repo, tasks)
        return

    # purge stash
    if purge_stash:
        purgetasks = all and tasks or [task]
        for t in purgetasks:
            removestashfiles(repo, t)
        return

    # create a task
    if task != None:
        if '\n' in task or ' ' in task or ',' in task:
            raise util.Abort(
                _('task name cannot contain newlines, spaces, or commas'))
        task = task.strip()
        if task in tasks and not force:
            raise util.Abort(_('a task of the same name already exists'))
        if ((task in repo.branchtags() or task == repo.dirstate.branch())
                and not force):
            raise util.Abort(
                _('a task cannot have the name of an existing branch'))
        if (task in repo.tags() and not force):
            raise util.Abort(_('a tag of the same name already exists'))
        if rev:
            nodes = [repo.lookup(r) for r in cmdutil.revrange(repo, [rev])]
            parentnode = nodes[0]
            endnode = None
            startnode = None
            state = 0
            if len(nodes) > 1:
                if len([
                        n for n in nodes[1:]
                        if repo.changelog.parents(n)[1] != nullid
                ]):
                    raise util.Abort(
                        _('tasks cannot include merged nodes '
                          '(nodes with two parents)'))
                startnode = nodes[1]
                state = 1
            if len(nodes) > 2:
                endnode = nodes[-1]
            tasks[task] = {
                'parent': parentnode,
                'start': startnode,
                'end': endnode,
                'state': state
            }
        else:
            tasks[task] = {
                'parent': repo.changectx('.').node(),
                'start': None,
                'end': None,
                'state': 0
            }
        write(ui, repo, tasks)

        # handle the auto tracking of newly created tasks on current node
        if ui.configbool('tasks', 'auto.track.new'):
            try:
                opts = {'rev': None, 'clean': False, 'date': None}
                tasksupdate(commands.update, ui, repo, *[task], **opts)
            except Exception, exception:
                raise type(exception)(
                    str(exception) +
                    "\nwarning: new task '%s' created but not set to current" %
                    task)
        return
示例#16
0
        return aliases

    amap = {}
    aliases = opts.get('aliases')
    if aliases:
        try:
            f = open(aliases, "r")
        except OSError, e:
            print "Error: " + e
            return

        amap = get_aliases(f)
        f.close()

    revs = [int(r) for r in cmdutil.revrange(repo, opts['rev'])]
    revs.sort()
    stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))

    # make a list of tuples (name, lines) and sort it in descending order
    ordered = stats.items()
    if not ordered:
        return
    ordered.sort(lambda x, y: cmp(y[1], x[1]))
    max_churn = ordered[0][1]

    tty_width = get_tty_width()
    ui.note(_("assuming %i character terminal\n") % tty_width)
    tty_width -= 1

    max_user_width = max([len(user) for user, churn in ordered])
示例#17
0
def get_revs(repo, rev_opt):
    if rev_opt:
        revs = revrange(repo, rev_opt)
        return (max(revs), min(revs))
    else:
        return (len(repo) - 1, 0)
示例#18
0
def transplant(ui, repo, *revs, **opts):
    '''transplant changesets from another branch

    Selected changesets will be applied on top of the current working
    directory with the log of the original changeset. If --log is
    specified, log messages will have a comment appended of the form:

    (transplanted from CHANGESETHASH)

    You can rewrite the changelog message with the --filter option.
    Its argument will be invoked with the current changelog message
    as $1 and the patch as $2.

    If --source is specified, selects changesets from the named
    repository. If --branch is specified, selects changesets from the
    branch holding the named revision, up to that revision. If --all
    is specified, all changesets on the branch will be transplanted,
    otherwise you will be prompted to select the changesets you want.

    hg transplant --branch REVISION --all will rebase the selected branch
    (up to the named revision) onto your current working directory.

    You can optionally mark selected transplanted changesets as
    merge changesets. You will not be prompted to transplant any
    ancestors of a merged transplant, and you can merge descendants
    of them normally instead of transplanting them.

    If no merges or revisions are provided, hg transplant will start
    an interactive changeset browser.

    If a changeset application fails, you can fix the merge by hand and
    then resume where you left off by calling hg transplant --continue.
    '''
    def getoneitem(opts, item, errmsg):
        val = opts.get(item)
        if val:
            if len(val) > 1:
                raise util.Abort(errmsg)
            else:
                return val[0]

    def getremotechanges(repo, url):
        sourcerepo = ui.expandpath(url)
        source = hg.repository(ui, sourcerepo)
        incoming = repo.findincoming(source, force=True)
        if not incoming:
            return (source, None, None)

        bundle = None
        if not source.local():
            cg = source.changegroup(incoming, 'incoming')
            bundle = changegroup.writebundle(cg, None, 'HG10UN')
            source = bundlerepo.bundlerepository(ui, repo.root, bundle)

        return (source, incoming, bundle)

    def incwalk(repo, incoming, branches, match=util.always):
        if not branches:
            branches = None
        for node in repo.changelog.nodesbetween(incoming, branches)[0]:
            if match(node):
                yield node

    def transplantwalk(repo, root, branches, match=util.always):
        if not branches:
            branches = repo.heads()
        ancestors = []
        for branch in branches:
            ancestors.append(repo.changelog.ancestor(root, branch))
        for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
            if match(node):
                yield node

    def checkopts(opts, revs):
        if opts.get('continue'):
            if filter(lambda opt: opts.get(opt), ('branch', 'all', 'merge')):
                raise util.Abort(
                    _('--continue is incompatible with branch, all or merge'))
            return
        if not (opts.get('source') or revs or opts.get('merge')
                or opts.get('branch')):
            raise util.Abort(
                _('no source URL, branch tag or revision list provided'))
        if opts.get('all'):
            if not opts.get('branch'):
                raise util.Abort(_('--all requires a branch revision'))
            if revs:
                raise util.Abort(
                    _('--all is incompatible with a revision list'))

    checkopts(opts, revs)

    if not opts.get('log'):
        opts['log'] = ui.config('transplant', 'log')
    if not opts.get('filter'):
        opts['filter'] = ui.config('transplant', 'filter')

    tp = transplanter(ui, repo)

    p1, p2 = repo.dirstate.parents()
    if p1 == revlog.nullid:
        raise util.Abort(_('no revision checked out'))
    if not opts.get('continue'):
        if p2 != revlog.nullid:
            raise util.Abort(_('outstanding uncommitted merges'))
        m, a, r, d = repo.status()[:4]
        if m or a or r or d:
            raise util.Abort(_('outstanding local changes'))

    bundle = None
    source = opts.get('source')
    if source:
        (source, incoming, bundle) = getremotechanges(repo, source)
    else:
        source = repo

    try:
        if opts.get('continue'):
            tp.resume(repo, source, opts)
            return

        tf = tp.transplantfilter(repo, source, p1)
        if opts.get('prune'):
            prune = [
                source.lookup(r)
                for r in cmdutil.revrange(source, opts.get('prune'))
            ]
            matchfn = lambda x: tf(x) and x not in prune
        else:
            matchfn = tf
        branches = map(source.lookup, opts.get('branch', ()))
        merges = map(source.lookup, opts.get('merge', ()))
        revmap = {}
        if revs:
            for r in cmdutil.revrange(source, revs):
                revmap[int(r)] = source.lookup(r)
        elif opts.get('all') or not merges:
            if source != repo:
                alltransplants = incwalk(source,
                                         incoming,
                                         branches,
                                         match=matchfn)
            else:
                alltransplants = transplantwalk(source,
                                                p1,
                                                branches,
                                                match=matchfn)
            if opts.get('all'):
                revs = alltransplants
            else:
                revs, newmerges = browserevs(ui, source, alltransplants, opts)
                merges.extend(newmerges)
            for r in revs:
                revmap[source.changelog.rev(r)] = r
        for r in merges:
            revmap[source.changelog.rev(r)] = r

        revs = revmap.keys()
        revs.sort()
        pulls = []

        tp.apply(repo, source, revmap, merges, opts)
    finally:
        if bundle:
            source.close()
            os.unlink(bundle)
示例#19
0
def collapse(ui, repo, **opts):
    """collapse multiple revisions into one

    Collapse combines multiple consecutive changesets into a single changeset,
    preserving any descendants of the final changeset. The commit messages for
    the collapsed changesets are concatenated and may be edited before the
    collapse is completed.
    """

    rng = cmdutil.revrange(repo, opts['rev'])
    first = rng[0]
    last = rng[len(rng) - 1]
    revs = inbetween(repo, first, last)

    if not revs:
        raise util.Abort(_('revision %s is not an ancestor of revision %s\n') %
                            (first, last))
    elif len(revs) == 1:
        raise util.Abort(_('only one revision specified'))

    ui.debug(_('Collapsing revisions %s\n') % revs)

    for r in revs:
        if repo[r].user() != ui.username() and not opts['force']:
            raise util.Abort(_('revision %s does not belong to %s\n') %
                (r, ui.username()))
        if r != last:
            children = repo[r].children()
            if len(children) > 1:
                for c in children:
                    if not c.rev() in revs:
                        raise util.Abort(_('revision %s has child %s not '
                            'being collapsed, please rebase\n') % (r, c.rev()))
        if r != first:
            parents = repo[r].parents()
            if len(parents) > 1:
                for p in parents:
                    if not p.rev() in revs:
                        raise util.Abort(_('revision %s has parent %s not '
                            'being collapsed.') % (r, p.rev()))

    if len(repo[first].parents()) > 1:
        raise util.Abort(_('start revision %s has multiple parents, '
            'won\'t collapse.') % first)

    cmdutil.bail_if_changed(repo)

    parent = repo[first].parents()[0]
    tomove = list(repo.changelog.descendants(last))
    movemap = dict.fromkeys(tomove, nullrev)
    ui.debug(_('will move revisions: %s\n') % tomove)
    
    origparent = repo['.'].rev()
    collapsed = None
    
    try:
        collapsed = makecollapsed(ui, repo, parent, revs)
        movemap[max(revs)] = collapsed
        movedescendants(ui, repo, collapsed, tomove, movemap)
    except:
        merge.update(repo, repo[origparent].rev(), False, True, False)
        if collapsed:
            repair.strip(ui, repo, collapsed.node(), "strip")
        raise

    if not opts['keep']:
        ui.debug(_('stripping revision %d\n') % first)
        repair.strip(ui, repo, repo[first].node(), "strip")

    ui.status(_('collapse completed\n'))
示例#20
0
def transplant(ui, repo, *revs, **opts):
    '''transplant changesets from another branch

    Selected changesets will be applied on top of the current working
    directory with the log of the original changeset. If --log is
    specified, log messages will have a comment appended of the form:

    (transplanted from CHANGESETHASH)

    You can rewrite the changelog message with the --filter option.
    Its argument will be invoked with the current changelog message
    as $1 and the patch as $2.

    If --source is specified, selects changesets from the named
    repository. If --branch is specified, selects changesets from the
    branch holding the named revision, up to that revision. If --all
    is specified, all changesets on the branch will be transplanted,
    otherwise you will be prompted to select the changesets you want.

    hg transplant --branch REVISION --all will rebase the selected branch
    (up to the named revision) onto your current working directory.

    You can optionally mark selected transplanted changesets as
    merge changesets. You will not be prompted to transplant any
    ancestors of a merged transplant, and you can merge descendants
    of them normally instead of transplanting them.

    If no merges or revisions are provided, hg transplant will start
    an interactive changeset browser.

    If a changeset application fails, you can fix the merge by hand and
    then resume where you left off by calling hg transplant --continue.
    '''
    def getoneitem(opts, item, errmsg):
        val = opts.get(item)
        if val:
            if len(val) > 1:
                raise util.Abort(errmsg)
            else:
                return val[0]

    def getremotechanges(repo, url):
        sourcerepo = ui.expandpath(url)
        source = hg.repository(ui, sourcerepo)
        incoming = repo.findincoming(source, force=True)
        if not incoming:
            return (source, None, None)

        bundle = None
        if not source.local():
            cg = source.changegroup(incoming, 'incoming')
            bundle = changegroup.writebundle(cg, None, 'HG10UN')
            source = bundlerepo.bundlerepository(ui, repo.root, bundle)

        return (source, incoming, bundle)

    def incwalk(repo, incoming, branches, match=util.always):
        if not branches:
            branches=None
        for node in repo.changelog.nodesbetween(incoming, branches)[0]:
            if match(node):
                yield node

    def transplantwalk(repo, root, branches, match=util.always):
        if not branches:
            branches = repo.heads()
        ancestors = []
        for branch in branches:
            ancestors.append(repo.changelog.ancestor(root, branch))
        for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
            if match(node):
                yield node

    def checkopts(opts, revs):
        if opts.get('continue'):
            if filter(lambda opt: opts.get(opt), ('branch', 'all', 'merge')):
                raise util.Abort(_('--continue is incompatible with branch, all or merge'))
            return
        if not (opts.get('source') or revs or
                opts.get('merge') or opts.get('branch')):
            raise util.Abort(_('no source URL, branch tag or revision list provided'))
        if opts.get('all'):
            if not opts.get('branch'):
                raise util.Abort(_('--all requires a branch revision'))
            if revs:
                raise util.Abort(_('--all is incompatible with a revision list'))

    checkopts(opts, revs)

    if not opts.get('log'):
        opts['log'] = ui.config('transplant', 'log')
    if not opts.get('filter'):
        opts['filter'] = ui.config('transplant', 'filter')

    tp = transplanter(ui, repo)

    p1, p2 = repo.dirstate.parents()
    if p1 == revlog.nullid:
        raise util.Abort(_('no revision checked out'))
    if not opts.get('continue'):
        if p2 != revlog.nullid:
            raise util.Abort(_('outstanding uncommitted merges'))
        m, a, r, d = repo.status()[:4]
        if m or a or r or d:
            raise util.Abort(_('outstanding local changes'))

    bundle = None
    source = opts.get('source')
    if source:
        (source, incoming, bundle) = getremotechanges(repo, source)
    else:
        source = repo

    try:
        if opts.get('continue'):
            tp.resume(repo, source, opts)
            return

        tf=tp.transplantfilter(repo, source, p1)
        if opts.get('prune'):
            prune = [source.lookup(r)
                     for r in cmdutil.revrange(source, opts.get('prune'))]
            matchfn = lambda x: tf(x) and x not in prune
        else:
            matchfn = tf
        branches = map(source.lookup, opts.get('branch', ()))
        merges = map(source.lookup, opts.get('merge', ()))
        revmap = {}
        if revs:
            for r in cmdutil.revrange(source, revs):
                revmap[int(r)] = source.lookup(r)
        elif opts.get('all') or not merges:
            if source != repo:
                alltransplants = incwalk(source, incoming, branches, match=matchfn)
            else:
                alltransplants = transplantwalk(source, p1, branches, match=matchfn)
            if opts.get('all'):
                revs = alltransplants
            else:
                revs, newmerges = browserevs(ui, source, alltransplants, opts)
                merges.extend(newmerges)
            for r in revs:
                revmap[source.changelog.rev(r)] = r
        for r in merges:
            revmap[source.changelog.rev(r)] = r

        revs = revmap.keys()
        revs.sort()
        pulls = []

        tp.apply(repo, source, revmap, merges, opts)
    finally:
        if bundle:
            source.close()
            os.unlink(bundle)
示例#21
0
def get_revs(repo, rev_opt):
    if rev_opt:
        revs = revrange(repo, rev_opt)
        return (max(revs), min(revs))
    else:
        return (repo.changelog.count() - 1, 0)