Beispiel #1
0
        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()
Beispiel #2
0
 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))
Beispiel #3
0
    def recordfunc(ui, repo, files, message, match, opts):
        """This is generic record driver.

        It's job is to interactively filter local changes, and accordingly
        prepare working dir into a state, where the job can be delegated to
        non-interactive commit command such as 'commit' or 'qrefresh'.

        After the actual job is done by non-interactive command, working dir
        state is restored to original.

        In the end we'll record intresting changes, and everything else will be
        left in place, so the user can continue his work.
        """
        if files:
            changes = None
        else:
            changes = repo.status(files=files, match=match)[:5]
            modified, added, removed = changes[:3]
            files = modified + added + removed
        diffopts = mdiff.diffopts(git=True, nodates=True)
        fp = cStringIO.StringIO()
        patch.diff(repo, repo.dirstate.parents()[0], files=files,
                   match=match, changes=changes, opts=diffopts, fp=fp)
        fp.seek(0)

        # 1. filter patch, so we have intending-to apply subset of it
        chunks = filterpatch(ui, parsepatch(fp))
        del fp

        contenders = {}
        for h in chunks:
            try: contenders.update(dict.fromkeys(h.files()))
            except AttributeError: pass

        newfiles = [f for f in files if f in contenders]

        if not newfiles:
            ui.status(_('no changes to record\n'))
            return 0

        if changes is None:
            changes = repo.status(files=newfiles, match=match)[:5]
        modified = dict.fromkeys(changes[0])

        # 2. backup changed files, so we can restore them in the end
        backups = {}
        backupdir = repo.join('record-backups')
        try:
            os.mkdir(backupdir)
        except OSError, err:
            if err.errno != errno.EEXIST:
                raise
Beispiel #4
0
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.changectx(node1).manifest()
        mmap2 = repo.changectx(node2).manifest()
        status = repo.status(node1, node2, files=files)[:5]
        modified, added, removed, deleted, unknown = status

        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, "")
            patch.diff(repo, node1, node2,
                       files=files,
                       opts=patch.diffopts(ui, {'git': True}))
        else:
            __difftree(repo, node1, node2, files=files)
        if not opts['stdin']:
            break
Beispiel #5
0
def get_lines_and_files(ui, repo, ctx1, ctx2, fns):
    # Returns a list of dicts:
    # [{'filename': <file>, 'added': nn, 'removed': nn},
    #  {....},
    # ]
    files = []
    currentfile = {}
    fmatch = scmutil.matchfiles(repo, fns)
    diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
    for l in diff.split('\n'):
        if l.startswith("diff -r"):
            # If we have anything in currentfile, append to list
            if currentfile:
                files.append(currentfile)
                currentfile = {}

            # This is the first line of a file set current file
            currentfile['filename'] = l.split(' ')[-1]
            currentfile['added'] = 0
            currentfile['removed'] = 0
            
        if l.startswith("+") and not l.startswith("+++ "):
            currentfile['added'] += 1
        elif l.startswith("-") and not l.startswith("--- "):
            currentfile['removed'] += 1
    # The last file won't have been added to files, so add it now
    files.append(currentfile)
    return files
Beispiel #6
0
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
Beispiel #7
0
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'))
Beispiel #8
0
 def createpatch(self, repo, name, msg, user, date, pats=[], opts={}):
     """creates a patch from the current state of the working copy"""
     fp = self.opener(name, 'w')
     ctx = repo[None]
     fp.write('# HG changeset patch\n')
     if user:
         fp.write('# User %s\n' % user)
     if date:
         fp.write('# Date %d %d\n' % date)
     parents = [p.node() for p in ctx.parents() if p]
     if parents and parents[0]:
         fp.write('# Parent  %s\n' % hex(parents[0]))
     if msg:
         if not isinstance(msg, str):
             msg = '\n'.join(msg)
         if msg and msg[-1] != '\n':
             msg += '\n'
         fp.write(msg)
     m = cmdutil.match(repo, pats, opts)
     chunks = patch.diff(repo, match = m, opts = self.diffopts(opts))
     for chunk in chunks:
         fp.write(chunk)
     fp.close()
     self.currentpatch=name
     self.persiststate()
Beispiel #9
0
def get_gitdiff(filenode_old, filenode_new):
    """Returns mercurial style git diff between given
    ``filenode_old`` and ``filenode_new``.
    """

    for filenode in (filenode_old, filenode_new):
        if not isinstance(filenode, FileNode):
            raise VCSError("Given object should be FileNode object, not %s"
                % filenode.__class__)

    repo = filenode_new.changeset.repository

    old_raw_id = getattr(filenode_old.changeset, 'raw_id', '0' * 40)
    new_raw_id = getattr(filenode_new.changeset, 'raw_id', '0' * 40)

    root = filenode_new.changeset.repository.path

    file_filter = match(root, '', [filenode_new.path])

    if isinstance(repo, MercurialRepository):

        vcs_gitdiff = patch.diff(repo._repo,
                          old_raw_id,
                          new_raw_id,
                          match=file_filter,
                          opts=diffopts(git=True))

    else:
        vcs_gitdiff = repo._get_diff(old_raw_id, new_raw_id, filenode_new.path)

    return vcs_gitdiff
