Beispiel #1
0
 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 = patch.parsepatch(buf)
         if chunks:
             header = chunks[0]
             return [header] + header.hunks
         else:
             return []
Beispiel #2
0
 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 = patch.parsepatch(buf)
         if chunks:
             header = chunks[0]
             return [header] + header.hunks
         else:
             return []
Beispiel #3
0
    def _files(self):
        if not hasattr(self, '_ph') or not self._ph.haspatch:
            return {}

        M, A, R = 0, 1, 2
        def get_path(a, b):
            type = (a == '/dev/null') and A or M
            type = (b == '/dev/null') and R or type
            rawpath = (b != '/dev/null') and b or a
            if not (rawpath.startswith('a/') or rawpath.startswith('b/')):
                return type, rawpath
            return type, rawpath.split('/', 1)[-1]

        files = {}
        pf = open(self._path, 'rb')
        try:
            # consume comments and headers
            for i in range(self._ph.diffstartline):
                pf.readline()
            for chunk in patch.parsepatch(pf):
                if not isinstance(chunk, patch.header):
                    continue
                top = patch.parsefilename(chunk.header[-2])
                bot = patch.parsefilename(chunk.header[-1])
                type, path = get_path(top, bot)
                if path not in chunk.files():
                    type, path = 0, chunk.files()[-1]
                if path not in files:
                    self._status[type].append(path)
                    files[path] = [chunk]
                    self._fileorder.append(path)
                files[path].extend(chunk.hunks)
        except (patch.PatchError, AttributeError), e:
            self._status[2].append(self._parseErrorFileName)
            files[self._parseErrorFileName] = []
            self._parseerror = e
            if 'THGDEBUG' in os.environ:
                print e
Beispiel #4
0
def absorb(ui, repo, stack=None, targetctx=None, pats=None, opts=None):
    """pick fixup chunks from targetctx, apply them to stack.

    if targetctx is None, the working copy context will be used.
    if stack is None, the current draft stack will be used.
    return fixupstate.
    """
    if stack is None:
        limit = ui.configint('absorb', 'maxstacksize', 50)
        stack = getdraftstack(repo['.'], limit)
        if limit and len(stack) >= limit:
            ui.warn(_('absorb: only the recent %d changesets will '
                      'be analysed\n')
                    % limit)
    if not stack:
        raise error.Abort(_('no changeset to change'))
    if targetctx is None: # default to working copy
        targetctx = repo[None]
    if pats is None:
        pats = ()
    if opts is None:
        opts = {}
    state = fixupstate(stack, ui=ui, opts=opts)
    matcher = scmutil.match(targetctx, pats, opts)
    if opts.get('interactive'):
        diff = patch.diff(repo, stack[-1].node(), targetctx.node(), matcher)
        origchunks = patch.parsepatch(diff)
        chunks = cmdutil.recordfilter(ui, origchunks)[0]
        targetctx = overlaydiffcontext(stack[-1], chunks)
    showchanges = (opts.get('print_changes') or opts.get('dry_run'))
    state.diffwith(targetctx, matcher, showchanges=showchanges)
    if not opts.get('dry_run'):
        state.apply()
        if state.commit():
            state.printchunkstats()
        elif not ui.quiet:
            ui.write(_('nothing applied\n'))
    return state
Beispiel #5
0
    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 = ''
