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))))
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)
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)
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')
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))))
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'))
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)
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
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)
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
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)
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])
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')
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
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])
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)
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)
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'))
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)
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)