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 updatedirstate(repo, rev, p1, p2): """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[p1], repo[p2], True) m1 = repo[rev].manifest() m2 = repo[p1].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)
def visualdiff(ui, repo, pats, opts): revs = opts.get('rev', []) change = opts.get('change') try: ctx1b = None if change: ctx2 = repo[change] p = ctx2.parents() if len(p) > 1: ctx1a, ctx1b = p else: ctx1a = p[0] else: n1, n2 = hglib.revpair(repo, revs) ctx1a, ctx2 = repo[n1], repo[n2] p = ctx2.parents() if not revs and len(p) > 1: ctx1b = p[1] except (error.LookupError, error.RepoError): QMessageBox.warning(None, _('Unable to find changeset'), _('You likely need to refresh this application')) return None pats = hglib.expandpats(pats) m = match.match(repo.root, '', pats, None, None, 'relpath') n2 = ctx2.node() mod_a, add_a, rem_a = map(set, repo.status(ctx1a.node(), n2, m)[:3]) if ctx1b: mod_b, add_b, rem_b = map(set, repo.status(ctx1b.node(), n2, m)[:3]) cpy = copies.copies(repo, ctx1a, ctx1b, ctx1a.ancestor(ctx1b))[0] else: cpy = copies.copies(repo, ctx1a, ctx2, repo[-1])[0] mod_b, add_b, rem_b = set(), set(), set() MA = mod_a | add_a | mod_b | add_b MAR = MA | rem_a | rem_b if not MAR: QMessageBox.information(None, _('No file changes'), _('There are no file changes to view')) return None detectedtools = hglib.difftools(repo.ui) if not detectedtools: QMessageBox.warning(None, _('No diff tool found'), _('No visual diff tools were detected')) return None preferred = besttool(repo.ui, detectedtools, opts.get('tool')) # Build tool list based on diff-patterns matches toollist = set() patterns = repo.ui.configitems('diff-patterns') patterns = [(p, t) for p,t in patterns if t in detectedtools] for path in MAR: for pat, tool in patterns: mf = match.match(repo.root, '', [pat]) if mf(path): toollist.add(tool) break else: toollist.add(preferred) cto = cpy.keys() for path in MAR: if path in cto: hascopies = True break else: hascopies = False force = repo.ui.configbool('tortoisehg', 'forcevdiffwin') if len(toollist) > 1 or (hascopies and len(MAR) > 1) or force: usewin = True else: preferred = toollist.pop() dirdiff = repo.ui.configbool('merge-tools', preferred + '.dirdiff') dir3diff = repo.ui.configbool('merge-tools', preferred + '.dir3diff') usewin = repo.ui.configbool('merge-tools', preferred + '.usewin') if not usewin and len(MAR) > 1: if ctx1b is not None: usewin = not dir3diff else: usewin = not dirdiff if usewin: # Multiple required tools, or tool does not support directory diffs sa = [mod_a, add_a, rem_a] sb = [mod_b, add_b, rem_b] dlg = FileSelectionDialog(repo, pats, ctx1a, sa, ctx1b, sb, ctx2, cpy) return dlg # We can directly use the selected tool, without a visual diff window diffcmd, diffopts, mergeopts = detectedtools[preferred] # Disable 3-way merge if there is only one parent or no tool support do3way = False if ctx1b: if mergeopts: do3way = True args = mergeopts else: args = diffopts if str(ctx1b.rev()) in revs: ctx1a = ctx1b else: args = diffopts def dodiff(): assert not (hascopies and len(MAR) > 1), \ 'dodiff cannot handle copies when diffing dirs' sa = [mod_a, add_a, rem_a] sb = [mod_b, add_b, rem_b] ctxs = [ctx1a, ctx1b, ctx2] # If more than one file, diff on working dir copy. copyworkingdir = len(MAR) > 1 dirs, labels, fns_and_mtimes = snapshotset(repo, ctxs, sa, sb, cpy, copyworkingdir) dir1a, dir1b, dir2 = dirs label1a, label1b, label2 = labels fns_and_mtime = fns_and_mtimes[2] if len(MAR) > 1 and label2 == '': label2 = 'working files' def getfile(fname, dir, label): file = os.path.join(qtlib.gettempdir(), dir, fname) if os.path.isfile(file): return fname+label, file nullfile = os.path.join(qtlib.gettempdir(), 'empty') fp = open(nullfile, 'w') fp.close() return (hglib.fromunicode(_nonexistant, 'replace') + label, nullfile) # If only one change, diff the files instead of the directories # Handle bogus modifies correctly by checking if the files exist if len(MAR) == 1: file2 = util.localpath(MAR.pop()) if file2 in cto: file1 = util.localpath(cpy[file2]) else: file1 = file2 label1a, dir1a = getfile(file1, dir1a, label1a) if do3way: label1b, dir1b = getfile(file1, dir1b, label1b) label2, dir2 = getfile(file2, dir2, label2) if do3way: label1a += '[local]' label1b += '[other]' label2 += '[merged]' replace = dict(parent=dir1a, parent1=dir1a, parent2=dir1b, plabel1=label1a, plabel2=label1b, phash1=str(ctx1a), phash2=str(ctx1b), repo=hglib.fromunicode(repo.displayname), clabel=label2, child=dir2, chash=str(ctx2)) launchtool(diffcmd, args, replace, True) # detect if changes were made to mirrored working files for copy_fn, working_fn, mtime in fns_and_mtime: try: if os.lstat(copy_fn).st_mtime != mtime: ui.debug('file changed while diffing. ' 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn)) util.copyfile(copy_fn, working_fn) except EnvironmentError: pass # Ignore I/O errors or missing files def dodiffwrapper(): try: dodiff() finally: # cleanup happens atexit ui.note(_('cleaning up temp directory\n')) if opts.get('mainapp'): dodiffwrapper() else: # We are not the main application, so this must be done in a # background thread thread = threading.Thread(target=dodiffwrapper, name='visualdiff') thread.setDaemon(True) thread.start()