def savediff(): opts = {'git': True} fp = opener('.saved', 'w') for chunk in patch.diff(repo, head, None, opts=patch.diffopts(self.ui, opts)): fp.write(chunk) fp.close()
def getChunksForFile(self, wfile): repo = self.repo ctx = self.ctx if isinstance(ctx, patchctx): if wfile in ctx._files: return ctx._files[wfile] else: return [] else: buf = cStringIO.StringIO() diffopts = patch.diffopts(repo.ui, {'git': True}) m = matchmod.exact(repo.root, repo.root, [wfile]) for p in patch.diff(repo, ctx.p1().node(), None, match=m, opts=diffopts): buf.write(p) buf.seek(0) chunks = record.parsepatch(buf) if chunks: header = chunks[0] return [header] + header.hunks else: return []
def cdm_pdiffs(ui, repo, *pats, **opts): '''diff workspace against its parent Show differences between this workspace and its parent workspace in the same manner as 'hg diff'. For a description of the changeset used to represent the parent workspace, see The Parent in the extension documentation ('hg help cdm'). ''' act = wslist[repo].active(opts.get('parent')) if not act.revs: return # # If no patterns were specified, either explicitly or via -I or -X # use the active list files to avoid a workspace walk. # if pats or opts.get('include') or opts.get('exclude'): matchfunc = wslist[repo].matcher(pats=pats, opts=opts) else: matchfunc = wslist[repo].matcher(files=act.files()) opts = patch.diffopts(ui, opts) diffs = wslist[repo].diff(act.parenttip.node(), act.localtip.node(), match=matchfunc, opts=opts) if diffs: ui.write(diffs)
def diff(orig, ui, repo, *args, **opts): """show a diff of the most recent revision against its parent from svn """ if not opts.get('svn', False) or opts.get('change', None): return orig(ui, repo, *args, **opts) meta = repo.svnmeta() hashes = meta.revmap.hashes() if not opts.get('rev', None): parent = repo.parents()[0] o_r = util.outgoing_revisions(repo, hashes, parent.node()) if o_r: parent = repo[o_r[-1]].parents()[0] opts['rev'] = ['%s:.' % node.hex(parent.node()), ] node1, node2 = cmdutil.revpair(repo, opts['rev']) baserev, _junk = hashes.get(node1, (-1, 'junk')) newrev, _junk = hashes.get(node2, (-1, 'junk')) it = patch.diff(repo, node1, node2, opts=patch.diffopts(ui, opts={'git': True, 'show_function': False, 'ignore_all_space': False, 'ignore_space_change': False, 'ignore_blank_lines': False, 'unified': True, 'text': False, })) ui.write(util.filterdiff(''.join(it), baserev, newrev))
def recordfunc(ui, repo, message, match, opts): """This is generic record driver. Its job is to interactively filter local changes, and accordingly prepare working directory into a state in which the job can be delegated to a non-interactive commit command such as 'commit' or 'qrefresh'. After the actual job is done by non-interactive command, the working directory is restored to its original state. In the end we'll record interesting changes, and everything else will be left in place, so the user can continue working. """ cmdutil.checkunfinished(repo, commit=True) merge = len(repo[None].parents()) > 1 if merge: raise util.Abort(_("cannot partially commit a merge " '(use "hg commit" instead)')) status = repo.status(match=match) diffopts = opts.copy() diffopts["nodates"] = True diffopts["git"] = True diffopts = patch.diffopts(ui, opts=diffopts) chunks = patch.diff(repo, changes=status, opts=diffopts) fp = cStringIO.StringIO() fp.write("".join(chunks)) fp.seek(0) # 1. filter patch, so we have intending-to apply subset of it try: chunks = filterpatch(ui, parsepatch(fp)) except patch.PatchError, err: raise util.Abort(_("error parsing patch: %s") % err)
def pick(ui, repo, ctx, ha, opts): oldctx = repo[ha] if oldctx.parents()[0] == ctx: ui.debug('node %s unchanged\n' % ha) return oldctx, [], [], [] hg.update(repo, ctx.node()) fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') fp = os.fdopen(fd, 'w') diffopts = patch.diffopts(ui, opts) diffopts.git = True gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts) for chunk in gen: fp.write(chunk) fp.close() try: files = {} try: patch.patch(patchfile, ui, cwd=repo.root, files=files, eolmode=None) if not files: ui.warn(_('%s: empty changeset') % node.hex(ha)) return ctx, [], [], [] finally: files = patch.updatedir(ui, repo, files) os.unlink(patchfile) except Exception, inst: raise util.Abort(_('Fix up the change and run ' 'hg histedit --continue'))
def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges): parent = ctx.parents()[0].node() hg.update(repo, parent) fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') fp = os.fdopen(fd, 'w') diffopts = patch.diffopts(ui, opts) diffopts.git = True gen = patch.diff(repo, parent, newnode, opts=diffopts) for chunk in gen: fp.write(chunk) fp.close() files = {} try: patch.patch(patchfile, ui, cwd=repo.root, files=files, eolmode=None) finally: files = patch.updatedir(ui, repo, files) os.unlink(patchfile) newmessage = '\n***\n'.join( [ctx.description(), ] + [repo[r].description() for r in internalchanges] + [oldctx.description(), ]) newmessage = ui.edit(newmessage, ui.username()) n = repo.commit(text=newmessage, user=ui.username(), date=max(ctx.date(), oldctx.date()), extra=oldctx.extra()) return repo[n], [n, ], [oldctx.node(), ctx.node() ], [newnode, ] # xxx
def generate_text_diffs(self, row): wfile = self.filemodel[row][FM_PATH] pfile = util.pconvert(wfile) lines = chunks.check_max_diff(self.get_ctx(), pfile) if lines: return self.diff_highlight_buffer(lines) matcher = cmdutil.matchfiles(self.repo, [pfile]) opts = patch.diffopts(self.ui, self.opts) opts.git = True difftext = [] if self.is_merge(): wctx = self.repo[None] pctx1, pctx2 = wctx.parents() difftext = [_('===== Diff to first parent %d:%s =====\n') % ( pctx1.rev(), str(pctx1))] try: for s in patch.diff(self.repo, pctx1.node(), None, match=matcher, opts=opts): difftext.extend(s.splitlines(True)) difftext.append(_('\n===== Diff to second parent %d:%s =====\n') % ( pctx2.rev(), str(pctx2))) for s in patch.diff(self.repo, pctx2.node(), None, match=matcher, opts=opts): difftext.extend(s.splitlines(True)) except (IOError, error.RepoError, error.LookupError, util.Abort), e: self.stbar.set_text(str(e))
def dohgdiff(): difftext = StringIO.StringIO() try: if len(files) != 0: wfiles = [self.repo.wjoin(x) for x in files] fns, matchfn, anypats = cmdutil.matchpats(self.repo, wfiles, self.opts) patch.diff(self.repo, self._node1, self._node2, fns, match=matchfn, fp=difftext, opts=patch.diffopts(self.ui, self.opts)) buffer = gtk.TextBuffer() buffer.create_tag('removed', foreground='#900000') buffer.create_tag('added', foreground='#006400') buffer.create_tag('position', foreground='#FF8000') buffer.create_tag('header', foreground='#000090') difftext.seek(0) iter = buffer.get_start_iter() for line in difftext: line = toutf(line) if line.startswith('---') or line.startswith('+++'): buffer.insert_with_tags_by_name(iter, line, 'header') elif line.startswith('-'): buffer.insert_with_tags_by_name(iter, line, 'removed') elif line.startswith('+'): buffer.insert_with_tags_by_name(iter, line, 'added') elif line.startswith('@@'): buffer.insert_with_tags_by_name(iter, line, 'position') else: buffer.insert(iter, line) self.diff_text.set_buffer(buffer) finally: difftext.close()
def savediff(): opts = {'git': True} fp = self.opener('.saved', 'w') for chunk in patch.diff(repo, head, None, opts=patch.diffopts(self.ui, opts)): fp.write(chunk) fp.close()
def autodiff(ui, repo, *pats, **opts): diffopts = patch.diffopts(ui, opts) git = opts.get('git', 'no') brokenfiles = set() losedatafn = None if git in ('yes', 'no'): diffopts.git = git == 'yes' diffopts.upgrade = False elif git == 'auto': diffopts.git = False diffopts.upgrade = True elif git == 'warn': diffopts.git = False diffopts.upgrade = True def losedatafn(fn=None, **kwargs): brokenfiles.add(fn) return True elif git == 'abort': diffopts.git = False diffopts.upgrade = True def losedatafn(fn=None, **kwargs): raise util.Abort('losing data for %s' % fn) else: raise util.Abort('--git must be yes, no or auto') node1, node2 = scmutil.revpair(repo, []) m = scmutil.match(repo[node2], pats, opts) it = patch.diff(repo, node1, node2, match=m, opts=diffopts, losedatafn=losedatafn) for chunk in it: ui.write(chunk) for fn in sorted(brokenfiles): ui.write(('data lost for: %s\n' % fn))
def __init__(self, repoagent, parent=None): super(AnnotateView, self).__init__(parent) self.setReadOnly(True) self.setMarginLineNumbers(1, True) self.setMarginType(2, qsci.TextMarginRightJustified) self.setMouseTracking(False) self._repoagent = repoagent repo = repoagent.rawRepo() # TODO: replace by repoagent if sci.repo = bundlerepo can be removed self.repo = repo self._annotation_enabled = False self._links = [] # by line self._anncache = {} # by rev self._revmarkers = {} # by rev self._lastrev = None diffopts = patch.diffopts(repo.ui, section='annotate') self._thread = AnnotateThread(self, diffopts=diffopts) self._thread.finished.connect(self.fillModel) self._initAnnotateOptionActions() self._repoagent.configChanged.connect(self.configChanged) self.configChanged() self._loadAnnotateSettings()
def fold(ui, repo, ctx, ha, opts): oldctx = repo[ha] hg.update(repo, ctx.node()) fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') fp = os.fdopen(fd, 'w') diffopts = patch.diffopts(ui, opts) diffopts.git = True diffopts.ignorews = False diffopts.ignorewsamount = False diffopts.ignoreblanklines = False gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts) for chunk in gen: fp.write(chunk) fp.close() try: files = set() try: applypatch(ui, repo, patchfile, files=files, eolmode=None) if not files: ui.warn(_('%s: empty changeset') % node.hex(ha)) return ctx, [], [], [] finally: os.unlink(patchfile) except Exception, inst: raise util.Abort( _('Fix up the change and run ' 'hg histedit --continue'))
def fold(ui, repo, ctx, ha, opts): oldctx = repo[ha] hg.update(repo, ctx.node()) fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') fp = os.fdopen(fd, 'w') diffopts = patch.diffopts(ui, opts) diffopts.git = True diffopts.ignorews = False diffopts.ignorewsamount = False diffopts.ignoreblanklines = False gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts) for chunk in gen: fp.write(chunk) fp.close() try: files = set() try: applypatch(ui, repo, patchfile, files=files, eolmode=None) if not files: ui.warn(_('%s: empty changeset') % node.hex(ha)) return ctx, [], [], [] finally: os.unlink(patchfile) except Exception, inst: raise util.Abort(_('Fix up the change and run ' 'hg histedit --continue'))
def diff(orig, ui, repo, *args, **opts): """show a diff of the most recent revision against its parent from svn """ if not opts.get('svn', False) or opts.get('change', None): return orig(ui, repo, *args, **opts) meta = repo.svnmeta() hashes = meta.revmap.hashes() if not opts.get('rev', None): parent = repo[None].parents()[0] o_r = util.outgoing_revisions(repo, hashes, parent.node()) if o_r: parent = repo[o_r[-1]].parents()[0] opts['rev'] = ['%s:.' % node.hex(parent.node()), ] node1, node2 = scmutil.revpair(repo, opts['rev']) if not isinstance(node1, bytes): # hg 4.6 and later return contexts, so convert to bytestr node1, node2 = node1.node(), node2.node() baserev, _junk = hashes.get(node1, (-1, 'junk')) newrev, _junk = hashes.get(node2, (-1, 'junk')) it = patch.diff(repo, node1, node2, opts=patch.diffopts(ui, opts={'git': True, 'show_function': False, 'ignore_all_space': False, 'ignore_space_change': False, 'ignore_blank_lines': False, 'unified': True, 'text': False, })) ui.write(util.filterdiff(''.join(it), baserev, newrev))
def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges): parent = ctx.parents()[0].node() hg.update(repo, parent) fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') fp = os.fdopen(fd, 'w') diffopts = patch.diffopts(ui, opts) diffopts.git = True diffopts.ignorews = False diffopts.ignorewsamount = False diffopts.ignoreblanklines = False gen = patch.diff(repo, parent, newnode, opts=diffopts) for chunk in gen: fp.write(chunk) fp.close() files = set() try: applypatch(ui, repo, patchfile, files=files, eolmode=None) finally: os.unlink(patchfile) newmessage = '\n***\n'.join( [ctx.description(), ] + [repo[r].description() for r in internalchanges] + [oldctx.description(), ]) # If the changesets are from the same author, keep it. if ctx.user() == oldctx.user(): username = ctx.user() else: username = ui.username() newmessage = ui.edit(newmessage, username) n = repo.commit(text=newmessage, user=username, date=max(ctx.date(), oldctx.date()), extra=oldctx.extra()) return repo[n], [n, ], [oldctx.node(), ctx.node() ], [newnode, ] # xxx
def diffs(repo, tmpl, ctx, basectx, files, parity, style): def countgen(): start = 1 while True: yield start start += 1 blockcount = countgen() def prettyprintlines(diff, blockno): for lineno, l in enumerate(diff.splitlines(True)): difflineno = "%d.%d" % (blockno, lineno + 1) if l.startswith('+'): ltype = "difflineplus" elif l.startswith('-'): ltype = "difflineminus" elif l.startswith('@'): ltype = "difflineat" else: ltype = "diffline" yield tmpl(ltype, line=l, lineno=lineno + 1, lineid="l%s" % difflineno, linenumber="% 8s" % difflineno) if files: m = match.exact(repo.root, repo.getcwd(), files) else: m = match.always(repo.root, repo.getcwd()) diffopts = patch.diffopts(repo.ui, untrusted=True) if basectx is None: parents = ctx.parents() if parents: node1 = parents[0].node() else: node1 = nullid else: node1 = basectx.node() node2 = ctx.node() block = [] for chunk in patch.diff(repo, node1, node2, m, opts=diffopts): if chunk.startswith('diff') and block: blockno = blockcount.next() yield tmpl('diffblock', parity=parity.next(), blockno=blockno, lines=prettyprintlines(''.join(block), blockno)) block = [] if chunk.startswith('diff') and style != 'raw': chunk = ''.join(chunk.splitlines(True)[1:]) block.append(chunk) blockno = blockcount.next() yield tmpl('diffblock', parity=parity.next(), blockno=blockno, lines=prettyprintlines(''.join(block), blockno))
def _show(self, ctx, copies, matchfn, props): if not matchfn: matchfn = self.patch node = ctx.node() diffopts = patch.diffopts(self.ui, self.diffopts) prev = self.repo.changelog.parents(node)[0] self.diff(diffopts, prev, node, match=matchfn) self.ui.write("\n")
def getpatches(revs): prev = repo['.'].rev() for r in scmutil.revrange(repo, revs): if r == prev and (repo[None].files() or repo[None].deleted()): ui.warn(_('warning: working directory has ' 'uncommitted changes\n')) output = cStringIO.StringIO() cmdutil.export(repo, [r], fp=output, opts=patch.diffopts(ui, opts)) yield output.getvalue().split('\n')
def getpatches(revs): prev = repo['.'].rev() for r in scmutil.revrange(repo, revs): if r == prev and (repo[None].files() or repo[None].deleted()): ui.warn( _('warning: working directory has ' 'uncommitted changes\n')) output = cStringIO.StringIO() cmdutil.export(repo, [r], fp=output, opts=patch.diffopts(ui, opts)) yield output.getvalue().split('\n')
def difftree(ui, repo, node1=None, node2=None, *files, **opts): """diff trees from two commits""" def __difftree(repo, node1, node2, files=[]): assert node2 is not None mmap = repo[node1].manifest() mmap2 = repo[node2].manifest() m = cmdutil.match(repo, files) modified, added, removed = repo.status(node1, node2, m)[:3] empty = short(nullid) for f in modified: # TODO get file permissions ui.write(":100664 100664 %s %s M\t%s\t%s\n" % (short(mmap[f]), short(mmap2[f]), f, f)) for f in added: ui.write(":000000 100664 %s %s N\t%s\t%s\n" % (empty, short(mmap2[f]), f, f)) for f in removed: ui.write(":100664 000000 %s %s D\t%s\t%s\n" % (short(mmap[f]), empty, f, f)) ## while True: if opts['stdin']: try: line = raw_input().split(' ') node1 = line[0] if len(line) > 1: node2 = line[1] else: node2 = None except EOFError: break node1 = repo.lookup(node1) if node2: node2 = repo.lookup(node2) else: node2 = node1 node1 = repo.changelog.parents(node1)[0] if opts['patch']: if opts['pretty']: catcommit(ui, repo, node2, "") m = cmdutil.match(repo, files) chunks = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, {'git': True})) for chunk in chunks: ui.write(chunk) else: __difftree(repo, node1, node2, files=files) if not opts['stdin']: break
def get_diffs(self): from mercurial import mdiff, util, patch from repo_browser.integration import Diff ctx = self.ctx parent = ctx.parents()[0] parent_date = util.datestr(parent.date()) this_date = util.datestr(ctx.date()) diffopts = patch.diffopts(self.repo.repo.ui, untrusted=True) # Returns a tuple of modified, added, removed, deleted, unknown # TODO: look up in the api what FIXME* are modified, added, removed, deleted, unknown, FIXME, FIXME2 = \ self.repo.repo.status( parent.node(), ctx.node(),) for modified_file in modified: filectx = ctx.filectx(modified_file) parent_filectx = parent.filectx(modified_file) this_data = filectx.data() parent_data = parent_filectx.data() yield Diff( mdiff.unidiff(parent_data, parent_date, this_data, this_date, modified_file, modified_file, opts=diffopts)) for added_file in added: filectx = ctx.filectx(added_file) this_data = filectx.data() yield Diff( mdiff.unidiff(None, parent_date, this_data, this_date, added_file, added_file, opts=diffopts)) for removed_file in removed: parent_filectx = parent.filectx(removed_file) parent_data = parent_filectx.data() yield Diff( mdiff.unidiff(parent_data, parent_date, None, ctx.date(), removed_file, removed_file, opts=diffopts))
def getdiff(ui, repo, r, parent, opts): '''return diff for the specified revision''' output = "" if opts.get('git') or ui.configbool('diff', 'git'): # Git diffs don't include the revision numbers with each file, so # we have to put them in the header instead. output += "# Node ID " + node.hex(r.node()) + "\n" output += "# Parent " + node.hex(parent.node()) + "\n" diffopts = patch.diffopts(ui, opts) for chunk in patch.diff(repo, parent.node(), r.node(), opts=diffopts): output += chunk return output
def shelvefunc(ui, repo, message, match, opts): parents = repo.dirstate.parents() changes = repo.status(match=match)[:3] modified, added, removed = changes diffopts = patch.diffopts(ui, opts={'git': True, 'nodates': True}) chunks = patch.diff(repo, changes=changes, opts=diffopts) fp = cStringIO.StringIO(''.join(chunks)) try: ac = parsepatch(fp) except patch.PatchError, err: raise util.Abort(_('error parsing patch: %s') % err)
def annotate(web, req, tmpl): fctx = webutil.filectx(web.repo, req) f = fctx.path() parity = paritygen(web.stripecount) diffopts = patch.diffopts(web.repo.ui, untrusted=True, section='annotate') def annotate(**map): last = None if binary(fctx.data()): mt = (mimetypes.guess_type(fctx.path())[0] or 'application/octet-stream') lines = enumerate([((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]) else: lines = enumerate( fctx.annotate(follow=True, linenumber=True, diffopts=diffopts)) for lineno, ((f, targetline), l) in lines: fnode = f.filenode() if last != fnode: last = fnode yield { "parity": parity.next(), "node": f.hex(), "rev": f.rev(), "author": f.user(), "desc": f.description(), "extra": f.extra(), "file": f.path(), "targetline": targetline, "line": l, "lineid": "l%d" % (lineno + 1), "linenumber": "% 6d" % (lineno + 1), "revdate": f.date() } return tmpl("fileannotate", file=f, annotate=annotate, path=webutil.up(f), rev=fctx.rev(), node=fctx.hex(), author=fctx.user(), date=fctx.date(), desc=fctx.description(), extra=fctx.extra(), rename=webutil.renamelink(fctx), branch=webutil.nodebranchnodefault(fctx), parent=webutil.parents(fctx), child=webutil.children(fctx), permissions=fctx.manifest().flags(f))
def annotate(web, req, tmpl): fctx = webutil.filectx(web.repo, req) f = fctx.path() parity = paritygen(web.stripecount) diffopts = patch.diffopts(web.repo.ui, untrusted=True, section="annotate") def annotate(**map): last = None if binary(fctx.data()): mt = mimetypes.guess_type(fctx.path())[0] or "application/octet-stream" lines = enumerate([((fctx.filectx(fctx.filerev()), 1), "(binary:%s)" % mt)]) else: lines = enumerate(fctx.annotate(follow=True, linenumber=True, diffopts=diffopts)) for lineno, ((f, targetline), l) in lines: fnode = f.filenode() if last != fnode: last = fnode yield { "parity": parity.next(), "node": f.hex(), "rev": f.rev(), "author": f.user(), "desc": f.description(), "extra": f.extra(), "file": f.path(), "targetline": targetline, "line": l, "lineid": "l%d" % (lineno + 1), "linenumber": "% 6d" % (lineno + 1), "revdate": f.date(), } return tmpl( "fileannotate", file=f, annotate=annotate, path=webutil.up(f), rev=fctx.rev(), node=fctx.hex(), author=fctx.user(), date=fctx.date(), desc=fctx.description(), extra=fctx.extra(), rename=webutil.renamelink(fctx), branch=webutil.nodebranchnodefault(fctx), parent=webutil.parents(fctx), child=webutil.children(fctx), permissions=fctx.manifest().flags(f), )
def diffopts(self, opts={}, patchfn=None): diffopts = patchmod.diffopts(self.ui, opts) if self.gitmode == 'auto': diffopts.upgrade = True elif self.gitmode == 'keep': pass elif self.gitmode in ('yes', 'no'): diffopts.git = self.gitmode == 'yes' else: raise util.Abort(_('mq.git option can be auto/keep/yes/no' ' got %s') % self.gitmode) if patchfn: diffopts = self.patchopts(diffopts, patchfn) return diffopts
def need_backup(self): '''Compare backup of uncommitted changes to workspace''' if self._dirstate() != node.hex(self.ws.repo.changectx().node()): return True curdiff = StringIO() diff = self.bu.backupfile('diff') fd = None patch.diff(self.ws.repo, fp=curdiff, opts=patch.diffopts(self.ws.ui, opts={'git': True})) if os.path.exists(diff): try: try: fd = open(diff) backdiff = fd.read() except EnvironmentError, e: raise util.Abort("couldn't open backup diff %s\n" " %s" % (diff, e)) finally: if fd and not fd.closed: fd.close() else: backdiff = '' if backdiff != curdiff.getvalue(): return True currrenamed = self._clobbering_renames() bakrenamed = None if os.path.exists(self.bu.backupfile('renames')): try: try: fd = open(self.bu.backupfile('renames')) bakrenamed = [line.strip().split(' ') for line in fd] except EnvironmentError, e: raise util.Abort("couldn't open renames file %s: %s\n" % (self.bu.backupfile('renames'), e)) finally: if fd and not fd.closed: fd.close() if currrenamed != bakrenamed: return True return False
def _get_diffs(self, repository=None, filepath=None): diffs = [] for repo, root in self.repositories: if repository and root != repository: continue #commands.pull(thisui, user_repo) # The hg diff command returns the entire set of diffs as one big # chunk. The following code is lifted from the source (version # 1.2) as the method for getting the individual diffs. As such, # this is prone to break in the case of internal changes. We # should try and get an external method to do the same thing. node1, node2 = cmdutil.revpair(repo, None) match = cmdutil.match(repo, (), {}) repodiffs = [] for diff in patch.diff(repo, node1, node2, match=match, opts=patch.diffopts(self.ui)): diffheader = diff.split('\n')[0] filename = DIFF_FILE.match(diffheader).groups()[0] if filepath and filepath == filename: return {'repository':root, 'filename':filename, 'diff': highlight(diff, DiffLexer(), HtmlFormatter())} # Should I instantiate a single lexer and formatter and share them? repodiffs.append({'repository':root, 'filename':filename, 'diff': highlight(diff, DiffLexer(), HtmlFormatter())}) # At the repo level, we want to go through all found files and look # for related issues try: issues = yamltrak.issues([repo.root])[root] except KeyError: # There is no issue database, or maybe just no open issues... issues = {} for diff in repodiffs: relatedissues = yamltrak.relatedissues(repo.root, filename=diff['filename'], ids=issues.keys()) related = {} for issue in relatedissues: related[issue] = {'repo':root, 'title':issues[issue]['title']} diff['relatedissues'] = related diffs += repodiffs # Done collecting the diffs if filepath: # User wanted a specific diff, and we didn't find it. This # probably isn't the best exception, but it will have to do... raise LookupError return diffs
def diff_branch(ui, repo, branch, **opts): """Shows the changes from a branch""" if branch not in repo.branchtags(): ui.warn("Branch %s does not exist! (use 'hg branches' to get a list of branches)\n" % branch) return curr = repo[None].branch() if branch == curr: ui.status("Already on branch %s\n" % branch) return rev = repo.branchtip(branch) dest = "default" drev = repo.branchtip(dest) ancestor = repo.changelog.ancestor(rev, drev) diffopts = patch.diffopts(ui, opts) cmdutil.diffordiffstat(ui, repo, diffopts, ancestor, rev, None)
def diff(self, node1=None, node2=None, match=None, opts=None): '''Return the diff of changes between two changesets as a string''' # # Retain compatibility by only calling diffopts() if it # obviously has not already been done. # if isinstance(opts, dict): opts = patch.diffopts(self.ui, opts) ret = cStringIO.StringIO() for chunk in patch.diff(self.repo, node1, node2, match=match, opts=opts): ret.write(chunk) return ret.getvalue()
def update_commit_preview(self): if self.is_merge(): opts = patch.diffopts(self.ui, self.opts) opts.git = True wctx = self.repo[None] pctx1, pctx2 = wctx.parents() difftext = [_('===== Diff to first parent %d:%s =====\n') % ( pctx1.rev(), str(pctx1))] try: for s in patch.diff(self.repo, pctx1.node(), None, opts=opts): difftext.extend(s.splitlines(True)) difftext.append(_('\n===== Diff to second parent %d:%s =====\n') % ( pctx2.rev(), str(pctx2))) for s in patch.diff(self.repo, pctx2.node(), None, opts=opts): difftext.extend(s.splitlines(True)) except (IOError, error.RepoError, error.LookupError, util.Abort), e: self.stbar.set_text(str(e))
def pdiff(self, pats, opts, parent=None): 'Return diffs relative to PARENT, as best as we can make out' parent = self.parent(parent) act = self.active(parent) # # act.localtip maybe nil, in the case of uncommitted local # changes. # if not act.revs: return matchfunc = cmdutil.match(self.repo, pats, opts) opts = patch.diffopts(self.ui, opts) return self.diff(act.parenttip.node(), act.localtip.node(), match=matchfunc, opts=opts)
def diffs(repo, tmpl, ctx, files, parity, style): def countgen(): start = 1 while True: yield start start += 1 blockcount = countgen() def prettyprintlines(diff): blockno = blockcount.next() for lineno, l in enumerate(diff.splitlines(True)): lineno = "%d.%d" % (blockno, lineno + 1) if l.startswith("+"): ltype = "difflineplus" elif l.startswith("-"): ltype = "difflineminus" elif l.startswith("@"): ltype = "difflineat" else: ltype = "diffline" yield tmpl(ltype, line=l, lineid="l%s" % lineno, linenumber="% 8s" % lineno) if files: m = match.exact(repo.root, repo.getcwd(), files) else: m = match.always(repo.root, repo.getcwd()) diffopts = patch.diffopts(repo.ui, untrusted=True) parents = ctx.parents() node1 = parents and parents[0].node() or nullid node2 = ctx.node() block = [] for chunk in patch.diff(repo, node1, node2, m, opts=diffopts): if chunk.startswith("diff") and block: yield tmpl("diffblock", parity=parity.next(), lines=prettyprintlines("".join(block))) block = [] if chunk.startswith("diff") and style != "raw": chunk = "".join(chunk.splitlines(True)[1:]) block.append(chunk) yield tmpl("diffblock", parity=parity.next(), lines=prettyprintlines("".join(block)))
def backup(self): '''Backup uncommitted changes''' if self.ws.merged(): raise util.Abort("Unable to backup an uncommitted merge.\n" "Please complete your merge and commit") dirstate = node.hex(self.ws.repo.changectx().node()) fp = None try: try: fp = open(self.bu.backupfile('dirstate'), 'w') fp.write(dirstate + '\n') except EnvironmentError, e: raise util.Abort("couldn't save working copy parent: %s" % e) finally: if fp and not fp.closed: fp.close() try: try: fp = open(self.bu.backupfile('renames'), 'w') for cons in self._clobbering_renames(): fp.write("%s %s\n" % cons) except EnvironmentError, e: raise util.Abort("couldn't save clobbering copies: %s" % e) finally: if fp and not fp.closed: fp.close() try: try: fp = open(self.bu.backupfile('diff'), 'w') patch.diff(self.ws.repo, fp=fp, opts=patch.diffopts(self.ws.ui, opts={'git': True})) except EnvironmentError, e: raise util.Abort("couldn't save working copy diff: %s" % e) finally: if fp and not fp.closed: fp.close()
def diff(self, node, ref): maxdiff = int(self.ui.config('notify', 'maxdiff', 300)) prev = self.repo.changelog.parents(node)[0] self.ui.pushbuffer() patch.diff(self.repo, prev, ref, opts=patch.diffopts(self.ui)) difflines = self.ui.popbuffer().splitlines(1) if self.ui.configbool('notify', 'diffstat', True): s = patch.diffstat(difflines) # s may be nil, don't include the header if it is if s: self.ui.write('\ndiffstat:\n\n%s' % s) if maxdiff == 0: return if maxdiff > 0 and len(difflines) > maxdiff: self.ui.write(_('\ndiffs (truncated from %d to %d lines):\n\n') % (len(difflines), maxdiff)) difflines = difflines[:maxdiff] elif difflines: self.ui.write(_('\ndiffs (%d lines):\n\n') % len(difflines)) self.ui.write(*difflines)
def pdiff(self, pats, opts, parent=None): 'Return diffs relative to PARENT, as best as we can make out' parent = self.parent(parent) act = self.active(parent) # # act.localtip maybe nil, in the case of uncommitted local # changes. # if not act.revs: return names, match = cmdutil.matchpats(self.repo, pats, opts)[:2] opts = patch.diffopts(self.ui, opts) ret = cStringIO.StringIO() patch.diff(self.repo, act.parenttip.node(), act.localtip.node(), names, fp=ret, opts=opts, match=match) return ret.getvalue()
def edit(ui, repo, ctx, ha, opts): oldctx = repo[ha] hg.update(repo, ctx.node()) fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') fp = os.fdopen(fd, 'w') diffopts = patch.diffopts(ui, opts) diffopts.git = True gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts) for chunk in gen: fp.write(chunk) fp.close() try: files = {} try: patch.patch(patchfile, ui, cwd=repo.root, files=files, eolmode=None) finally: files = patch.updatedir(ui, repo, files) os.unlink(patchfile) except Exception, inst: pass
def get_diffs(self): from mercurial import mdiff, util, patch from repo_browser.integration import Diff ctx = self.ctx parent = ctx.parents()[0] parent_date = util.datestr(parent.date()) this_date = util.datestr(ctx.date()) diffopts = patch.diffopts(self.repo.repo.ui, untrusted=True) # Returns a tuple of modified, added, removed, deleted, unknown # TODO: look up in the api what FIXME* are modified, added, removed, deleted, unknown, FIXME, FIXME2 = \ self.repo.repo.status( parent.node(), ctx.node(),) for modified_file in modified: filectx = ctx.filectx(modified_file) parent_filectx = parent.filectx(modified_file) this_data = filectx.data() parent_data = parent_filectx.data() yield Diff(mdiff.unidiff(parent_data, parent_date, this_data,this_date, modified_file, modified_file, opts=diffopts)) for added_file in added: filectx = ctx.filectx(added_file) this_data = filectx.data() yield Diff(mdiff.unidiff( None, parent_date, this_data, this_date, added_file, added_file, opts=diffopts)) for removed_file in removed: parent_filectx = parent.filectx(removed_file) parent_data = parent_filectx.data() yield Diff(mdiff.unidiff( parent_data, parent_date, None, ctx.date(), removed_file, removed_file, opts=diffopts))
def diff(self, node, ref): maxdiff = int(self.ui.config('notify', 'maxdiff', 300)) prev = self.repo.changelog.parents(node)[0] self.ui.pushbuffer() patch.diff(self.repo, prev, ref, opts=patch.diffopts(self.ui)) difflines = self.ui.popbuffer().splitlines(1) if self.ui.configbool('notify', 'diffstat', True): s = patch.diffstat(difflines) # s may be nil, don't include the header if it is if s: self.ui.write('\ndiffstat:\n\n%s' % s) if maxdiff == 0: return if maxdiff > 0 and len(difflines) > maxdiff: self.ui.write( _('\ndiffs (truncated from %d to %d lines):\n\n') % (len(difflines), maxdiff)) difflines = difflines[:maxdiff] elif difflines: self.ui.write(_('\ndiffs (%d lines):\n\n') % len(difflines)) self.ui.write(*difflines)
def recordfunc(ui, repo, message, match, opts): """This is generic record driver. Its job is to interactively filter local changes, and accordingly prepare working directory into a state in which the job can be delegated to a non-interactive commit command such as 'commit' or 'qrefresh'. After the actual job is done by non-interactive command, the working directory is restored to its original state. In the end we'll record interesting changes, and everything else will be left in place, so the user can continue working. """ cmdutil.checkunfinished(repo, commit=True) merge = len(repo[None].parents()) > 1 if merge: raise util.Abort( _('cannot partially commit a merge ' '(use "hg commit" instead)')) changes = repo.status(match=match)[:3] diffopts = patch.diffopts( ui, opts=dict(git=True, nodates=True, ignorews=opts.get('ignore_all_space'), ignorewsamount=opts.get('ignore_space_change'), ignoreblanklines=opts.get('ignore_blank_lines'))) chunks = patch.diff(repo, changes=changes, opts=diffopts) fp = cStringIO.StringIO() fp.write(''.join(chunks)) fp.seek(0) # 1. filter patch, so we have intending-to apply subset of it try: chunks = filterpatch(ui, parsepatch(fp)) except patch.PatchError, err: raise util.Abort(_('error parsing patch: %s') % err)
def getChunksForFile(self, wfile): repo = self.repo ctx = self.ctx if isinstance(ctx, patchctx): if wfile in ctx._files: return ctx._files[wfile] else: return [] else: buf = cStringIO.StringIO() diffopts = patch.diffopts(repo.ui, {'git':True}) m = matchmod.exact(repo.root, repo.root, [wfile]) for p in patch.diff(repo, ctx.p1().node(), None, match=m, opts=diffopts): buf.write(p) buf.seek(0) chunks = record.parsepatch(buf) if chunks: header = chunks[0] return [header] + header.hunks else: return []
def edit(ui, repo, ctx, ha, opts): oldctx = repo[ha] hg.update(repo, ctx.node()) fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') fp = os.fdopen(fd, 'w') diffopts = patch.diffopts(ui, opts) diffopts.git = True diffopts.ignorews = False diffopts.ignorewsamount = False diffopts.ignoreblanklines = False gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts) for chunk in gen: fp.write(chunk) fp.close() try: files = set() try: applypatch(ui, repo, patchfile, files=files, eolmode=None) finally: os.unlink(patchfile) except Exception, inst: pass
def diff(self, ctx, ref=None): maxdiff = int(self.ui.config('notify', 'maxdiff', 300)) prev = ctx.p1().node() ref = ref and ref.node() or ctx.node() chunks = patch.diff(self.repo, prev, ref, opts=patch.diffopts(self.ui)) difflines = ''.join(chunks).splitlines() if self.ui.configbool('notify', 'diffstat', True): s = patch.diffstat(difflines) # s may be nil, don't include the header if it is if s: self.ui.write('\ndiffstat:\n\n%s' % s) if maxdiff == 0: return elif maxdiff > 0 and len(difflines) > maxdiff: msg = _('\ndiffs (truncated from %d to %d lines):\n\n') self.ui.write(msg % (len(difflines), maxdiff)) difflines = difflines[:maxdiff] elif difflines: self.ui.write(_('\ndiffs (%d lines):\n\n') % len(difflines)) self.ui.write("\n".join(difflines))
def getpatches(revs): for r in scmutil.revrange(repo, revs): output = cStringIO.StringIO() cmdutil.export(repo, [r], fp=output, opts=patch.diffopts(ui, opts)) yield output.getvalue().split('\n')
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 diffopts(self, opts={}): """proxies a call to patch.diffopts, providing the ui argument""" # could this be curried like opener is? return patch.diffopts(self.ui, opts)
def _readStatus(self, ctx, ctx2, wfile, status, changeselect, force): def getstatus(repo, n1, n2, wfile): m = match.exact(repo.root, repo.getcwd(), [wfile]) modified, added, removed = repo.status(n1, n2, match=m)[:3] if wfile in modified: return 'M' if wfile in added: return 'A' if wfile in removed: return 'R' if wfile in ctx: return 'C' return None isbfile = False repo = ctx._repo maxdiff = repo.maxdiff self.flabel = u'<b>%s</b>' % self.filePath() if ctx2: # If a revision to compare to was provided, we must put it in # the context of the subrepo as well if ctx2._repo.root != ctx._repo.root: wsub2, wfileinsub2, sctx2 = \ hglib.getDeepestSubrepoContainingFile(wfile, ctx2) if wsub2: ctx2 = sctx2 absfile = repo.wjoin(wfile) if (wfile in ctx and 'l' in ctx.flags(wfile)) or \ os.path.islink(absfile): if wfile in ctx: data = ctx[wfile].data() else: data = os.readlink(absfile) self.contents = data self.flabel += _(' <i>(is a symlink)</i>') return if ctx2 is None: ctx2 = ctx.p1() if status is None: status = getstatus(repo, ctx2.node(), ctx.node(), wfile) mde = _('File or diffs not displayed: ' 'File is larger than the specified max size.\n' 'maxdiff = %s KB') % (maxdiff // 1024) if status in ('R', '!'): if wfile in ctx.p1(): fctx = ctx.p1()[wfile] if fctx._filelog.rawsize(fctx.filerev()) > maxdiff: self.error = mde else: olddata = fctx.data() if '\0' in olddata: self.error = 'binary file' else: self.contents = olddata self.flabel += _(' <i>(was deleted)</i>') elif hasattr(ctx.p1(), 'hasStandin') and ctx.p1().hasStandin(wfile): self.error = 'binary file' self.flabel += _(' <i>(was deleted)</i>') else: self.flabel += _(' <i>(was added, now missing)</i>') return if status in ('I', '?'): assert ctx.rev() is None self.flabel += _(' <i>(is unversioned)</i>') if os.path.getsize(absfile) > maxdiff: self.error = mde return data = util.posixfile(absfile, 'r').read() if not force and '\0' in data: self.error = 'binary file' else: self.contents = data return if status in ('M', 'A', 'C'): if ctx.hasStandin(wfile): wfile = ctx.findStandin(wfile) isbfile = True try: fctx, newdata = self._checkMaxDiff(ctx, wfile, maxdiff, force) except _BadContent: if status == 'A': self._checkRenamed(repo, ctx, ctx2, wfile) raise self.contents = newdata if status == 'C': # no further comparison is necessary return for pctx in ctx.parents(): if 'x' in fctx.flags() and 'x' not in pctx.flags(wfile): self.elabel = _("exec mode has been " "<font color='red'>set</font>") elif 'x' not in fctx.flags() and 'x' in pctx.flags(wfile): self.elabel = _("exec mode has been " "<font color='red'>unset</font>") if status == 'A': oldname = self._checkRenamed(repo, ctx, ctx2, wfile) if not oldname: return olddata = ctx2[oldname].data() elif status == 'M': if wfile not in ctx2: # merge situation where file was added in other branch self.flabel += _(' <i>(was added)</i>') return oldname = wfile olddata = ctx2[wfile].data() else: return self.olddata = olddata if changeselect: diffopts = patch.diffopts(repo.ui, {}) diffopts.git = True m = match.exact(repo.root, repo.root, [wfile]) fp = cStringIO.StringIO() copy = {} if oldname != wfile: copy[wfile] = oldname patches = patch.diff(repo, ctx.node(), None, match=m, opts=diffopts, copy=copy) for c in patches: fp.write(c) fp.seek(0) # feed diffs through parsepatch() for more fine grained # chunk selection filediffs = patch.parsepatch(fp) if filediffs and filediffs[0].hunks: self.changes = filediffs[0] else: self.diff = '' return self.changes.excludecount = 0 values = [] lines = 0 for chunk in self.changes.hunks: buf = cStringIO.StringIO() chunk.write(buf) chunk.excluded = False val = buf.getvalue() values.append(val) chunk.lineno = lines chunk.linecount = len(val.splitlines()) lines += chunk.linecount self.diff = ''.join(values) else: diffopts = patch.diffopts(repo.ui, {}) diffopts.git = False newdate = util.datestr(ctx.date()) olddate = util.datestr(ctx2.date()) if isbfile: olddata += '\0' newdata += '\0' difftext = hglib.unidifftext(olddata, olddate, newdata, newdate, oldname, wfile, opts=diffopts) if difftext: self.diff = ('diff -r %s -r %s %s\n' % (ctx, ctx2, oldname) + difftext) else: self.diff = ''
def rdiff(ui, repo, url, lrev=None, rrev=None, *pats, **opts): def rui(): try: return hg.remoteui(repo, opts) except AttributeError: # pre 1.6 return cmdutil.remoteui(repo, opts) try: other = getpeer(rui(), {}, url) except AttributeError: # pre-1.3 other = hg.repository(ui, url) cmdutil.setremoteconfig(ui, opts) ui.status(_('comparing with %s\n') % url) if rrev: if capable(other, 'lookup'): rrev = other.lookup(rrev) else: error = _( "Other repository doesn't support revision lookup, so a rev cannot be specified." ) raise util.Abort(error) incoming = findincomingfn(repo)(other, heads=rrev and [rrev] or []) if not incoming: # remote is a subset of local if not rrev: if capable(other, 'lookup'): rrev = other.lookup('tip') else: raise util.Abort(_('cannot determine remote tip')) other = repo bundle = None try: if incoming: # create a bundle (uncompressed if other repo is not local) if not rrev: cg = other.changegroup(incoming, "incoming") else: if not capable(other, 'changegroupsubset'): raise util.Abort( _("Partial incoming cannot be done because other repository doesn't support changegroupsubset." )) cg = other.changegroupsubset(incoming, rrev and [rrev] or [], 'incoming') bundle = changegroup.writebundle(cg, '', 'HG10UN') other = hg.repository(ui, bundle) if lrev: lrev = repo.changectx(lrev).node() rrev = other.changectx(rrev or 'tip').node() if opts['reverse']: lrev, rrev = rrev, lrev if not lrev: # bundle dirstate removed prior to hg 1.1 lrev = repo.dirstate.parents()[0] try: try: # scmutil.match expects a context not a repo m = scmutil.match(repo[None], pats, opts) except (ImportError, AttributeError): m = cmdutil.match(repo, pats, opts) chunks = patch.diff(other, lrev, rrev, match=m, opts=patch.diffopts(ui, opts)) for chunk in chunks: ui.write(chunk) except AttributeError: # 1.0 compatibility fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) patch.diff(other, lrev, rrev, fns, match=matchfn, opts=patch.diffopts(ui, opts)) finally: if hasattr(other, 'close'): other.close() if bundle: os.unlink(bundle)