def abort(repo, originalwd, target, state): 'Restore the repository to its original state' dstates = [s for s in state.values() if s != nullrev] if [d for d in dstates if not repo[d].mutable()]: repo.ui.warn( _("warning: immutable rebased changeset detected, " "can't abort\n")) return -1 descendants = set() if dstates: descendants = set(repo.changelog.descendants(*dstates)) if descendants - set(dstates): repo.ui.warn( _("warning: new changesets detected on target branch, " "can't abort\n")) return -1 else: # Strip from the first rebased revision merge.update(repo, repo[originalwd].rev(), False, True, False) rebased = filter(lambda x: x > -1 and x != target, state.values()) if rebased: strippoint = min(rebased) # no backup of rebased cset versions needed repair.strip(repo.ui, repo, repo[strippoint].node()) clearstatus(repo) repo.ui.warn(_('rebase aborted\n')) return 0
def abort(repo, originalwd, target, state): 'Restore the repository to its original state' dstates = [s for s in state.values() if s > nullrev] immutable = [d for d in dstates if not repo[d].mutable()] cleanup = True if immutable: repo.ui.warn(_("warning: can't clean up immutable changesets %s\n") % ', '.join(str(repo[r]) for r in immutable), hint=_('see hg help phases for details')) cleanup = False descendants = set() if dstates: descendants = set(repo.changelog.descendants(dstates)) if descendants - set(dstates): repo.ui.warn(_("warning: new changesets detected on target branch, " "can't strip\n")) cleanup = False if cleanup: # Update away from the rebase if necessary if inrebase(repo, originalwd, state): merge.update(repo, repo[originalwd].rev(), False, True, False) # Strip from the first rebased revision rebased = filter(lambda x: x > -1 and x != target, state.values()) if rebased: strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)] # no backup of rebased cset versions needed repair.strip(repo.ui, repo, strippoints) clearstatus(repo) repo.ui.warn(_('rebase aborted\n')) return 0
def fix_hgtags(ui, repo, head_hgtags, tagsmap): for tf in iter(tagsmap): ui.debug('fix_hgtags: tagsmap %s -> %s\n' % (tf, tagsmap[tf])) for old in iter(head_hgtags): new = map_recursive(tagsmap, old) ui.debug('fix_hgtags: head %s -> %s\n' % (old, new)) merge.update(repo, repo[new].node(), False, False, False) tfile = open('.hgtags', 'wb') lines = StringIO.StringIO(head_hgtags[old]) for line in lines: if not line: continue (nodehex, name) = line.split(" ", 1) name = name.strip() nhm = map_recursive(tagsmap, nodehex) ui.debug('fix_hgtags: hgtags write: %s %s\n' % (nhm, name)) tfile.write('%s %s\n' % (nhm, name)) lines.close() tfile.close() wctx = repo[None] if '.hgtags' not in wctx: wctx.add(['.hgtags']) nrev = repo.commit(text="collapse tag fix") if nrev: nctx = repo[nrev] ui.debug(_('fix_hgtags: nctx rev %d node %r files %r\n') % (nctx.rev(), hex(nctx.node()), nctx.files())) ui.debug(_('fix_hgtags: nctx parents %r\n') % ([hex(p.node()) for p in nctx.parents()])) else: ui.debug(_('fix_hgtags: nctx: None\n'))
def unshelveabort(ui, repo, state, opts): """subcommand that abort an in-progress unshelve""" with repo.lock(): try: checkparents(repo, state) merge.update(repo, state.pendingctx, branchmerge=False, force=True) if (state.activebookmark and state.activebookmark in repo._bookmarks): bookmarks.activate(repo, state.activebookmark) if repo.vfs.exists('unshelverebasestate'): repo.vfs.rename('unshelverebasestate', 'rebasestate') rebase.clearstatus(repo) mergefiles(ui, repo, state.wctx, state.pendingctx) if not phases.supportinternal(repo): repair.strip(ui, repo, state.nodestoremove, backup=False, topic='shelve') finally: shelvedstate.clear(repo) ui.warn(_("unshelve of '%s' aborted\n") % state.name)
def makecollapsed(ui, repo, parent, revs): 'Creates the collapsed revision on top of parent' last = max(revs) ui.debug(_('updating to revision %d\n') % parent) merge.update(repo, parent.node(), False, False, False) ui.debug(_('reverting to revision %d\n') % last) commands.revert(ui, repo, rev=last, all=True, date=None) msg = '' first = True for r in revs: if not first: msg += '----------------\n' first = False msg += repo[r].description() + "\n" msg += "\nHG: Enter commit message. Lines beginning with 'HG:' are removed.\n" msg += "HG: Remove all lines to abort the collapse operation.\n" msg = ui.edit(msg, ui.username()) if not msg: raise util.Abort(_('empty commit message, collapse won\'t proceed')) newrev = repo.commit( text=msg, user=repo[last].user(), date=repo[last].date()) return repo[newrev]
def rebasenode(repo, rev, p1, base, state, collapse, target): 'Rebase a single revision rev on top of p1 using base as merge ancestor' # Merge phase # Update to target and merge it with local if repo['.'].rev() != p1: repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1])) merge.update(repo, p1, False, True, False) else: repo.ui.debug(" already in target\n") repo.dirstate.write() repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev])) if base is not None: repo.ui.debug(" detach base %d:%s\n" % (base, repo[base])) # When collapsing in-place, the parent is the common ancestor, we # have to allow merging with it. stats = merge.update(repo, rev, True, True, False, base, collapse, labels=['dest', 'source']) if collapse: copies.duplicatecopies(repo, rev, target) else: # If we're not using --collapse, we need to # duplicate copies between the revision we're # rebasing and its first parent, but *not* # duplicate any copies that have already been # performed in the destination. p1rev = repo[rev].p1().rev() copies.duplicatecopies(repo, rev, p1rev, skiprev=target) return stats
def rebasenode(repo, rev, p1, base, state, collapse, target): 'Rebase a single revision rev on top of p1 using base as merge ancestor' # Merge phase # Update to target and merge it with local if repo['.'].rev() != p1: repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1])) merge.update(repo, p1, False, True, False) else: repo.ui.debug(" already in target\n") repo.dirstate.write(repo.currenttransaction()) repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev])) if base is not None: repo.ui.debug(" detach base %d:%s\n" % (base, repo[base])) # When collapsing in-place, the parent is the common ancestor, we # have to allow merging with it. stats = merge.update(repo, rev, True, True, False, base, collapse, labels=['dest', 'source']) if collapse: copies.duplicatecopies(repo, rev, target) else: # If we're not using --collapse, we need to # duplicate copies between the revision we're # rebasing and its first parent, but *not* # duplicate any copies that have already been # performed in the destination. p1rev = repo[rev].p1().rev() copies.duplicatecopies(repo, rev, p1rev, skiprev=target) return stats
def abort(repo, originalwd, target, state): 'Restore the repository to its original state' dstates = [s for s in state.values() if s != nullrev] immutable = [d for d in dstates if not repo[d].mutable()] if immutable: raise util.Abort(_("can't abort rebase due to immutable changesets %s") % ', '.join(str(repo[r]) for r in immutable), hint=_('see hg help phases for details')) descendants = set() if dstates: descendants = set(repo.changelog.descendants(*dstates)) if descendants - set(dstates): repo.ui.warn(_("warning: new changesets detected on target branch, " "can't abort\n")) return -1 else: # Strip from the first rebased revision merge.update(repo, repo[originalwd].rev(), False, True, False) rebased = filter(lambda x: x > -1 and x != target, state.values()) if rebased: strippoint = min(rebased) # no backup of rebased cset versions needed repair.strip(repo.ui, repo, repo[strippoint].node()) clearstatus(repo) repo.ui.warn(_('rebase aborted\n')) return 0
def checkout_branch(self, branch=None): if branch is None: branch = self.repository.DEFAULT_BRANCH_NAME if branch not in self.repository.branches: raise BranchDoesNotExistError hg_merge.update(self.repository._repo, branch, False, False, None)
def rebasenode(repo, rev, p1, state, collapse): 'Rebase a single revision' # Merge phase # Update to target and merge it with local if repo['.'].rev() != repo[p1].rev(): repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1])) merge.update(repo, p1, False, True, False) else: repo.ui.debug(" already in target\n") repo.dirstate.write() repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev])) if repo[rev].rev() == repo[min(state)].rev(): # Case (1) initial changeset of a non-detaching rebase. # Let the merge mechanism find the base itself. base = None elif not repo[rev].p2(): # Case (2) detaching the node with a single parent, use this parent base = repo[rev].p1().node() else: # In case of merge, we need to pick the right parent as merge base. # # Imagine we have: # - M: currently rebase revision in this step # - A: one parent of M # - B: second parent of M # - D: destination of this merge step (p1 var) # # If we are rebasing on D, D is the successors of A or B. The right # merge base is the one D succeed to. We pretend it is B for the rest # of this comment # # If we pick B as the base, the merge involves: # - changes from B to M (actual changeset payload) # - changes from B to D (induced by rebase) as D is a rebased # version of B) # Which exactly represent the rebase operation. # # If we pick the A as the base, the merge involves # - changes from A to M (actual changeset payload) # - changes from A to D (with include changes between unrelated A and B # plus changes induced by rebase) # Which does not represent anything sensible and creates a lot of # conflicts. for p in repo[rev].parents(): if state.get(p.rev()) == repo[p1].rev(): base = p.node() break else: # fallback when base not found base = None # Raise because this function is called wrong (see issue 4106) raise AssertionError('no base found to rebase on ' '(rebasenode called wrong)') if base is not None: repo.ui.debug(" detach base %d:%s\n" % (repo[base].rev(), repo[base])) # When collapsing in-place, the parent is the common ancestor, we # have to allow merging with it. return merge.update(repo, rev, True, True, False, base, collapse)
def rebasenode(repo, rev, p1, state, collapse): 'Rebase a single revision' # Merge phase # Update to target and merge it with local if repo['.'].rev() != repo[p1].rev(): repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1])) merge.update(repo, p1, False, True, False) else: repo.ui.debug(" already in target\n") repo.dirstate.write() repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev])) if repo[rev].rev() == repo[min(state)].rev(): # Case (1) initial changeset of a non-detaching rebase. # Let the merge mechanism find the base itself. base = None elif not repo[rev].p2(): # Case (2) detaching the node with a single parent, use this parent base = repo[rev].p1().node() else: # In case of merge, we need to pick the right parent as merge base. # # Imagine we have: # - M: currently rebase revision in this step # - A: one parent of M # - B: second parent of M # - D: destination of this merge step (p1 var) # # If we are rebasing on D, D is the successors of A or B. The right # merge base is the one D succeed to. We pretend it is B for the rest # of this comment # # If we pick B as the base, the merge involves: # - changes from B to M (actual changeset payload) # - changes from B to D (induced by rebase) as D is a rebased # version of B) # Which exactly represent the rebase operation. # # If we pick the A as the base, the merge involves # - changes from A to M (actual changeset payload) # - changes from A to D (with include changes between unrelated A and B # plus changes induced by rebase) # Which does not represent anything sensible and creates a lot of # conflicts. for p in repo[rev].parents(): if state.get(p.rev()) == repo[p1].rev(): base = p.node() break else: # fallback when base not found base = None # Raise because this function is called wrong (see issue 4106) raise AssertionError('no base found to rebase on ' '(rebasenode called wrong)') if base is not None: repo.ui.debug(" detach base %d:%s\n" % (repo[base].rev(), repo[base])) # When collapsing in-place, the parent is the common ancestor, we # have to allow merging with it. return merge.update(repo, rev, True, True, False, base, collapse, labels=['dest', 'source'])
def makecollapsed(ui, repo, parent, revs, branch, tagsmap, parent_hgtags, movelog, opts): 'Creates the collapsed revision on top of parent' last = max(revs) ui.debug(_('updating to revision %d\n') % parent) merge.update(repo, parent.node(), False, False, False) ui.debug(_('reverting to revision %d\n') % last) recreaterev(ui, repo, last) repo.dirstate.setbranch(branch) msg = '' nodelist = [] if opts['message'] != "" : msg = opts['message'] else: first = True for r in revs: nodelist.append(hex(repo[r].node())) if repo[r].files() != ['.hgtags']: if not first: if opts['changelog']: msg += '\n' else: msg += '----------------\n' first = False if opts['changelog']: msg += "* " + ' '.join(repo[r].files()) + ":\n" msg += repo[r].description() + "\n" msg += "\nHG: Enter commit message. Lines beginning with 'HG:' are removed.\n" msg += "HG: Remove all lines to abort the collapse operation.\n" if ui.config('ui', 'interactive') != 'off': msg = ui.edit(msg, ui.username()) pattern = re.compile("^HG:.*\n", re.MULTILINE); msg = re.sub(pattern, "", msg).strip(); if not msg: raise util.Abort(_('empty commit message, collapse won\'t proceed')) write_hgtags(parent_hgtags) newrev = repo.commit( text=msg, user=repo[last].user(), date=repo[last].date()) ctx = repo[newrev] newhex = hex(ctx.node()) for n in nodelist: ui.debug(_('makecollapsed %s -> %s\n' % (n, newhex))) tagsmap[n] = newhex if movelog: movelog.write('coll %s -> %s\n' % (n, newhex)) return ctx
def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse, extrafn): 'Rebase a single revision' repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev])) p1, p2 = defineparents(repo, rev, target, state, targetancestors) repo.ui.debug( _(" future parents are %d and %d\n") % (repo[p1].rev(), repo[p2].rev())) # Merge phase if len(repo.parents()) != 2: # Update to target and merge it with local if repo['.'].rev() != repo[p1].rev(): repo.ui.debug(_(" update to %d:%s\n") % (repo[p1].rev(), repo[p1])) merge.update(repo, p1, False, True, False) else: repo.ui.debug(_(" already in target\n")) repo.dirstate.write() repo.ui.debug( _(" merge against %d:%s\n") % (repo[rev].rev(), repo[rev])) first = repo[rev].rev() == repo[min(state)].rev() stats = rebasemerge(repo, rev, first) if stats[3] > 0: raise util.Abort( _('fix unresolved conflicts with hg resolve then ' 'run hg rebase --continue')) else: # we have an interrupted rebase repo.ui.debug(_('resuming interrupted rebase\n')) # Keep track of renamed files in the revision that is going to be rebased # Here we simulate the copies and renames in the source changeset cop, diver = copies.copies(repo, repo[rev], repo[target], repo[p2], True) m1 = repo[rev].manifest() m2 = repo[target].manifest() for k, v in cop.iteritems(): if k in m1: if v in m1 or v in m2: repo.dirstate.copy(v, k) if v in m2 and v not in m1: repo.dirstate.remove(v) newrev = concludenode(repo, rev, p1, p2, state, collapse, extrafn=extrafn) # Update the state if newrev is not None: state[rev] = repo[newrev].rev() else: if not collapse: repo.ui.note(_('no changes, revision %d skipped\n') % rev) repo.ui.debug(_('next revision set to %s\n') % p1) skipped.add(rev) state[rev] = p1
def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse, extrafn): 'Rebase a single revision' repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev])) p1, p2 = defineparents(repo, rev, target, state, targetancestors) repo.ui.debug(_(" future parents are %d and %d\n") % (repo[p1].rev(), repo[p2].rev())) # Merge phase if len(repo.parents()) != 2: # Update to target and merge it with local if repo['.'].rev() != repo[p1].rev(): repo.ui.debug(_(" update to %d:%s\n") % (repo[p1].rev(), repo[p1])) merge.update(repo, p1, False, True, False) else: repo.ui.debug(_(" already in target\n")) repo.dirstate.write() repo.ui.debug(_(" merge against %d:%s\n") % (repo[rev].rev(), repo[rev])) first = repo[rev].rev() == repo[min(state)].rev() stats = rebasemerge(repo, rev, first) if stats[3] > 0: raise util.Abort(_('fix unresolved conflicts with hg resolve then ' 'run hg rebase --continue')) else: # we have an interrupted rebase repo.ui.debug(_('resuming interrupted rebase\n')) # Keep track of renamed files in the revision that is going to be rebased # Here we simulate the copies and renames in the source changeset cop, diver = copies.copies(repo, repo[rev], repo[target], repo[p2], True) m1 = repo[rev].manifest() m2 = repo[target].manifest() for k, v in cop.iteritems(): if k in m1: if v in m1 or v in m2: repo.dirstate.copy(v, k) if v in m2 and v not in m1: repo.dirstate.remove(v) newrev = concludenode(repo, rev, p1, p2, state, collapse, extrafn=extrafn) # Update the state if newrev is not None: state[rev] = repo[newrev].rev() else: if not collapse: repo.ui.note(_('no changes, revision %d skipped\n') % rev) repo.ui.debug(_('next revision set to %s\n') % p1) skipped.add(rev) state[rev] = p1
def override_rollback(orig, ui, repo, **opts): result = orig(ui, repo, **opts) merge.update(repo, node=None, branchmerge=False, force=True, partial=lfutil.isstandin) lfdirstate = lfutil.openlfdirstate(ui, repo) lfiles = lfutil.listlfiles(repo) oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev()) for file in lfiles: if file in oldlfiles: lfdirstate.normallookup(file) else: lfdirstate.add(file) lfdirstate.write() return result
def abort(repo, originalwd, target, state): 'Restore the repository to its original state' if set(repo.changelog.descendants(target)) - set(state.values()): repo.ui.warn(_("warning: new changesets detected on target branch, " "not stripping\n")) else: # Strip from the first rebased revision merge.update(repo, repo[originalwd].rev(), False, True, False) rebased = filter(lambda x: x > -1, state.values()) if rebased: strippoint = min(rebased) repair.strip(repo.ui, repo, repo[strippoint].node(), "strip") clearstatus(repo) repo.ui.status(_('rebase aborted\n'))
def rebasenode(repo, rev, p1, p2, state): 'Rebase a single revision' # Merge phase # Update to target and merge it with local if repo['.'].rev() != repo[p1].rev(): repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1])) merge.update(repo, p1, False, True, False) else: repo.ui.debug(" already in target\n") repo.dirstate.write() repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev])) first = repo[rev].rev() == repo[min(state)].rev() stats = rebasemerge(repo, rev, first) return stats
def abort(repo, originalwd, target, state): 'Restore the repository to its original state' if set(repo.changelog.descendants(target)) - set(state.values()): repo.ui.warn( _("warning: new changesets detected on target branch, " "not stripping\n")) else: # Strip from the first rebased revision merge.update(repo, repo[originalwd].rev(), False, True, False) rebased = filter(lambda x: x > -1, state.values()) if rebased: strippoint = min(rebased) repair.strip(repo.ui, repo, repo[strippoint].node(), "strip") clearstatus(repo) repo.ui.status(_('rebase aborted\n'))
def abort(repo, originalwd, target, state, activebookmark=None): '''Restore the repository to its original state. Additional args: activebookmark: the name of the bookmark that should be active after the restore''' try: # If the first commits in the rebased set get skipped during the rebase, # their values within the state mapping will be the target rev id. The # dstates list must must not contain the target rev (issue4896) dstates = [s for s in state.values() if s >= 0 and s != target] immutable = [d for d in dstates if not repo[d].mutable()] cleanup = True if immutable: repo.ui.warn(_("warning: can't clean up public changesets %s\n") % ', '.join(str(repo[r]) for r in immutable), hint=_('see "hg help phases" for details')) cleanup = False descendants = set() if dstates: descendants = set(repo.changelog.descendants(dstates)) if descendants - set(dstates): repo.ui.warn(_("warning: new changesets detected on target branch, " "can't strip\n")) cleanup = False if cleanup: # Update away from the rebase if necessary if needupdate(repo, state): merge.update(repo, originalwd, False, True, False) # Strip from the first rebased revision rebased = filter(lambda x: x >= 0 and x != target, state.values()) if rebased: strippoints = [ c.node() for c in repo.set('roots(%ld)', rebased)] # no backup of rebased cset versions needed repair.strip(repo.ui, repo, strippoints) if activebookmark and activebookmark in repo._bookmarks: bookmarks.activate(repo, activebookmark) finally: clearstatus(repo) repo.ui.warn(_('rebase aborted\n')) return 0
def rebasenode(repo, rev, p1, state, collapse): 'Rebase a single revision' # Merge phase # Update to target and merge it with local if repo['.'].rev() != repo[p1].rev(): repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1])) merge.update(repo, p1, False, True, False) else: repo.ui.debug(" already in target\n") repo.dirstate.write() repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev])) base = None if repo[rev].rev() != repo[min(state)].rev(): base = repo[rev].p1().node() # When collapsing in-place, the parent is the common ancestor, we # have to allow merging with it. return merge.update(repo, rev, True, True, False, base, collapse)
def _moveto(repo, bookmark, ctx, clean=False): """Moves the given bookmark and the working copy to the given revision. By default it does not overwrite the working copy contents unless clean is True. Assumes the wlock is already taken. """ # Move working copy over if clean: merge.update(repo, ctx.node(), False, # not a branchmerge True, # force overwriting files None) # not a partial update else: # Mark any files that are different between the two as normal-lookup # so they show up correctly in hg status afterwards. wctx = repo[None] m1 = wctx.manifest() m2 = ctx.manifest() diff = m1.diff(m2) changedfiles = [] changedfiles.extend(diff.iterkeys()) dirstate = repo.dirstate dirchanges = [f for f in dirstate if dirstate[f] != 'n'] changedfiles.extend(dirchanges) if changedfiles or ctx.node() != repo['.'].node(): with dirstate.parentchange(): dirstate.rebuild(ctx.node(), m2, changedfiles) # Move bookmark over if bookmark: lock = tr = None try: lock = repo.lock() tr = repo.transaction('reset') changes = [(bookmark, ctx.node())] repo._bookmarks.applychanges(repo, tr, changes) tr.close() finally: lockmod.release(lock, tr)
def abort(repo, originalwd, target, state, activebookmark=None): '''Restore the repository to its original state. Additional args: activebookmark: the name of the bookmark that should be active after the restore''' dstates = [s for s in state.values() if s >= 0] immutable = [d for d in dstates if not repo[d].mutable()] cleanup = True if immutable: repo.ui.warn(_("warning: can't clean up public changesets %s\n") % ', '.join(str(repo[r]) for r in immutable), hint=_('see "hg help phases" for details')) cleanup = False descendants = set() if dstates: descendants = set(repo.changelog.descendants(dstates)) if descendants - set(dstates): repo.ui.warn( _("warning: new changesets detected on target branch, " "can't strip\n")) cleanup = False if cleanup: # Update away from the rebase if necessary if needupdate(repo, state): merge.update(repo, originalwd, False, True, False) # Strip from the first rebased revision rebased = filter(lambda x: x >= 0 and x != target, state.values()) if rebased: strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)] # no backup of rebased cset versions needed repair.strip(repo.ui, repo, strippoints) if activebookmark and activebookmark in repo._bookmarks: bookmarks.activate(repo, activebookmark) clearstatus(repo) repo.ui.warn(_('rebase aborted\n')) return 0
def pullrebase(orig, ui, repo, *args, **opts): 'Call rebase after pull if the latter has been invoked with --rebase' if opts.get('rebase'): if opts.get('update'): del opts['update'] ui.debug('--update and --rebase are not compatible, ignoring ' 'the update flag\n') cmdutil.bail_if_changed(repo) revsprepull = len(repo) orig(ui, repo, *args, **opts) revspostpull = len(repo) if revspostpull > revsprepull: rebase(ui, repo, **opts) branch = repo[None].branch() dest = repo[branch].rev() if dest != repo['.'].rev(): # there was nothing to rebase we force an update merge.update(repo, dest, False, False, False) else: orig(ui, repo, *args, **opts)
def abort(repo, originalwd, target, state, activebookmark=None): """Restore the repository to its original state. Additional args: activebookmark: the name of the bookmark that should be active after the restore""" dstates = [s for s in state.values() if s >= 0] immutable = [d for d in dstates if not repo[d].mutable()] cleanup = True if immutable: repo.ui.warn( _("warning: can't clean up public changesets %s\n") % ", ".join(str(repo[r]) for r in immutable), hint=_('see "hg help phases" for details'), ) cleanup = False descendants = set() if dstates: descendants = set(repo.changelog.descendants(dstates)) if descendants - set(dstates): repo.ui.warn(_("warning: new changesets detected on target branch, " "can't strip\n")) cleanup = False if cleanup: # Update away from the rebase if necessary if needupdate(repo, state): merge.update(repo, originalwd, False, True, False) # Strip from the first rebased revision rebased = filter(lambda x: x >= 0 and x != target, state.values()) if rebased: strippoints = [c.node() for c in repo.set("roots(%ld)", rebased)] # no backup of rebased cset versions needed repair.strip(repo.ui, repo, strippoints) if activebookmark and activebookmark in repo._bookmarks: bookmarks.activate(repo, activebookmark) clearstatus(repo) repo.ui.warn(_("rebase aborted\n")) return 0
def makecollapsed(ui, repo, parent, revs, branch, opts): 'Creates the collapsed revision on top of parent' last = max(revs) ui.debug(_('updating to revision %d\n') % parent) merge.update(repo, parent.node(), False, False, False) ui.debug(_('reverting to revision %d\n') % last) recreaterev(ui, repo, last) repo.dirstate.setbranch(branch) msg = '' if opts['message'] != "" : msg = opts['message'] else: first = True for r in revs: if not first: msg += '----------------\n' first = False msg += repo[r].description() + "\n" msg += "\nHG: Enter commit message. Lines beginning with 'HG:' are removed.\n" msg += "HG: Remove all lines to abort the collapse operation.\n" if ui.config('ui', 'interactive') != 'off': msg = ui.edit(msg, ui.username()) pattern = re.compile("^HG:.*\n", re.MULTILINE); msg = re.sub(pattern, "", msg).strip(); if not msg: raise util.Abort(_('empty commit message, collapse won\'t proceed')) newrev = repo.commit( text=msg, user=repo[last].user(), date=repo[last].date()) return repo[newrev]
def publish(ui, repo, source="", node="default", **opts): # 只对静态编译框架维护的库进行操作 if not opm.StaticPackage.is_root(repo.root) or source == "pull": return publish_branch = ui.config("opm", "publish-branch", "default") # 默认作为发布源的分支名称 node = repo[node] node_branch = node.branch() # 不是需要被编译的分支 # 不判断分支,因为通过changegroup hook触发编译时,获取到的分支信息有可能不是default,导致不编译 # if node_branch != publish_branch: # ui.warn('%s: ignore branch %s\n' % (repo.root, node_branch)) # return # update到需要编译的分支 mergemod.update(repo, publish_branch, False, False, None) # 生成commitlog commitlog_path = os.path.realpath(os.path.join(repo.root, "./commitlog.txt")) parent = node.parents()[0].rev() rev = node.rev() ui.write("%s: update version from %s to %s\n" % (repo.root, parent, rev)) os.chdir(repo.root) os.system("hg log -b %s -r %s:%s > %s" % (node_branch, parent, rev, commitlog_path)) # 编译自己 _publish(ui, repo, commitlog_path, rebuild=True) # 编译依赖自己的库 package = opm.StaticPackage(repo.root) for repo_path in package.get_reverse_libs(all=True): sub_repo = hg.repository(ui, repo_path) _publish(sub_repo.ui, sub_repo, commitlog_path, rebuild=False) # 删除commitlog os.remove(commitlog_path)
def abort(repo, originalwd, target, state): 'Restore the repository to its original state' descendants = repo.changelog.descendants ispublic = lambda r: repo._phaserev[r] == phases.public if filter(ispublic, descendants(target)): repo.ui.warn(_("warning: immutable rebased changeset detected, " "can't abort\n")) return -1 elif set(descendants(target)) - set(state.values()): repo.ui.warn(_("warning: new changesets detected on target branch, " "can't abort\n")) return -1 else: # Strip from the first rebased revision merge.update(repo, repo[originalwd].rev(), False, True, False) rebased = filter(lambda x: x > -1 and x != target, state.values()) if rebased: strippoint = min(rebased) # no backup of rebased cset versions needed repair.strip(repo.ui, repo, repo[strippoint].node()) clearstatus(repo) repo.ui.warn(_('rebase aborted\n')) return 0
def makecollapsed(ui, repo, parent, revs, branch, opts): 'Creates the collapsed revision on top of parent' last = max(revs) ui.debug(_('updating to revision %d\n') % parent) merge.update(repo, parent.node(), False, False, False) ui.debug(_('reverting to revision %d\n') % last) recreaterev(ui, repo, last) repo.dirstate.setbranch(branch) msg = '' if opts['message'] != "": msg = opts['message'] else: first = True for r in revs: if not first: msg += '----------------\n' first = False msg += repo[r].description() + "\n" msg += "\nHG: Enter commit message. Lines beginning with 'HG:' are removed.\n" msg += "HG: Remove all lines to abort the collapse operation.\n" if ui.config('ui', 'interactive') != 'off': msg = ui.edit(msg, ui.username()) pattern = re.compile("^HG:.*\n", re.MULTILINE) msg = re.sub(pattern, "", msg).strip() if not msg: raise util.Abort(_('empty commit message, collapse won\'t proceed')) newrev = repo.commit(text=msg, user=repo[last].user(), date=repo[last].date()) return repo[newrev]
def rebasemerge(repo, rev, first=False): 'return the correct ancestor' oldancestor = ancestor.ancestor def newancestor(a, b, pfunc): ancestor.ancestor = oldancestor if b == rev: return repo[rev].parents()[0].rev() return ancestor.ancestor(a, b, pfunc) if not first: ancestor.ancestor = newancestor else: repo.ui.debug(_("first revision, do not change ancestor\n")) stats = merge.update(repo, rev, True, True, False) return stats
def applychanges(ui, repo, ctx, opts): """Merge changeset from ctx (only) in the current working directory""" wcpar = repo.dirstate.parents()[0] if ctx.p1().node() == wcpar: # edition ar "in place" we do not need to make any merge, # just applies changes on parent for edition cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True) stats = None else: try: # ui.forcemerge is an internal variable, do not document repo.ui.setconfig("ui", "forcemerge", opts.get("tool", "")) stats = mergemod.update(repo, ctx.node(), True, True, False, ctx.p1().node()) finally: repo.ui.setconfig("ui", "forcemerge", "") repo.setparents(wcpar, node.nullid) repo.dirstate.write() # fix up dirstate for copies and renames cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev()) return stats
def applychanges(ui, repo, ctx, opts): """Merge changeset from ctx (only) in the current working directory""" wcpar = repo.dirstate.parents()[0] if ctx.p1().node() == wcpar: # edition ar "in place" we do not need to make any merge, # just applies changes on parent for edition cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True) stats = None else: try: # ui.forcemerge is an internal variable, do not document repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', '')) stats = mergemod.update(repo, ctx.node(), True, True, False, ctx.p1().node()) finally: repo.ui.setconfig('ui', 'forcemerge', '') repo.setparents(wcpar, node.nullid) repo.dirstate.write() # fix up dirstate for copies and renames cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev()) return stats
def apply(self, repo, source, revmap, merges, opts={}): '''apply the revisions in revmap one by one in revision order''' revs = sorted(revmap) p1, p2 = repo.dirstate.parents() pulls = [] diffopts = patch.diffopts(self.ui, opts) diffopts.git = True lock = wlock = tr = None try: wlock = repo.wlock() lock = repo.lock() tr = repo.transaction('transplant') for rev in revs: node = revmap[rev] revstr = '%s:%s' % (rev, short(node)) if self.applied(repo, node, p1): self.ui.warn(_('skipping already applied revision %s\n') % revstr) continue parents = source.changelog.parents(node) if not (opts.get('filter') or opts.get('log')): # If the changeset parent is the same as the # wdir's parent, just pull it. if parents[0] == p1: pulls.append(node) p1 = node continue if pulls: if source != repo: repo.pull(source.peer(), heads=pulls) merge.update(repo, pulls[-1], False, False, None) p1, p2 = repo.dirstate.parents() pulls = [] domerge = False if node in merges: # pulling all the merge revs at once would mean we # couldn't transplant after the latest even if # transplants before them fail. domerge = True if not hasnode(repo, node): repo.pull(source.peer(), heads=[node]) skipmerge = False if parents[1] != revlog.nullid: if not opts.get('parent'): self.ui.note(_('skipping merge changeset %s:%s\n') % (rev, short(node))) skipmerge = True else: parent = source.lookup(opts['parent']) if parent not in parents: raise util.Abort(_('%s is not a parent of %s') % (short(parent), short(node))) else: parent = parents[0] if skipmerge: patchfile = None else: fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-') fp = os.fdopen(fd, 'w') gen = patch.diff(source, parent, node, opts=diffopts) for chunk in gen: fp.write(chunk) fp.close() del revmap[rev] if patchfile or domerge: try: try: n = self.applyone(repo, node, source.changelog.read(node), patchfile, merge=domerge, log=opts.get('log'), filter=opts.get('filter')) except TransplantError: # Do not rollback, it is up to the user to # fix the merge or cancel everything tr.close() raise if n and domerge: self.ui.status(_('%s merged at %s\n') % (revstr, short(n))) elif n: self.ui.status(_('%s transplanted to %s\n') % (short(node), short(n))) finally: if patchfile: os.unlink(patchfile) tr.close() if pulls: repo.pull(source.peer(), heads=pulls) merge.update(repo, pulls[-1], False, False, None) finally: self.saveseries(revmap, merges) self.transplants.write() if tr: tr.release() lock.release() wlock.release()
def do_collapse(ui, repo, first, last, revs, movelog, timedelta, opts): ui.debug(_('Collapsing revisions %s\n') % revs) if opts['debugdelay']: debug_delay = float(opts['debugdelay']) else: debug_delay = False 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) try: cmdutil.bailifchanged(repo) except AttributeError: cmdutil.bail_if_changed(repo) parent = repo[first].parents()[0] tomove = list(repo.changelog.descendants(last)) head_hgtags = get_hgtags_from_heads(ui, repo, last) if '.hgtags' in parent: parent_hgtags = parent['.hgtags'].data() else: parent_hgtags = False movemap = dict.fromkeys(tomove, nullrev) ui.debug(_('will move revisions: %s\n') % tomove) tagsmap = dict() if opts['noop']: ui.status(_('noop: not collapsing\n')) else: origparent = repo['.'].rev() collapsed = None try: branch = repo[last].branch() collapsed = makecollapsed(ui, repo, parent, revs, branch, tagsmap, parent_hgtags, movelog, opts) movemap[max(revs)] = collapsed movedescendants(ui, repo, collapsed, tomove, movemap, tagsmap, parent_hgtags, movelog, debug_delay) fix_hgtags(ui, repo, head_hgtags, tagsmap) 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 update(rev): merge.update(repo, rev, False, True, False)
def merge_(rev): merge.update(repo, rev, branchmerge=True, force=False)
def merge_(rev): merge.update(repo, rev, True, False, False)
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 fixupamend(ui, repo): """rebases any children found on the preamend changset and strips the preamend changset """ wlock = None lock = None tr = None try: wlock = repo.wlock() lock = repo.lock() current = repo['.'] # Use obsolescence information to fix up the amend instead of relying # on the preamend bookmark if the user enables this feature. if ui.configbool('fbamend', 'userestack'): with repo.transaction('fixupamend') as tr: try: common.restackonce(ui, repo, current.rev()) except error.InterventionRequired: tr.close() raise return preamendname = _preamendname(repo, current.node()) if not preamendname in repo._bookmarks: raise error.Abort(_('no bookmark %s') % preamendname, hint=_('check if your bookmark is active')) old = repo[preamendname] if old == current: hint = _('please examine smartlog and rebase your changsets ' 'manually') err = _('cannot automatically determine what to rebase ' 'because bookmark "%s" points to the current changset') % \ preamendname raise error.Abort(err, hint=hint) oldbookmarks = old.bookmarks() ui.status(_("rebasing the children of %s\n") % (preamendname)) active = bmactive(repo) opts = { 'rev' : [str(c.rev()) for c in old.descendants()], 'dest' : current.rev() } if opts['rev'] and opts['rev'][0]: rebasemod.rebase(ui, repo, **opts) changes = [] for bookmark in oldbookmarks: changes.append((bookmark, None)) # delete the bookmark tr = repo.transaction('fixupamend') repo._bookmarks.applychanges(repo, tr, changes) if obsolete.isenabled(repo, obsolete.createmarkersopt): tr.close() else: tr.close() repair.strip(ui, repo, old.node(), topic='preamend-backup') merge.update(repo, current.node(), False, True, False) if active: bmactivate(repo, active) finally: lockmod.release(wlock, lock, tr)
def apply(self, repo, source, revmap, merges, opts={}): '''apply the revisions in revmap one by one in revision order''' revs = revmap.keys() revs.sort() p1, p2 = repo.dirstate.parents() pulls = [] diffopts = patch.diffopts(self.ui, opts) diffopts.git = True lock = wlock = None try: wlock = repo.wlock() lock = repo.lock() for rev in revs: node = revmap[rev] revstr = '%s:%s' % (rev, revlog.short(node)) if self.applied(repo, node, p1): self.ui.warn(_('skipping already applied revision %s\n') % revstr) continue parents = source.changelog.parents(node) if not opts.get('filter'): # If the changeset parent is the same as the wdir's parent, # just pull it. if parents[0] == p1: pulls.append(node) p1 = node continue if pulls: if source != repo: repo.pull(source, heads=pulls) merge.update(repo, pulls[-1], False, False, None) p1, p2 = repo.dirstate.parents() pulls = [] domerge = False if node in merges: # pulling all the merge revs at once would mean we couldn't # transplant after the latest even if transplants before them # fail. domerge = True if not hasnode(repo, node): repo.pull(source, heads=[node]) if parents[1] != revlog.nullid: self.ui.note(_('skipping merge changeset %s:%s\n') % (rev, revlog.short(node))) patchfile = None else: fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-') fp = os.fdopen(fd, 'w') patch.diff(source, parents[0], node, fp=fp, opts=diffopts) fp.close() del revmap[rev] if patchfile or domerge: try: n = self.applyone(repo, node, source.changelog.read(node), patchfile, merge=domerge, log=opts.get('log'), filter=opts.get('filter')) if n and domerge: self.ui.status(_('%s merged at %s\n') % (revstr, revlog.short(n))) elif n: self.ui.status(_('%s transplanted to %s\n') % (revlog.short(node), revlog.short(n))) finally: if patchfile: os.unlink(patchfile) if pulls: repo.pull(source, heads=pulls) merge.update(repo, pulls[-1], False, False, None) finally: self.saveseries(revmap, merges) self.transplants.write() del lock, wlock
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'))