Beispiel #10
0
 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()
Beispiel #11
0
def new_commit(orig_commit, ui, repo, *pats, **opts):
    if opts['message'] or opts['logfile']:
        # don't act if user already specified a message
        return orig_commit(ui, repo, *pats, **opts)

    # check if changelog changed
    logname = ui.config('changelog', 'filename', 'CHANGES')
    if pats:
        match = cmdutil.match(repo, pats, opts)
        if logname not in match:
            # changelog is not mentioned
            return orig_commit(ui, repo, *pats, **opts)
    logmatch = cmdutil.match(repo, [logname], {})
    logmatch.bad = lambda f, msg: None  # don't complain if file is missing

    # get diff of changelog
    log = []
    for chunk in patch.diff(repo, None, None, match=logmatch):
        for line in chunk.splitlines():
            # naive: all added lines are the changelog
            if line.startswith('+') and not line.startswith('+++'):
                log.append(line[1:].rstrip().expandtabs())
    log = normalize_log(log)

    # always let the user edit the message
    opts['force_editor'] = True
    opts['message'] = log
    return orig_commit(ui, repo, *pats, **opts)
Beispiel #12
0
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 apply_change(node, reverse, push_patch=True, name=None):
        p1, p2 = repo.changelog.parents(node)
        if p2 != nullid:
            raise util.Abort('cannot %s a merge changeset' % desc['action'])

        opts = mdiff.defaultopts
        opts.git = True
        rpatch = StringIO.StringIO()
        orig, mod = (node, p1) if reverse else (p1, node)
        for chunk in patch.diff(repo, node1=orig, node2=mod, opts=opts):
            rpatch.write(chunk)
        rpatch.seek(0)

        saved_stdin = None
        try:
            save_fin = ui.fin
            ui.fin = rpatch
        except:
            # Old versions of hg did not use the ui.fin mechanism
            saved_stdin = sys.stdin
            sys.stdin = rpatch

        handle_change(desc, node, qimport=(use_mq and new_opts.get('nopush')))

        if saved_stdin is None:
            ui.fin = save_fin
        else:
            sys.stdin = saved_stdin
Beispiel #14
0
    def diff(self, ctx, ref=None):

        maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
        prev = ctx.p1().node()
        if ref:
            ref = ref.node()
        else:
            ref = ctx.node()
        chunks = patch.diff(self.repo, prev, ref,
                            opts=patch.diffallopts(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))
Beispiel #15
0
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 apply_change(node, reverse, push_patch=True, name=None):
        p1, p2 = repo.changelog.parents(node)
        if p2 != nullid:
            raise util.Abort('cannot %s a merge changeset' % desc['action'])

        opts = mdiff.defaultopts
        opts.git = True
        rpatch = StringIO.StringIO()
        orig, mod = (node, p1) if reverse else (p1, node)
        for chunk in patch.diff(repo, node1=orig, node2=mod, opts=opts):
            rpatch.write(chunk)
        rpatch.seek(0)

        saved_stdin = None
        try:
            save_fin = ui.fin
            ui.fin = rpatch
        except:
            # Old versions of hg did not use the ui.fin mechanism
            saved_stdin = sys.stdin
            sys.stdin = rpatch

        if push_patch:
            commands.import_(ui, repo, '-',
                             force=True,
                             no_commit=True,
                             strip=1,
                             base='')
        else:
            mq.qimport(ui, repo, '-', name=name, rev=[], git=True)

        if saved_stdin is None:
            ui.fin = save_fin
        else:
            sys.stdin = saved_stdin
Beispiel #17
0
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))
Beispiel #18
0
    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)
Beispiel #19
0
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))
Beispiel #20
0
 def __iter__(self):
     for item in self.stream:
         ctx = item.ctx
         if len(ctx.parents()) < 2:
             p = ''.join(patch.diff(ctx._repo, ctx.p1().node(), ctx.node()))
             lines = p.split('\n')
             stats = sum(map(self.delta_function, patch.diffstatdata(lines)))
             yield item.child(x=ctx.date()[0], y=stats)
Beispiel #21
0
 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))
Beispiel #22
0
def _file_changes(hg_ui, local_repo_path, commit):
    "Return FileChange instances for a commit"
    repo = hg.repository(hg_ui, path=local_repo_path)
    node2 = repo.lookup(commit.hash)
    node1 = repo[node2].parents()[0].node()
    lines = util.iterlines(patch.diff(repo, node1, node2))
    for f in patch.diffstatdata(lines):
        yield FileChange(f[0], commit.file_changes[f[0]], f[1], f[2], f[3])
Beispiel #23
0
def changedlines(ui, repo, ctx1, ctx2, fns):
    lines = 0
    fmatch = cmdutil.matchfiles(repo, fns)
    diff = ''.join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
    for l in diff.split('\n'):
        if (l.startswith("+") and not l.startswith("+++ ") or
            l.startswith("-") and not l.startswith("--- ")):
            lines += 1
    return lines