Beispiel #6
0
class RejectsDialog(QDialog):
    def __init__(self, ui, path, parent=None):
        super(RejectsDialog, self).__init__(parent)
        self.setWindowTitle(
            _('Merge rejected patch chunks into %s') % hglib.tounicode(path))
        self.setWindowFlags(Qt.Window)
        self.path = path

        self.setLayout(QVBoxLayout())
        editor = qscilib.Scintilla()
        editor.setBraceMatching(qsci.SloppyBraceMatch)
        editor.setFolding(qsci.BoxedTreeFoldStyle)
        editor.installEventFilter(qscilib.KeyPressInterceptor(self))
        editor.setContextMenuPolicy(Qt.CustomContextMenu)
        editor.customContextMenuRequested.connect(self._onMenuRequested)
        self.baseLineColor = editor.markerDefine(qsci.Background, -1)
        editor.setMarkerBackgroundColor(QColor('lightblue'),
                                        self.baseLineColor)
        self.layout().addWidget(editor, 3)

        searchbar = qscilib.SearchToolBar(self)
        searchbar.searchRequested.connect(editor.find)
        searchbar.conditionChanged.connect(editor.highlightText)
        searchbar.hide()

        def showsearchbar():
            searchbar.show()
            searchbar.setFocus(Qt.OtherFocusReason)

        qtlib.newshortcutsforstdkey(QKeySequence.Find, self, showsearchbar)
        self.addActions(searchbar.editorActions())
        self.layout().addWidget(searchbar)

        hbox = QHBoxLayout()
        hbox.setContentsMargins(2, 2, 2, 2)
        self.layout().addLayout(hbox, 1)
        self.chunklist = QListWidget(self)
        self.updating = True
        self.chunklist.currentRowChanged.connect(self.showChunk)
        hbox.addWidget(self.chunklist, 1)

        bvbox = QVBoxLayout()
        bvbox.setContentsMargins(2, 2, 2, 2)
        self.resolved = tb = QToolButton()
        tb.setIcon(qtlib.geticon('thg-success'))
        tb.setToolTip(_('Mark this chunk as resolved, goto next unresolved'))
        tb.pressed.connect(self.resolveCurrentChunk)
        self.unresolved = tb = QToolButton()
        tb.setIcon(qtlib.geticon('thg-warning'))
        tb.setToolTip(_('Mark this chunk as unresolved'))
        tb.pressed.connect(self.unresolveCurrentChunk)
        bvbox.addStretch(1)
        bvbox.addWidget(self.resolved, 0)
        bvbox.addWidget(self.unresolved, 0)
        bvbox.addStretch(1)
        hbox.addLayout(bvbox, 0)

        self.editor = editor
        self.rejectbrowser = RejectBrowser(self)
        hbox.addWidget(self.rejectbrowser, 5)

        self.textencgroup = fileencoding.createActionGroup(self)
        self.textencgroup.triggered.connect(self._reloadFile)
        fileencoding.checkActionByName(self.textencgroup,
                                       fileencoding.contentencoding(ui))

        BB = QDialogButtonBox
        bb = QDialogButtonBox(BB.Save | BB.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.layout().addWidget(bb)
        self.saveButton = bb.button(BB.Save)

        s = QSettings()
        self.restoreGeometry(qtlib.readByteArray(s, 'rejects/geometry'))
        self.editor.loadSettings(s, 'rejects/editor')
        self.rejectbrowser.loadSettings(s, 'rejects/rejbrowse')

        if not qscilib.readFile(editor, hglib.tounicode(path),
                                self._textEncoding()):
            self.hide()
            QTimer.singleShot(0, self.reject)
            return
        earlybytes = hglib.fromunicode(editor.text(), 'replace')[:4096]
        lexer = lexers.getlexer(ui, path, earlybytes, self)
        editor.setLexer(lexer)
        if lexer is None:
            editor.setFont(qtlib.getfont('fontlog').font())
        editor.setMarginLineNumbers(1, True)
        editor.setMarginWidth(1, str(editor.lines()) + 'X')

        buf = cStringIO.StringIO()
        try:
            buf.write('diff -r aaaaaaaaaaaa -r bbbbbbbbbbb %s\n' % path)
            buf.write(open(path + '.rej', 'rb').read())
            buf.seek(0)
        except IOError, e:
            pass
        try:
            header = patch.parsepatch(buf)[0]
            self.chunks = header.hunks
        except (patch.PatchError, IndexError), e:
            self.chunks = []
Beispiel #7
0
def absorb(ui, repo, stack=None, targetctx=None, pats=None, opts=None):
    """pick fixup chunks from targetctx, apply them to stack.

    if targetctx is None, the working copy context will be used.
    if stack is None, the current draft stack will be used.
    return fixupstate.
    """
    if stack is None:
        limit = ui.configint(b'absorb', b'max-stack-size')
        headctx = repo[b'.']
        if len(headctx.parents()) > 1:
            raise error.Abort(_(b'cannot absorb into a merge'))
        stack = getdraftstack(headctx, limit)
        if limit and len(stack) >= limit:
            ui.warn(
                _(b'absorb: only the recent %d changesets will '
                  b'be analysed\n') % limit)
    if not stack:
        raise error.Abort(_(b'no mutable changeset to change'))
    if targetctx is None:  # default to working copy
        targetctx = repo[None]
    if pats is None:
        pats = ()
    if opts is None:
        opts = {}
    state = fixupstate(stack, ui=ui, opts=opts)
    matcher = scmutil.match(targetctx, pats, opts)
    if opts.get(b'interactive'):
        diff = patch.diff(repo, stack[-1].node(), targetctx.node(), matcher)
        origchunks = patch.parsepatch(diff)
        chunks = cmdutil.recordfilter(ui, origchunks, matcher)[0]
        targetctx = overlaydiffcontext(stack[-1], chunks)
    fm = None
    if opts.get(b'print_changes') or not opts.get(b'apply_changes'):
        fm = ui.formatter(b'absorb', opts)
    state.diffwith(targetctx, matcher, fm)
    if fm is not None:
        fm.startitem()
        fm.write(b"count", b"\n%d changesets affected\n",
                 len(state.ctxaffected))
        fm.data(linetype=b'summary')
        for ctx in reversed(stack):
            if ctx not in state.ctxaffected:
                continue
            fm.startitem()
            fm.context(ctx=ctx)
            fm.data(linetype=b'changeset')
            fm.write(b'node', b'%-7.7s ', ctx.hex(), label=b'absorb.node')
            descfirstline = ctx.description().splitlines()[0]
            fm.write(
                b'descfirstline',
                b'%s\n',
                descfirstline,
                label=b'absorb.description',
            )
        fm.end()
    if not opts.get(b'dry_run'):
        if (not opts.get(b'apply_changes') and state.ctxaffected
                and ui.promptchoice(b"apply changes (yn)? $$ &Yes $$ &No",
                                    default=1)):
            raise error.Abort(_(b'absorb cancelled\n'))

        state.apply()
        if state.commit():
            state.printchunkstats()
        elif not ui.quiet:
            ui.write(_(b'nothing applied\n'))
    return state
Beispiel #8
0
    def displayFile(self, filename, status, force=False):
        self._status = status
        self.clearDisplay()
        if filename == self._lastfile:
            reenable = [(c.fromline, len(c.before)) for c in self.curchunks[1:]\
                        if c.selected]
        else:
            reenable = []
        self._lastfile = filename
        self.clearChunks()

        fd = filedata.createFileData(self._ctx, None, filename, status)
        fd.load(force=force)
        fd.detectTextEncoding()

        if fd.elabel:
            self.extralabel.setText(fd.elabel)
            self.extralabel.show()
        else:
            self.extralabel.hide()
        self.filenamelabel.setText(fd.flabel)

        if not fd.isValid() or not fd.diff:
            if fd.error is None:
                self.sci.clear()
                return
            self.sci.setText(fd.error)
            forcedisplaymsg = filedata.forcedisplaymsg
            linkstart = fd.error.find(forcedisplaymsg)
            if linkstart >= 0:
                # add the link to force to view the data anyway
                self._setupForceViewIndicator()
                self.sci.fillIndicatorRange(0, linkstart, 0,
                                            linkstart + len(forcedisplaymsg),
                                            self._forceviewindicator)
            return
        elif type(self._ctx.rev()) is str:
            chunks = self._ctx._files[filename]
        else:
            header = patch.parsepatch(cStringIO.StringIO(fd.diff))[0]
            chunks = [header] + header.hunks

        utext = []
        for chunk in chunks[1:]:
            buf = cStringIO.StringIO()
            chunk.selected = False
            chunk.write(buf)
            chunk.lines = buf.getvalue().splitlines()
            utext.append(buf.getvalue().decode(fd.textEncoding(), 'replace'))
        self.sci.setText(u'\n'.join(utext))

        start = 0
        self.sci.markerDeleteAll(-1)
        for chunk in chunks[1:]:
            chunk.lrange = (start, start + len(chunk.lines))
            chunk.mline = start
            if start:
                self.sci.markerAdd(start - 1, self.divider)
            for i in xrange(0, len(chunk.lines)):
                if start + i == chunk.mline:
                    self.sci.markerAdd(chunk.mline, self.unselected)
                else:
                    self.sci.markerAdd(start + i, self.vertical)
            start += len(chunk.lines) + 1
        self.origcontents = fd.olddata
        self.countselected = 0
        self.curchunks = chunks
        for c in chunks[1:]:
            if (c.fromline, len(c.before)) in reenable:
                self.toggleChunk(c)
        self.updateSummary()
Beispiel #9
0
    def displayFile(self, filename, status, force=False):
        self._status = status
        self.clearDisplay()
        if filename == self._lastfile:
            reenable = [(c.fromline, len(c.before)) for c in self.curchunks[1:]\
                        if c.selected]
        else:
            reenable = []
        self._lastfile = filename
        self.clearChunks()

        fd = filedata.createFileData(self._ctx, None, filename, status)
        fd.load(force=force)
        fd.detectTextEncoding()

        if fd.elabel:
            self.extralabel.setText(fd.elabel)
            self.extralabel.show()
        else:
            self.extralabel.hide()
        self.filenamelabel.setText(fd.flabel)

        if not fd.isValid() or not fd.diff:
            if fd.error is None:
                self.sci.clear()
                return
            self.sci.setText(fd.error)
            forcedisplaymsg = filedata.forcedisplaymsg
            linkstart = fd.error.find(forcedisplaymsg)
            if linkstart >= 0:
                # add the link to force to view the data anyway
                self._setupForceViewIndicator()
                self.sci.fillIndicatorRange(
                    0, linkstart, 0, linkstart+len(forcedisplaymsg),
                    self._forceviewindicator)
            return
        elif type(self._ctx.rev()) is str:
            chunks = self._ctx._files[filename]
        else:
            header = patch.parsepatch(cStringIO.StringIO(fd.diff))[0]
            chunks = [header] + header.hunks

        utext = []
        for chunk in chunks[1:]:
            buf = cStringIO.StringIO()
            chunk.selected = False
            chunk.write(buf)
            chunk.lines = buf.getvalue().splitlines()
            utext.append(buf.getvalue().decode(fd.textEncoding(), 'replace'))
        self.sci.setText(u'\n'.join(utext))

        start = 0
        self.sci.markerDeleteAll(-1)
        for chunk in chunks[1:]:
            chunk.lrange = (start, start+len(chunk.lines))
            chunk.mline = start
            if start:
                self.sci.markerAdd(start-1, self.divider)
            for i in xrange(0,len(chunk.lines)):
                if start + i == chunk.mline:
                    self.sci.markerAdd(chunk.mline, self.unselected)
                else:
                    self.sci.markerAdd(start+i, self.vertical)
            start += len(chunk.lines) + 1
        self.origcontents = fd.olddata
        self.countselected = 0
        self.curchunks = chunks
        for c in chunks[1:]:
            if (c.fromline, len(c.before)) in reenable:
                self.toggleChunk(c)
        self.updateSummary()
Beispiel #10
0
    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 = ''