def _updateannotation(self, ctx, filename): if ctx.rev() is None: return wsub, filename, ctx = hglib.getDeepestSubrepoContainingFile(filename, ctx) if wsub is None: # The file was not found in the repo context or its subrepos # This may happen for files that have been removed return self.ctx = ctx self.annfile = filename self._thread.abort() self._thread.start(ctx[filename])
def _findsub(self, paths): """Find the nearest (sub-)repository for the given paths All paths should be in the same repository. Otherwise, unmatched paths are silently omitted. """ if not paths: return self.repo, [], self.ctx.rev() repopath, _relpath, ctx = hglib.getDeepestSubrepoContainingFile( paths[0], self.ctx) if not repopath: return self.repo, paths, self.ctx.rev() repo = thgrepo.repository(self.repo.ui, self.repo.wjoin(repopath)) pfx = repopath + '/' relpaths = [e[len(pfx):] for e in paths if e.startswith(pfx)] return repo, relpaths, ctx.rev()
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 readStatus(self, ctx, ctx2, wfile, status): 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 self.flabel += u'<b>%s</b>' % hglib.tounicode(wfile) if isinstance(ctx, patchctx.patchctx): self.diff = ctx.thgmqpatchdata(wfile) flags = ctx.flags(wfile) if flags == 'x': self.elabel = _( "exec mode has been <font color='red'>set</font>") elif flags == '-': self.elabel = _( "exec mode has been <font color='red'>unset</font>") elif flags == 'l': self.flabel += _(' <i>(is a symlink)</i>') return 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 status is None: status = getstatus(repo, ctx.p1().node(), ctx.node(), wfile) if ctx2 is None: ctx2 = ctx.p1() if status == 'S': try: from mercurial import subrepo, commands def genSubrepoRevChangedDescription(subrelpath, sfrom, sto, repo): """Generate a subrepository revision change description""" out = [] def getLog(_ui, srepo, opts): _ui.pushbuffer() try: commands.log(_ui, srepo, **opts) logOutput = _ui.popbuffer() except error.ParseError, e: # Some mercurial versions have a bug that results in # saving a subrepo node id in the .hgsubstate file # which ends with a "+" character. If that is the # case, add a warning to the output, but try to # get the revision information anyway logOutput = '' for n, rev in enumerate(opts['rev']): if rev.endswith('+'): logOutput += _( '[WARNING] Invalid subrepo ' 'revision ID:\n\t%s\n\n') % rev opts['rev'][n] = rev[:-1] commands.log(_ui, srepo, **opts) logOutput += _ui.popbuffer() return logOutput opts = {'date': None, 'user': None, 'rev': [sfrom]} subabspath = os.path.join(repo.root, subrelpath) missingsub = not os.path.isdir(subabspath) incompletesub = False sfromlog = '' def isinitialrevision(rev): return all([el == '0' for el in rev]) if isinitialrevision(sfrom): sfrom = '' if isinitialrevision(sto): sto = '' if not sfrom and not sto: sstatedesc = 'new' out.append( _('Subrepo created and set to initial revision.') + u'\n\n') return out, sstatedesc elif not sfrom: sstatedesc = 'new' out.append( _('Subrepo initialized to revision:') + u'\n\n') elif not sto: sstatedesc = 'removed' out.append( _('Subrepo removed from repository.') + u'\n\n') return out, sstatedesc elif sfrom == sto: sstatedesc = 'unchanged' out.append(_('Subrepo was not changed.') + u'\n\n') out.append(_('Subrepo state is:') + u'\n\n') if missingsub: out.append(_('changeset: %s') % sfrom + u'\n') else: out.append( hglib.tounicode(getLog(_ui, srepo, opts))) return out, sstatedesc else: sstatedesc = 'changed' out.append(_('Revision has changed to:') + u'\n\n') if missingsub: sfromlog = _('changeset: %s') % sfrom + u'\n\n' else: sfromlog = hglib.tounicode(getLog( _ui, srepo, opts)) if not sfromlog: incompletesub = True sfromlog = _('changeset: %s') % sfrom + u'\n\n' sfromlog = _('From:') + u'\n' + sfromlog if missingsub: stolog = _('changeset: %s') % sto + '\n\n' sfromlog += _('Subrepository not found in the working ' 'directory.') + '\n' sfromlog += _( 'Further subrepository revision ' 'information cannot be retrieved.') + '\n' elif incompletesub: stolog = _('changeset: %s') % sto + '\n\n' sfromlog += _('Subrepository is either damaged or ' 'missing some revisions') + '\n' sfromlog += _( 'Further subrepository revision ' 'information cannot be retrieved.') + '\n' sfromlog += _('You may need to open the missing ' 'subrepository and manually\n' 'pull the missing revisions from its ' 'source repository.') + '\n' else: opts['rev'] = [sto] stolog = getLog(_ui, srepo, opts) if not stolog: stolog = _('Initial revision') + u'\n' out.append(hglib.tounicode(stolog)) if sfromlog: out.append(hglib.tounicode(sfromlog)) return out, sstatedesc srev = ctx.substate.get(wfile, subrepo.nullstate)[1] srepo = None try: subabspath = os.path.join(ctx._repo.root, wfile) if not os.path.isdir(subabspath): sactual = '' else: sub = ctx.sub(wfile) if isinstance(sub, subrepo.hgsubrepo): srepo = sub._repo sactual = srepo['.'].hex() else: self.error = _( 'Not a Mercurial subrepo, not previewable') return except (util.Abort, KeyError), e: sactual = '' out = [] _ui = uimod.ui() if srepo is None or ctx.rev() is not None: data = [] else: _ui.pushbuffer() commands.status(_ui, srepo, modified=True, added=True, removed=True, deleted=True) data = _ui.popbuffer() if data: out.append( _('The subrepository is dirty.') + u' ' + _('File Status:') + u'\n') out.append(hglib.tounicode(data)) out.append(u'\n') sstatedesc = 'changed' if ctx.rev() is not None: sparent = ctx.p1().substate.get(wfile, subrepo.nullstate)[1] subrepochange, sstatedesc = \ genSubrepoRevChangedDescription(wfile, sparent, srev, ctx._repo) out += subrepochange else: sstatedesc = 'dirty' if srev != sactual: subrepochange, sstatedesc = \ genSubrepoRevChangedDescription(wfile, srev, sactual, ctx._repo) out += subrepochange if data: sstatedesc += ' and dirty' elif srev and not sactual: sstatedesc = 'removed' self.ucontents = u''.join(out).strip() lbl = { 'changed': _('(is a changed sub-repository)'), 'unchanged': _('(is an unchanged sub-repository)'), 'dirty': _('(is a dirty sub-repository)'), 'new': _('(is a new sub-repository)'), 'removed': _('(is a removed sub-repository)'), 'changed and dirty': _('(is a changed and dirty sub-repository)'), 'new and dirty': _('(is a new and dirty sub-repository)'), 'removed and dirty': _('(is a removed sub-repository)') }[sstatedesc] self.flabel += ' <i>' + lbl + '</i>' if sactual: lbl = _(' <a href="subrepo:%s">open...</a>') self.flabel += lbl % hglib.tounicode(srepo.root)
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>' % hglib.tounicode(wfile) if isinstance(ctx, patchctx.patchctx): self.diff = ctx.thgmqpatchdata(wfile) flags = ctx.flags(wfile) if flags == 'x': self.elabel = _("exec mode has been <font color='red'>set</font>") elif flags == '-': self.elabel = _("exec mode has been <font color='red'>unset</font>") elif flags == 'l': self.flabel += _(' <i>(is a symlink)</i>') # Do not show patches that are too big or may be binary if not force: p = _('Diff not displayed: ') data = self.diff size = len(data) if (size > maxdiff): self.error = p + _('File is larger than the specified max size.\n' 'maxdiff = %s KB') % (maxdiff // 1024) elif '\0' in data: self.error = p + _('File is binary') elif _exceedsMaxLineLength(data): # it's incredibly slow to render long line by QScintilla self.error = p + \ _('File may be binary (maximum line length exceeded)') if self.error: self.error += u'\n\n' + forcedisplaymsg return 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 status is None: status = getstatus(repo, ctx.p1().node(), ctx.node(), wfile) if ctx2 is None: ctx2 = ctx.p1() if status == 'S': try: from mercurial import subrepo, commands def genSubrepoRevChangedDescription(subrelpath, sfrom, sto, repo): """Generate a subrepository revision change description""" out = [] def getLog(_ui, srepo, opts): _ui.pushbuffer() try: commands.log(_ui, srepo, **opts) logOutput = _ui.popbuffer() except error.ParseError, e: # Some mercurial versions have a bug that results in # saving a subrepo node id in the .hgsubstate file # which ends with a "+" character. If that is the # case, add a warning to the output, but try to # get the revision information anyway logOutput = '' for n, rev in enumerate(opts['rev']): if rev.endswith('+'): logOutput += _('[WARNING] Invalid subrepo ' 'revision ID:\n\t%s\n\n') % rev opts['rev'][n] = rev[:-1] commands.log(_ui, srepo, **opts) logOutput += _ui.popbuffer() return logOutput opts = {'date':None, 'user':None, 'rev':[sfrom]} subabspath = os.path.join(repo.root, subrelpath) missingsub = not os.path.isdir(subabspath) incompletesub = False sfromlog = '' def isinitialrevision(rev): return all([el == '0' for el in rev]) if isinitialrevision(sfrom): sfrom = '' if isinitialrevision(sto): sto = '' if not sfrom and not sto: sstatedesc = 'new' out.append(_('Subrepo created and set to initial revision.') + u'\n\n') return out, sstatedesc elif not sfrom: sstatedesc = 'new' out.append(_('Subrepo initialized to revision:') + u'\n\n') elif not sto: sstatedesc = 'removed' out.append(_('Subrepo removed from repository.') + u'\n\n') return out, sstatedesc elif sfrom == sto: sstatedesc = 'unchanged' out.append(_('Subrepo was not changed.') + u'\n\n') out.append(_('Subrepo state is:') + u'\n\n') if missingsub: out.append(_('changeset: %s') % sfrom + u'\n') else: out.append(hglib.tounicode(getLog(_ui, srepo, opts))) return out, sstatedesc else: sstatedesc = 'changed' out.append(_('Revision has changed to:') + u'\n\n') if missingsub: sfromlog = _('changeset: %s') % sfrom + u'\n\n' else: sfromlog = hglib.tounicode(getLog(_ui, srepo, opts)) if not sfromlog: incompletesub = True sfromlog = _('changeset: %s') % sfrom + u'\n\n' sfromlog = _('From:') + u'\n' + sfromlog if missingsub: stolog = _('changeset: %s') % sto + '\n\n' sfromlog += _('Subrepository not found in the working ' 'directory.') + '\n' sfromlog += _('Further subrepository revision ' 'information cannot be retrieved.') + '\n' elif incompletesub: stolog = _('changeset: %s') % sto + '\n\n' sfromlog += _('Subrepository is either damaged or ' 'missing some revisions') + '\n' sfromlog += _('Further subrepository revision ' 'information cannot be retrieved.') + '\n' sfromlog += _('You may need to open the missing ' 'subrepository and manually\n' 'pull the missing revisions from its ' 'source repository.') + '\n' else: opts['rev'] = [sto] stolog = getLog(_ui, srepo, opts) if not stolog: stolog = _('Initial revision') + u'\n' out.append(hglib.tounicode(stolog)) if sfromlog: out.append(hglib.tounicode(sfromlog)) return out, sstatedesc srev = ctx.substate.get(wfile, subrepo.nullstate)[1] srepo = None try: subabspath = os.path.join(ctx._repo.root, wfile) if not os.path.isdir(subabspath): sactual = '' else: sub = ctx.sub(wfile) if isinstance(sub, subrepo.hgsubrepo): srepo = sub._repo sactual = srepo['.'].hex() else: self.error = _('Not a Mercurial subrepo, not previewable') return except (util.Abort, KeyError), e: self.error = (_('Error previewing subrepo: %s') % hglib.tounicode(str(e))) return out = [] _ui = uimod.ui() if srepo is None or ctx.rev() is not None: data = [] else: _ui.pushbuffer() commands.status(_ui, srepo, modified=True, added=True, removed=True, deleted=True) data = _ui.popbuffer() if data: out.append(_('The subrepository is dirty.') + u' ' + _('File Status:') + u'\n') out.append(hglib.tounicode(data)) out.append(u'\n') sstatedesc = 'changed' if ctx.rev() is not None: sparent = ctx.p1().substate.get(wfile, subrepo.nullstate)[1] subrepochange, sstatedesc = \ genSubrepoRevChangedDescription(wfile, sparent, srev, ctx._repo) out += subrepochange else: sstatedesc = 'dirty' if srev != sactual: subrepochange, sstatedesc = \ genSubrepoRevChangedDescription(wfile, srev, sactual, ctx._repo) out += subrepochange if data: sstatedesc += ' and dirty' elif srev and not sactual: sstatedesc = 'removed' self.ucontents = u''.join(out).strip() lbl = { 'changed': _('(is a changed sub-repository)'), 'unchanged': _('(is an unchanged sub-repository)'), 'dirty': _('(is a dirty sub-repository)'), 'new': _('(is a new sub-repository)'), 'removed': _('(is a removed sub-repository)'), 'changed and dirty': _('(is a changed and dirty sub-repository)'), 'new and dirty': _('(is a new and dirty sub-repository)'), 'removed and dirty': _('(is a removed sub-repository)') }[sstatedesc] self.flabel += ' <i>' + lbl + '</i>' if sactual: lbl = ' <a href="repo:%%s">%s</a>' % _('open...') self.flabel += lbl % hglib.tounicode(srepo.root)
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() for c in patch.diff(repo, ctx.node(), None, match=m, opts=diffopts): 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 = mdiff.unidiff(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 = ''