Beispiel #24
0
    def recordfunc(ui, repo, message, match, opts):
        """This is generic record driver.

        Its job is to interactively filter local changes, and accordingly
        prepare working dir into a state, where the job can be delegated to
        non-interactive commit command such as 'commit' or 'qrefresh'.

        After the actual job is done by non-interactive command, working dir
        state is restored to original.

        In the end we'll record interesting changes, and everything else will be
        left in place, so the user can continue his work.
        """

        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 = mdiff.diffopts(git=True, nodates=True)
        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
        chunks = crpatch.filterpatch(opts,
                                     crpatch.parsepatch(changes, fp),
                                     chunk_selector.chunkselector, ui)
        del fp

        contenders = set()
        for h in chunks:
            try:
                contenders.update(set(h.files()))
            except AttributeError:
                pass

        changed = changes[0] + changes[1] + changes[2]
        newfiles = [f for f in changed if f in contenders]

        if not newfiles:
            ui.status(_('no changes to record\n'))
            return 0

        modified = set(changes[0])

        # 2. backup changed files, so we can restore them in the end
        backups = {}
        backupdir = repo.join('record-backups')
        try:
            os.mkdir(backupdir)
        except OSError, err:
            if err.errno != errno.EEXIST:
                raise
Beispiel #25
0
def changedlines(ui, repo, ctx1, ctx2, fns):
    added, removed = 0, 0
    fmatch = scmutil.matchfiles(repo, fns)
    diff = "".join(patch.diff(repo, ctx1.node(), ctx2.node(), fmatch))
    for l in diff.split("\n"):
        if l.startswith("+") and not l.startswith("+++ "):
            added += 1
        elif l.startswith("-") and not l.startswith("--- "):
            removed += 1
    return (added, removed)
Beispiel #26
0
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))
Beispiel #27
0
 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)
     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)
Beispiel #28
0
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 = scmutil.match(repo[node1], 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 = scmutil.match(repo[node1], files)
            diffopts = patch.difffeatureopts(ui)
            diffopts.git = True
            chunks = patch.diff(repo, node1, node2, match=m,
                                opts=diffopts)
            for chunk in chunks:
                ui.write(chunk)
        else:
            __difftree(repo, node1, node2, files=files)
        if not opts['stdin']:
            break
Beispiel #29
0
    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)
Beispiel #30
0
 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
Beispiel #31
0
    def recordfunc(ui, repo, message, match, opts):
        """This is generic record driver.

        Its job is to interactively filter local changes, and accordingly
        prepare working dir into a state, where the job can be delegated to
        non-interactive commit command such as 'commit' or 'qrefresh'.

        After the actual job is done by non-interactive command, working dir
        state is restored to original.

        In the end we'll record interesting changes, and everything else will be
        left in place, so the user can continue his work.
        """

        merge = len(repo[None].parents()) > 1
        if merge:
            raise util.Abort(
                _('cannot partially commit a merge '
                  '(use hg commit instead)'))

        # status gives back
        #   modified, added, removed, deleted, unknown, ignored, clean
        # we take only the first 3 of these
        changes = repo.status(match=match)[:3]
        modified, added, removed = changes
        try:
            # Mercurial >= 3.3 allow disabling format-changing diffopts
            diffopts = patch.difffeatureopts(ui,
                                             opts=opts,
                                             section='crecord',
                                             whitespace=True)
        except AttributeError:
            diffopts = patch.diffopts(ui, opts=opts, section='crecord')
        diffopts.nodates = True
        diffopts.git = True
        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
        chunks = crpatch.filterpatch(opts, crpatch.parsepatch(changes, fp),
                                     chunk_selector.chunkselector, ui)
        del fp

        contenders = set()
        for h in chunks:
            try:
                contenders.update(set(h.files()))
            except AttributeError:
                pass

        changed = changes[0] + changes[1] + changes[2]
        newfiles = [f for f in changed if f in contenders]

        if not newfiles:
            ui.status(_('no changes to record\n'))
            return 0

        # 2. backup changed files, so we can restore them in the end
        backups = {}
        newly_added_backups = {}
        backupdir = repo.join('record-backups')
        try:
            os.mkdir(backupdir)
        except OSError, err:
            if err.errno != errno.EEXIST:
                raise
Beispiel #32
0
 def haschanges (self, repo, pats=[], opts={}):
     """checks if repository has changes or not"""
     return list(patch.diff(repo, match = matchutil(repo, pats, opts), opts = self.diffopts(opts))) != []
Beispiel #33
0
    def shelvefunc(ui, repo, message, match, opts):
        files = []
        if match.files():
            changes = None
        else:
            changes = repo.status(match = match)[:3]
            modified, added, removed = changes
            files = modified + added + removed
            match = matchfilesutil(repo, files)
        diffopts = repo.attic.diffopts( {'git':True, 'nodates':True})
        chunks = patch.diff(repo, repo.dirstate.parents()[0], match = match,
                            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
        ac = record.parsepatch(fp)
        chunks = record.filterpatch(ui, ac)
        # and a not-intending-to apply subset of it
        rc = refilterpatch(ac, chunks)
        del fp

        contenders = {}
        for h in chunks:
            try: contenders.update(dict.fromkeys(h.files()))
            except AttributeError: pass

        newfiles = [f for f in files if f in contenders]

        if not newfiles:
            ui.status(_('no changes to shelve\n'))
            return 0

        modified = dict.fromkeys(changes[0])
        backups = {}
        backupdir = repo.join('shelve-backups')

        try:
            bkfiles = [f for f in newfiles if f in modified]
            backups = makebackup(ui, repo, backupdir, bkfiles)

            # patch to shelve
            sp = cStringIO.StringIO()
            for c in chunks:
                if c.filename() in backups:
                    c.write(sp)
            doshelve = sp.tell()
            sp.seek(0)

            # patch to apply to shelved files
            fp = cStringIO.StringIO()
            for c in rc:
                if c.filename() in backups:
                    c.write(fp)
            dopatch = fp.tell()
            fp.seek(0)

            try:
                # 3a. apply filtered patch to clean repo (clean)
                if backups:
                    hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)

                # 3b. apply filtered patch to clean repo (apply)
                if dopatch:
                    ui.debug(_('applying patch\n'))
                    ui.debug(fp.getvalue())
                    patch.internalpatch(fp, ui, 1, repo.root)
                del fp

                # 3c. apply filtered patch to clean repo (shelve)
                if doshelve:
                    ui.debug(_("saving patch to %s\n") % (name))
                    s = repo.attic
                    f = s.opener(name, 'w')
                    f.write(sp.getvalue())
                    del f
                    s.currentpatch = name
                    s.persiststate()
                del sp
            except:
                try:
                    for realname, tmpname in backups.iteritems():
                        ui.debug(_('restoring %r to %r\n') % (tmpname, realname))
                        util.copyfile(tmpname, repo.wjoin(realname))
                except OSError:
                    pass

            return 0
        finally:
            try:
                for realname, tmpname in backups.iteritems():
                    ui.debug(_('removing backup for %r : %r\n') % (realname, tmpname))
                    os.unlink(tmpname)
                os.rmdir(backupdir)
            except OSError:
                pass
Beispiel #34
0
 def diff(self):
     """Difference between working copy and repository."""
     opts = patch.mdiff.diffopts(nodates=True)
     diff = patch.diff(self.repository._repository, opts=opts)
     return "".join(diff)
Beispiel #35
0
def _processpushreview(repo, req, ldap_username):
    """Handle a request to turn changesets into review requests.

    ``ldap_username`` is the LDAP username to associate with the MozReview
    account whose credentials are passed as part of the request. We implicitly
    trust the passed LDAP username has been authenticated to belong to the
    MozReview account.
    """
    bzusername = req.get('bzusername')
    bzapikey = req.get('bzapikey')

    if not bzusername or not bzapikey:
        return errorresponse(
            'Bugzilla API keys not configured; see '
            'https://mozilla-version-control-tools.readthedocs.io/en/latest/mozreview/install.html#obtaining-accounts-credentials-and-privileges '
            'for instructions on how to configure your client')

    identifier = req['identifier']
    nodes = []
    precursors = {}
    for cset in req['changesets']:
        node = cset['node']
        nodes.append(node)
        if 'precursors' in cset:
            precursors[node] = cset['precursors']

    diffopts = mdiff.diffopts(context=8, showfunc=True, git=True)

    commits = {
        'individual': [],
        'squashed': {},
        'obsolescence': req.get('obsolescence', False),
    }

    # We do multiple passes over the changesets requested for review because
    # some operations could be slow or may involve queries to external
    # resources. We want to run the fast checks first so we don't waste
    # resources before finding the error. The drawback here is the client
    # will not see the full set of errors. We may revisit this decision
    # later.

    for node in nodes:
        ctx = repo[node]
        # Reviewing merge commits doesn't make much sense and only makes
        # situations more complicated. So disallow the practice.
        if len(ctx.parents()) > 1:
            msg = 'cannot review merge commits (%s)' % short(ctx.node())
            return errorresponse(msg)

    # Invalid or confidental bugs will raise errors in the Review Board
    # interface later. Fail fast to minimize wasted time and resources.
    try:
        reviewid = ReviewID(identifier)
    except error.Abort as e:
        return errorresponse(str(e))

    # We use xmlrpc here because the Bugsy REST client doesn't currently handle
    # errors in responses.

    # We don't use available Bugzilla credentials because that's the
    # easiest way to test for confidential bugs. If/when we support posting
    # reviews to confidential bugs, we'll need to change this.
    xmlrpc_url = repo.ui.config('bugzilla', 'url').rstrip('/') + '/xmlrpc.cgi'
    proxy = xmlrpclib.ServerProxy(xmlrpc_url)
    try:
        proxy.Bug.get({'ids': [reviewid.bug]})
    except xmlrpclib.Fault as f:
        if f.faultCode == 101:
            return errorresponse('bug %s does not exist; '
                                 'please change the review id (%s)' %
                                 (reviewid.bug, reviewid.full))
        elif f.faultCode == 102:
            return errorresponse(
                'bug %s could not be accessed '
                '(we do not currently allow posting of reviews to '
                'confidential bugs)' % reviewid.bug)

        return errorresponse('server error verifying bug %s exists; '
                             'please retry or report a bug' % reviewid.bug)

    # Find the first public node in the ancestry of this series. This is
    # used by MozReview to query the upstream repo for additional context.
    first_public_ancestor = None
    for node in repo[nodes[0]].ancestors():
        ctx = repo[node]
        if ctx.phase() == phases.public:
            first_public_ancestor = ctx.hex()
            break
    commits['squashed']['first_public_ancestor'] = first_public_ancestor

    # Note patch.diff() appears to accept anything that can be fed into
    # repo[]. However, it blindly does a hex() on the argument as opposed
    # to the changectx, so we need to pass in the binary node.
    base_ctx = repo[nodes[0]].p1()
    base_parent_node = base_ctx.node()
    for i, node in enumerate(nodes):
        ctx = repo[node]
        p1 = ctx.p1().node()

        diff = ''.join(
            patch.diff(repo, node1=p1, node2=ctx.node(), opts=diffopts)) + '\n'

        if i:
            base_commit_id = nodes[i - 1]
        else:
            base_commit_id = base_ctx.hex()

        summary = encoding.fromlocal(ctx.description().splitlines()[0])
        if req.get('deduce-reviewers', True):
            reviewers = list(commitparser.parse_rquestion_reviewers(summary))
            requal_reviewers = list(
                commitparser.parse_requal_reviewers(summary))
        else:
            reviewers = []
            requal_reviewers = []
        commits['individual'].append({
            'id':
            node,
            'author':
            encoding.fromlocal(ctx.user()),
            'precursors':
            precursors.get(node, []),
            'message':
            encoding.fromlocal(ctx.description()),
            # Diffs are arbitrary byte sequences. json.dump() will try to
            # interpret str as UTF-8, which could fail. Instead of trying
            # to coerce the str to a unicode or use ensure_ascii=False (which
            # is a giant pain), just base64 encode the diff in the JSON.
            'diff_b64':
            diff.encode('base64'),
            'bug':
            str(reviewid.bug),
            'base_commit_id':
            base_commit_id,
            'first_public_ancestor':
            first_public_ancestor,
            'reviewers':
            reviewers,
            'requal_reviewers':
            requal_reviewers
        })

    squashed_diff = b''.join(
        patch.diff(repo,
                   node1=base_parent_node,
                   node2=repo[nodes[-1]].node(),
                   opts=diffopts)) + '\n'

    commits['squashed']['diff_b64'] = squashed_diff.encode('base64')
    commits['squashed']['base_commit_id'] = base_ctx.hex()

    rburl = repo.ui.config('reviewboard', 'url', None).rstrip('/')
    repoid = repo.ui.configint('reviewboard', 'repoid', None)
    privileged_rb_username = repo.ui.config('reviewboard', 'username', None)
    privileged_rb_password = repo.ui.config('reviewboard', 'password', None)

    if ldap_username:
        associate_ldap_username(rburl,
                                ldap_username,
                                privileged_rb_username,
                                privileged_rb_password,
                                username=bzusername,
                                apikey=bzapikey)

    res = {
        'rburl': rburl,
        'reviewid': identifier,
        'reviewrequests': {},
        'display': [],
    }

    try:
        parentrid, commitmap, reviews, warnings = \
            post_reviews(rburl, repoid, identifier, commits,
                         privileged_rb_username, privileged_rb_password,
                         username=bzusername, apikey=bzapikey)

        res['display'].extend(warnings)
        res['parentrrid'] = parentrid
        res['reviewrequests'][parentrid] = {
            'status': reviews[parentrid]['status'],
            'public': reviews[parentrid]['public'],
        }

        for node, rid in commitmap.items():
            rd = reviews[rid]
            res['reviewrequests'][rid] = {
                'node': node,
                'status': rd['status'],
                'public': rd['public'],
            }

            if rd['reviewers']:
                res['reviewrequests'][rid]['reviewers'] = list(rd['reviewers'])

    except AuthorizationError as e:
        return errorresponse(str(e))
    except BadRequestError as e:
        return errorresponse(str(e))

    return res
def add_latest_rev_and_toolshed(repo, **kwargs):
    """
        Iterating over all, but the ignored mercurial files. If a file is called
        tool_dependencies.xml or repository_dependencies.xml we check if 
        'changeset_revision' and/or 'toolshed' is not set or empty and insert
        the latest revision of the corresponding repo (repo-name/owner/tooshed).
        The default tool_shed url is hardcoded and can be changed.
        This hook creates a backup of the original file, replaces revision number
        and toolshed and commit the adopted changes.
        To restore the backup files use the additional script (toolshed_pretxncommit_hook.py) 
        as pretxncommit-hook.

        Add the following to your .hgrc:

        [hooks]
        pre-commit = python:.hg/toolshed_pre-commit_hook.py:add_latest_rev_and_toolshed
    """
    toolshed_url = "http://testtoolshed.g2.bx.psu.edu/"

    # to force a commit the user can add a temporary file called: force_pre-commit_hook_temp_file
    # we will forget that file, because it should only force the execution of that function
    commands.forget(ui.ui(), repo, 'force_pre-commit_hook_temp_file')

    logging.info(
        'Emtering pre-commit Hook: Updating "toolshed" and/or "changeset_revision" attribute.'
    )
    filename_categories = repo.status(clean=True)
    filepaths = [item for sublist in filename_categories for item in sublist]

    backup_files = list()
    for filepath in filepaths:
        if os.path.split(filepath)[-1] in [
                'tool_dependencies.xml', 'repository_dependencies.xml'
        ]:
            tree = ET.parse(filepath, parser=CommentedTreeBuilder())
            root = tree.getroot()
            change = False
            for repo_dep in root.iter('repository'):
                if repo_dep.attrib.get('changeset_revision', '') == '':
                    logging.info(
                        'Change *changeset_revision* of [%s]\n in file: %s\n and repository: %s'
                        % ('%s :: %s' %
                           (repo_dep.attrib['owner'], repo_dep.attrib['name']),
                           filepath, repo.url()))
                    tip = get_latest_repo_rev(
                        '%srepos/%s/%s' %
                        (toolshed_url, repo_dep.attrib['owner'],
                         repo_dep.attrib['name']))
                    repo_dep.attrib.update({'changeset_revision': "%s" % tip})
                    change = True
                if repo_dep.attrib.get('toolshed', '') == '':
                    logging.info(
                        'Change *toolshed* of [%s]\n in file: %s\n and repository: %s'
                        % ('%s :: %s' %
                           (repo_dep.attrib['owner'], repo_dep.attrib['name']),
                           filepath, repo.url()))
                    repo_dep.attrib.update(
                        {'toolshed': "http://testtoolshed.g2.bx.psu.edu/"})
                    change = True
            if change:
                backup_filepath = '%s.pre-commit-backup' % filepath
                backup_files.append(backup_filepath)
                shutil.move(filepath, backup_filepath)
                tree.write(filepath, xml_declaration=True, encoding='utf-8')
                logging.info('Add %s to repository: %s' %
                             (filepath, repo.url()))
                commands.add(ui.ui(), repo, filepath)

    # check if there is anything to commit
    if not [diff for diff in patch.diff(repo)]:
        logging.info('Nothing to commit for repository: %s.' % repo.url())
        # if nothing to commit, restore the original files
        # these is necessary because I could not find a 'nothing to commit'-hook
        for backup_file in backup_files:
            if os.path.split(backup_file)[-1] in [
                    'tool_dependencies.xml.pre-commit-backup',
                    'repository_dependencies.xml.pre-commit-backup'
            ]:
                ori_filepath = backup_file.replace('.pre-commit-backup', '')
                if os.path.split(ori_filepath)[-1] in [
                        'tool_dependencies.xml', 'repository_dependencies.xml'
                ]:
                    os.remove(ori_filepath)
                    shutil.move(backup_file, ori_filepath)
        # abort the commit, because nothing is to commit
        sys.exit(1)
Beispiel #37
0
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 = hg.repository(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 'lookup' in other.capabilities:
            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 'lookup' in other.capabilities:
                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 'changegroupsubset' not in other.capabilities:
                    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:
            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)
Beispiel #38
0
    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.
        """

        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 = mdiff.diffopts(
            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
        chunks = filterpatch(ui, parsepatch(fp))
        del fp

        contenders = set()
        for h in chunks:
            try:
                contenders.update(set(h.files()))
            except AttributeError:
                pass

        changed = changes[0] + changes[1] + changes[2]
        newfiles = [f for f in changed if f in contenders]
        if not newfiles:
            ui.status(_('no changes to record\n'))
            return 0

        modified = set(changes[0])

        # 2. backup changed files, so we can restore them in the end
        if backupall:
            tobackup = changed
        else:
            tobackup = [f for f in newfiles if f in modified]

        backups = {}
        if tobackup:
            backupdir = repo.join('record-backups')
            try:
                os.mkdir(backupdir)
            except OSError, err:
                if err.errno != errno.EEXIST:
                    raise
Beispiel #39
0
    def apply(self, repo, source, revmap, merges, opts={}):
        '''apply the revisions in revmap one by one in revision order'''
        revs = sorted(revmap)
        p1, p2 = repo.dirstate.parents()
        pulls = []
        diffopts = patch.diffopts(self.ui, opts)
        diffopts.git = True

        lock = wlock = 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')
                    gen = patch.diff(source, parents[0], node, opts=diffopts)
                    for chunk in gen:
                        fp.write(chunk)
                    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()
            lock.release()
            wlock.release()
Beispiel #40
0
def getdiff(ui, repo):
    m = cmdutil.match(repo)
    o = patch.diffopts(ui, {'git': True})
    return patch.diff(repo, match=m, opts=o)
Beispiel #41
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', 'max-stack-size')
        headctx = repo['.']
        if len(headctx.parents()) > 1:
            raise error.Abort(_('cannot absorb into a merge'))
        stack = getdraftstack(headctx, 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 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('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('print_changes') or not opts.get('apply_changes'):
        fm = ui.formatter('absorb', opts)
    state.diffwith(targetctx, matcher, fm)
    if fm is not None:
        fm.startitem()
        fm.write("count", "\n%d changesets affected\n", len(state.ctxaffected))
        fm.data(linetype='summary')
        for ctx in reversed(stack):
            if ctx not in state.ctxaffected:
                continue
            fm.startitem()
            fm.context(ctx=ctx)
            fm.data(linetype='changeset')
            fm.write('node', '%-7.7s ', ctx.hex(), label='absorb.node')
            descfirstline = ctx.description().splitlines()[0]
            fm.write('descfirstline',
                     '%s\n',
                     descfirstline,
                     label='absorb.description')
        fm.end()
    if not opts.get('dry_run'):
        if (not opts.get('apply_changes') and state.ctxaffected
                and ui.promptchoice("apply changes (yn)? $$ &Yes $$ &No",
                                    default=1)):
            raise error.Abort(_('absorb cancelled\n'))

        state.apply()
        if state.commit():
            state.printchunkstats()
        elif not ui.quiet:
            ui.write(_('nothing applied\n'))
    return state
Beispiel #42
0
    def shelvefunc(ui, repo, message, match, opts):
        parents = repo.dirstate.parents()
        changes = repo.status(match=match)[:5]
        modified, added, removed = changes[:3]
        files = modified + added + removed
        diffopts = mdiff.diffopts(git=True, nodates=True)
        patch_diff = ''.join(
            patch.diff(repo,
                       parents[0],
                       match=match,
                       changes=changes,
                       opts=diffopts))

        fp = cStringIO.StringIO(patch_diff)
        ac = parsepatch(fp)
        fp.close()

        chunks = filterpatch(ui, ac, not opts['all'])
        rc = refilterpatch(ac, chunks)

        # set of files to be processed
        contenders = {}
        for h in chunks:
            try:
                contenders.update(dict.fromkeys(h.files()))
            except AttributeError:
                pass

        # exclude sources of copies that are otherwise untouched
        newfiles = set(f for f in files if f in contenders)

        if not newfiles:
            ui.status(_('no changes to shelve\n'))
            return 0

        backupdir = repo.join('shelve-backups')

        try:
            backups = makebackup(ui, repo, backupdir, newfiles)

            # patch to shelve
            sp = cStringIO.StringIO()
            for c in chunks:
                c.write(sp)

            # patch to apply to shelved files
            fp = cStringIO.StringIO()
            for c in rc:
                # skip files not selected for shelving
                if c.filename() in newfiles:
                    c.write(fp)
            dopatch = fp.tell()
            fp.seek(0)

            try:
                # 3a. apply filtered patch to clean repo (clean)
                opts['no_backup'] = True
                cmdutil.revert(ui, repo, repo['.'], parents,
                               *[os.path.join(repo.root, f) for f in newfiles],
                               **opts)
                for f in added:
                    if f in newfiles:
                        util.unlinkpath(repo.wjoin(f))

                # 3b. apply filtered patch to clean repo (apply)
                if dopatch:
                    ui.debug('applying patch\n')
                    ui.debug(fp.getvalue())
                    patch.internalpatch(ui, repo, fp, 1)
                del fp

                # 3c. apply filtered patch to clean repo (shelve)
                ui.debug("saving patch to shelve\n")
                if opts['append']:
                    sp.write(repo.opener(shelfpath).read())
                sp.seek(0)
                f = repo.opener(shelfpath, "w")
                f.write(sp.getvalue())
                del f, sp
            except:
                ui.warn("shelving failed: %s\n" % sys.exc_info()[1])
                try:
                    # re-schedule remove
                    matchremoved = scmutil.matchfiles(repo, removed)
                    cmdutil.forget(ui, repo, matchremoved, "", True)
                    for f in removed:
                        if f in newfiles and os.path.isfile(f):
                            os.unlink(f)
                    # copy back backups
                    for realname, tmpname in backups.iteritems():
                        ui.debug('restoring %r to %r\n' % (tmpname, realname))
                        util.copyfile(tmpname, repo.wjoin(realname))
                    # re-schedule add
                    matchadded = scmutil.matchfiles(repo, added)
                    cmdutil.add(ui, repo, matchadded, False, False, "", True)

                    ui.debug('removing shelve file\n')
                    if os.path.isfile(repo.wjoin(shelfpath)):
                        os.unlink(repo.join(shelfpath))
                except OSError, err:
                    ui.warn("restoring backup failed: %s\n" % err)

            return 0
Beispiel #43
0
    def shelvefunc(ui, repo, message, match, opts):
        changes = repo.status(match=match)[:5]
        modified, added, removed = changes[:3]
        files = modified + added + removed
        diffopts = mdiff.diffopts(git=True, nodates=True)
        patch_diff = ''.join(
            patch.diff(repo,
                       repo.dirstate.parents()[0],
                       match=match,
                       changes=changes,
                       opts=diffopts))

        fp = cStringIO.StringIO(patch_diff)
        ac = parsepatch(fp)
        fp.close()

        chunks = filterpatch(ui, ac, not opts['all'])
        rc = refilterpatch(ac, chunks)

        contenders = {}
        for h in chunks:
            try:
                contenders.update(dict.fromkeys(h.files()))
            except AttributeError:
                pass

        newfiles = [f for f in files if f in contenders]

        if not newfiles:
            ui.status(_('no changes to shelve\n'))
            return 0

        modified = dict.fromkeys(changes[0])

        backupdir = repo.join('shelve-backups')

        try:
            bkfiles = [f for f in newfiles if f in modified]
            backups = makebackup(ui, repo, backupdir, bkfiles)

            # patch to shelve
            sp = cStringIO.StringIO()
            for c in chunks:
                if c.filename() in backups:
                    c.write(sp)
            doshelve = sp.tell()
            sp.seek(0)

            # patch to apply to shelved files
            fp = cStringIO.StringIO()
            for c in rc:
                if c.filename() in backups:
                    c.write(fp)
            dopatch = fp.tell()
            fp.seek(0)

            try:
                # 3a. apply filtered patch to clean repo (clean)
                if backups:
                    hg.revert(repo,
                              repo.dirstate.parents()[0], backups.has_key)

                # 3b. apply filtered patch to clean repo (apply)
                if dopatch:
                    ui.debug('applying patch\n')
                    ui.debug(fp.getvalue())
                    patch.internalpatch(fp, ui, 1, repo.root)
                del fp

                # 3c. apply filtered patch to clean repo (shelve)
                if doshelve:
                    ui.debug("saving patch to shelve\n")
                    if opts['append']:
                        f = repo.opener(shelfpath, "a")
                    else:
                        f = repo.opener(shelfpath, "w")
                    f.write(sp.getvalue())
                    del f
                del sp
            except:
                try:
                    for realname, tmpname in backups.iteritems():
                        ui.debug('restoring %r to %r\n' % (tmpname, realname))
                        util.copyfile(tmpname, repo.wjoin(realname))
                    ui.debug('removing shelve file\n')
                    os.unlink(repo.join(shelfpath))
                except OSError:
                    pass

            return 0
        finally:
            try:
                for realname, tmpname in backups.iteritems():
                    ui.debug('removing backup for %r : %r\n' %
                             (realname, tmpname))
                    os.unlink(tmpname)
                os.rmdir(backupdir)
            except OSError:
                pass
Beispiel #44
0
    def apply(self, repo, source, revmap, merges, opts=None):
        '''apply the revisions in revmap one by one in revision order'''
        if opts is None:
            opts = {}
        revs = sorted(revmap)
        p1, p2 = repo.dirstate.parents()
        pulls = []
        diffopts = patch.difffeatureopts(self.ui, opts)
        diffopts.git = True

        lock = wlock = tr = None
        try:
            wlock = repo.wlock()
            lock = repo.lock()
            tr = repo.transaction('transplant')
            for rev in revs:
                node = revmap[rev]
                revstr = '%s:%s' % (rev, short(node))

                if self.applied(repo, node, p1):
                    self.ui.warn(
                        _('skipping already applied revision %s\n') % revstr)
                    continue

                parents = source.changelog.parents(node)
                if not (opts.get('filter') or opts.get('log')):
                    # If the changeset parent is the same as the
                    # wdir's parent, just pull it.
                    if parents[0] == p1:
                        pulls.append(node)
                        p1 = node
                        continue
                    if pulls:
                        if source != repo:
                            exchange.pull(repo, source.peer(), heads=pulls)
                        merge.update(repo, pulls[-1], False, False, None)
                        p1, p2 = repo.dirstate.parents()
                        pulls = []

                domerge = False
                if node in merges:
                    # pulling all the merge revs at once would mean we
                    # couldn't transplant after the latest even if
                    # transplants before them fail.
                    domerge = True
                    if not hasnode(repo, node):
                        exchange.pull(repo, source.peer(), heads=[node])

                skipmerge = False
                if parents[1] != revlog.nullid:
                    if not opts.get('parent'):
                        self.ui.note(
                            _('skipping merge changeset %s:%s\n') %
                            (rev, short(node)))
                        skipmerge = True
                    else:
                        parent = source.lookup(opts['parent'])
                        if parent not in parents:
                            raise error.Abort(
                                _('%s is not a parent of %s') %
                                (short(parent), short(node)))
                else:
                    parent = parents[0]

                if skipmerge:
                    patchfile = None
                else:
                    fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
                    fp = os.fdopen(fd, 'w')
                    gen = patch.diff(source, parent, node, opts=diffopts)
                    for chunk in gen:
                        fp.write(chunk)
                    fp.close()

                del revmap[rev]
                if patchfile or domerge:
                    try:
                        try:
                            n = self.applyone(repo,
                                              node,
                                              source.changelog.read(node),
                                              patchfile,
                                              merge=domerge,
                                              log=opts.get('log'),
                                              filter=opts.get('filter'))
                        except TransplantError:
                            # Do not rollback, it is up to the user to
                            # fix the merge or cancel everything
                            tr.close()
                            raise
                        if n and domerge:
                            self.ui.status(
                                _('%s merged at %s\n') % (revstr, short(n)))
                        elif n:
                            self.ui.status(
                                _('%s transplanted to %s\n') %
                                (short(node), short(n)))
                    finally:
                        if patchfile:
                            os.unlink(patchfile)
            tr.close()
            if pulls:
                exchange.pull(repo, source.peer(), heads=pulls)
                merge.update(repo, pulls[-1], False, False, None)
        finally:
            self.saveseries(revmap, merges)
            self.transplants.write()
            if tr:
                tr.release()
            if lock:
                lock.release()
            wlock.release()