Esempio n. 1
0
def diffpatch(ui, repo, node1, node2, tmproot, matcher, cmdline):
    template = b'hg-%h.patch'
    # write patches to temporary files
    with formatter.nullformatter(ui, b'extdiff', {}) as fm:
        cmdutil.export(
            repo,
            [repo[node1].rev(), repo[node2].rev()],
            fm,
            fntemplate=repo.vfs.reljoin(tmproot, template),
            match=matcher,
        )
    label1 = cmdutil.makefilename(repo[node1], template)
    label2 = cmdutil.makefilename(repo[node2], template)
    file1 = repo.vfs.reljoin(tmproot, label1)
    file2 = repo.vfs.reljoin(tmproot, label2)
    cmdline = formatcmdline(
        cmdline,
        repo.root,
        # no 3way while comparing patches
        do3way=False,
        parent1=file1,
        plabel1=label1,
        # while comparing patches, there is no second parent
        parent2=None,
        plabel2=None,
        child=file2,
        clabel=label2,
    )
    ui.debug(b'running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot))
    ui.system(cmdline, cwd=tmproot, blockedtag=b'extdiff')
    return 1
Esempio n. 2
0
def qshow(ui, repo, patchspec=None, **opts):
    '''display a patch

    If no patch is given, the top of the applied stack is shown.'''
    q = repo.mq

    patchf = None
    if patchspec is None:
        p = q.lookup("qtip")
        patchf = q.opener(p, "r")
    else:
        try:
            p = q.lookup(patchspec)
            patchf = q.opener(p, "r")
        except util.Abort, e:
            try:
                patchf = file(patchspec, "r")
            except Exception, e:
                # commands.diff has a bad error message
                if patchspec not in repo:
                    raise util.Abort(_("Unknown patch '%s'") % patchspec)

                # the built-in export command does not label the diff for color
                # output, and the patch header generation is not reusable
                # independently
                def empty_diff(*args, **kwargs):
                    return []
                temp = patch.diff
                try:
                    patch.diff = empty_diff
                    cmdutil.export(repo, [ patchspec ], fp=ui)
                finally:
                    patch.diff = temp

                return commands.diff(ui, repo, change=patchspec, date=None)
Esempio n. 3
0
 def getpatches(revs):
     prev = repo['.'].rev()
     for r in scmutil.revrange(repo, revs):
         if r == prev and (repo[None].files() or repo[None].deleted()):
             ui.warn(
                 _('warning: working directory has '
                   'uncommitted changes\n'))
         output = cStringIO.StringIO()
         cmdutil.export(repo, [r], fp=output, opts=patch.diffopts(ui, opts))
         yield output.getvalue().split('\n')
Esempio n. 4
0
 def getpatches(revs):
     prev = repo['.'].rev()
     for r in scmutil.revrange(repo, revs):
         if r == prev and (repo[None].files() or repo[None].deleted()):
             ui.warn(_('warning: working directory has '
                       'uncommitted changes\n'))
         output = cStringIO.StringIO()
         cmdutil.export(repo, [r], fp=output,
                      opts=patch.diffopts(ui, opts))
         yield output.getvalue().split('\n')
Esempio n. 5
0
def _shelvecreatedcommit(ui, repo, node, name, tr):
    shelvedfile(repo, name, 'oshelve').writeobsshelveinfo({
        'node': nodemod.hex(node)
    })
    if util.safehasattr(cmdutil, 'exportfile'):
        # Mercurial 4.6 and later
        cmdutil.exportfile(repo.unfiltered(), [node],
                       shelvedfile(repo, name, patchextension).opener('wb'),
                       opts=mdiff.diffopts(git=True))
    else:
        # Mercurial 4.5 and earlier
        cmdutil.export(repo.unfiltered(), [node],
                       fp=shelvedfile(repo, name, patchextension).opener('wb'),
                       opts=mdiff.diffopts(git=True))
Esempio n. 6
0
    def diffstat(self):
        class patchbuf(object):
            def __init__(self):
                self.lines = []
                # diffstat is stupid
                self.name = 'cia'
            def write(self, data):
                self.lines.append(data)
            def close(self):
                pass

        n = self.ctx.node()
        pbuf = patchbuf()
        cmdutil.export(self.cia.repo, [n], fp=pbuf)
        return patch.diffstat(pbuf.lines) or ''
Esempio n. 7
0
def _getpatches(repo, revs, **opts):
    """return a list of patches for a list of revisions

    Each patch in the list is itself a list of lines.
    """
    ui = repo.ui
    prev = repo['.'].rev()
    for r in revs:
        if r == prev and (repo[None].files() or repo[None].deleted()):
            ui.warn(_('warning: working directory has '
                      'uncommitted changes\n'))
        output = cStringIO.StringIO()
        cmdutil.export(repo, [r], fp=output,
                     opts=patch.difffeatureopts(ui, opts, git=True))
        yield output.getvalue().split('\n')
Esempio n. 8
0
    def diffstat(self):
        class patchbuf(object):
            def __init__(self):
                self.lines = []
                # diffstat is stupid
                self.name = 'cia'
            def write(self, data):
                self.lines.append(data)
            def close(self):
                pass

        n = self.ctx.node()
        pbuf = patchbuf()
        cmdutil.export(self.cia.repo, [n], fp=pbuf)
        return patch.diffstat(pbuf.lines) or ''
Esempio n. 9
0
def _getpatches(repo, revs, **opts):
    """return a list of patches for a list of revisions

    Each patch in the list is itself a list of lines.
    """
    ui = repo.ui
    prev = repo['.'].rev()
    for r in scmutil.revrange(repo, revs):
        if r == prev and (repo[None].files() or repo[None].deleted()):
            ui.warn(_('warning: working directory has '
                      'uncommitted changes\n'))
        output = cStringIO.StringIO()
        cmdutil.export(repo, [r], fp=output,
                     opts=patch.difffeatureopts(ui, opts, git=True))
        yield output.getvalue().split('\n')
Esempio n. 10
0
    def diffstat(self):
        class patchbuf(object):
            def __init__(self):
                self.lines = []
                # diffstat is stupid
                self.name = "cia"

            def write(self, data):
                self.lines += data.splitlines(True)

            def close(self):
                pass

        n = self.ctx.node()
        pbuf = patchbuf()
        cmdutil.export(self.cia.repo, [n], fp=pbuf)
        return patch.diffstat(pbuf.lines) or ""
Esempio n. 11
0
def qshow(ui, repo, patchspec=None, **opts):
    '''display a patch

    If no patch is given, the top of the applied stack is shown.'''

    patchf = resolve_patchfile(ui, repo, patchspec)

    if patchf is None:
        # commands.diff has a bad error message
        if patchspec is None:
            patchspec = '.'
        if patchspec not in repo and not repo.revs(patchspec).first():
            raise util.Abort(_("Unknown patch '%s'") % patchspec)

        # the built-in export command does not label the diff for color
        # output, and the patch header generation is not reusable
        # independently
        def empty_diff(*args, **kwargs):
            return []

        temp = patch.diff
        try:
            patch.diff = empty_diff
            cmdutil.export(repo, repo.revs(patchspec), fp=ui)
        finally:
            patch.diff = temp

        return commands.diff(ui, repo, change=patchspec, date=None, **opts)

    if opts['stat']:
        del opts['stat']
        lines = patch.diffstatui(patchf, **opts)
    else:

        def singlefile(*a, **b):
            return patchf

        lines = patch.difflabel(singlefile, **opts)

    for chunk, label in lines:
        ui.write(chunk, label=label)

    patchf.close()
Esempio n. 12
0
def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
           opts=None):
    '''
    export changesets as hg patches.

    Mercurial moved patch.export to cmdutil.export after version 1.5
    (change e764f24a45ee in mercurial).
    '''

    try:
        return cmdutil.export(repo, revs, template, fp, switch_parent, opts)
    except AttributeError:
        from mercurial import patch
        return patch.export(repo, revs, template, fp, switch_parent, opts)
Esempio n. 13
0
def qshow(ui, repo, patchspec=None, **opts):
    '''display a patch

    If no patch is given, the top of the applied stack is shown.'''
    q = repo.mq

    patchf = None
    if patchspec is None:
        p = q.lookup("qtip")
        patchf = q.opener(p, "r")
    else:
        try:
            p = q.lookup(patchspec)
            patchf = q.opener(p, "r")
        except util.Abort, e:
            try:
                patchf = file(patchspec, "r")
            except Exception, e:
                # commands.diff has a bad error message
                if patchspec not in repo:
                    raise util.Abort(_("Unknown patch '%s'") % patchspec)

                # the built-in export command does not label the diff for color
                # output, and the patch header generation is not reusable
                # independently
                def empty_diff(*args, **kwargs):
                    return []

                temp = patch.diff
                try:
                    patch.diff = empty_diff
                    cmdutil.export(repo, [patchspec], fp=ui)
                finally:
                    patch.diff = temp

                return commands.diff(ui, repo, change=patchspec, date=None)
Esempio n. 14
0
def export(repo,
           revs,
           template='hg-%h.patch',
           fp=None,
           switch_parent=False,
           opts=None):
    '''
    export changesets as hg patches.

    Mercurial moved patch.export to cmdutil.export after version 1.5
    (change e764f24a45ee in mercurial).
    '''

    try:
        return cmdutil.export(repo, revs, template, fp, switch_parent, opts)
    except AttributeError:
        from mercurial import patch
        return patch.export(repo, revs, template, fp, switch_parent, opts)
Esempio n. 15
0
def bzexport(ui, repo, *args, **opts):
    """
    Export changesets to bugzilla attachments.

    The -e option may be used to bring up an editor that will allow editing all
    fields of the attachment and bug (if creating one).

    The --new option may be used to create a new bug rather than using an
    existing bug. See the newbug command for details.

    The -u (--update) option is equivalent to setting both 'update-patch'
    and 'rename-patch' to True in the [bzexport] section of your config file.
    """
    auth, api_server, bugzilla = bugzilla_info(ui, opts.get('ffprofile'))

    rev, bug = infer_arguments(ui, repo, args, opts)

    if not opts['new']:
        for o in ('cc', 'depends', 'blocks'):
            if opts[o]:
                ui.write(
                    "Warning: ignoring --%s option when not creating a bug\n" %
                    o)

    contents = StringIO()
    diffopts = patch.diffopts(ui, opts)
    context = ui.config("bzexport", "unified",
                        ui.config("diff", "unified", None))
    if context:
        diffopts.context = int(context)
    if rev in repo:
        description_from_patch = repo[rev].description().decode('utf-8')
        if hasattr(cmdutil, "export"):
            cmdutil.export(repo, [rev], fp=contents, opts=diffopts)
        else:
            # Support older hg versions
            patch.export(repo, [rev], fp=contents, opts=diffopts)
    else:
        q = repo.mq
        contents = q.opener(q.lookup(rev), "r")
        description_from_patch = '\n'.join(
            mq.patchheader(q.join(rev), q.plainmode).message)

    # Just always use the rev name as the patch name. Doesn't matter much,
    # unless you want to avoid obsoleting existing patches when uploading a
    # version that doesn't include whitespace changes.
    filename = rev
    if opts['ignore_all_space']:
        filename += "_ws"

    patch_comment = None
    reviewers = []
    orig_desc = opts['description'] or description_from_patch
    if not orig_desc or orig_desc.startswith('[mq]'):
        desc = '<required>'
    else:
        # Lightly reformat changeset messages into attachment descriptions.
        # Only use the first line of the provided description for our actual
        # description - use the rest for the patch/bug comment.
        parts = orig_desc.split('\n', 1)
        firstline = parts[0]
        if len(parts) == 2:
            patch_comment = parts[1].strip()

        # Attempt to split the firstline into a bug number, and strip()ed
        # description with that bug number string removed.
        desc_bug_number, desc = extract_bug_num_and_desc(firstline)

        # Failing that try looking in the commit description for a bug number,
        # since orig_desc could have come from the command line instead.
        if not desc_bug_number:
            commit_firstline = description_from_patch.split('\n', 1)[0]
            desc_bug_number, __ = extract_bug_num_and_desc(commit_firstline)

        if desc_bug_number:
            if bug and bug != desc_bug_number:
                ui.warn(
                    "Warning: Bug number %s from commandline doesn't match "
                    "bug number %s from changeset description\n" %
                    (bug, desc_bug_number))
            else:
                bug = desc_bug_number

        # Strip any remaining leading separator and whitespace,
        # if the original was something like "bug NNN - "
        if desc[0] in ['-', ':', '.']:
            desc = desc[1:].lstrip()

        # Next strip off review and approval annotations, grabbing the
        # reviewers from the patch comments only if -r auto was given
        def grab_reviewer(m):
            if opts['review'] == 'auto':
                reviewers.append(m.group(1))
            return ''

        desc = review_re.sub(grab_reviewer, desc).rstrip()

        # Strip any trailing separators, if the original was something like:
        # "Desc; r=foo" or "Desc. r=foo"
        if desc and desc[-1] in (';', '.'):
            desc = desc[:-1].rstrip()

        if len(reviewers) > 0:
            opts['review'] = ''

    attachment_comment = opts['comment']
    bug_comment = opts['bug_description']

    if not attachment_comment:
        # New bugs get first shot at the patch comment
        if not opts['new'] or bug_comment:
            attachment_comment = patch_comment

    if not bug_comment and opts['new']:
        bug_comment = patch_comment

    if opts["review"]:
        search_strings = opts["review"].split(",")
        valid_users = validate_users(ui, api_server, auth, search_strings,
                                     multi_user_prompt, 'reviewer')
        reviewers = select_users(valid_users, search_strings)
    elif len(reviewers) > 0:
        # Pulled reviewers out of commit message
        valid_users = validate_users(ui, api_server, auth, reviewers,
                                     multi_user_prompt, 'reviewer')
        reviewers = select_users(valid_users, reviewers)

    if reviewers is None:
        raise util.Abort(_("Invalid reviewers"))

    feedback = []
    if opts["feedback"]:
        search_strings = opts["feedback"].split(",")
        valid_users = validate_users(ui, api_server, auth, search_strings,
                                     multi_user_prompt, 'feedback from')
        feedback = select_users(valid_users, search_strings)

    values = {
        'BUGNUM': bug,
        'ATTACHMENT_FILENAME': filename,
        'ATTACHMENT_DESCRIPTION': desc,
        'ATTACHCOMMENT': attachment_comment,
        'REVIEWERS': reviewers,
        'FEEDBACK': feedback,
    }

    cc = []
    depends = opts["depends"].split(",")
    blocks = opts["blocks"].split(",")
    if opts['new']:
        if opts["cc"]:
            search_strings = opts["cc"].split(",")
            valid_users = validate_users(ui, api_server, auth, search_strings,
                                         multi_user_prompt, 'CC')
            cc = select_users(valid_users, search_strings)

        values['BUGTITLE'] = opts['title'] or desc
        values['PRODUCT'] = opts.get('product', '') or ui.config(
            "bzexport", "product", '<choose-from-menu>')
        values['COMPONENT'] = opts.get('component', '') or ui.config(
            "bzexport", "component", '<choose-from-menu>')
        values['PRODVERSION'] = opts.get('prodversion', '') or ui.config(
            "bzexport", "prodversion", '<default>')
        values['BUGCOMMENT0'] = bug_comment
        values['CC'] = cc
        values['BLOCKS'] = blocks
        values['DEPENDS'] = depends

    values = fill_values(values, ui, api_server, finalize=False)

    if opts['edit']:
        if opts['new']:
            values = edit_form(ui, repo, values, 'new_both_template')
        else:
            values = edit_form(ui, repo, values, 'existing_bug_template')
            bug = values['BUGNUM']

        search_strings = []
        for key in ('REVIEWERS', 'CC', 'FEEDBACK'):
            # TODO: Handle <choose-from-menu>
            search_strings.extend(values.get(key, []))
        users = validate_users(ui, api_server, auth, search_strings,
                               multi_user_prompt, 'reviewer')
        if users is None:
            raise util.Abort("Invalid users")

        if 'REVIEWERS' in values:  # Always true
            reviewers = select_users(users, values['REVIEWERS'])
        if 'CC' in values:  # Only when opts['new']
            cc = select_users(users, values['CC'])
        if 'BLOCKS' in values:  # Only when opts['new']
            blocks = values['BLOCKS']
        if 'DEPENDS' in values:  # Only when opts['new']
            depends = values['DEPENDS']
        if 'FEEDBACK' in values:  # Always true
            feedback = select_users(users, values['FEEDBACK'])
        if 'ATTACHMENT_FILENAME' in values:
            filename = values['ATTACHMENT_FILENAME']

    values = fill_values(values, ui, api_server, finalize=True)

    if opts["new"]:
        if bug is not None:
            raise util.Abort(
                "Bug %s given but creation of new bug requested!" % bug)

        if opts['interactive'] and ui.prompt(
                _("Create bug in '%s' :: '%s' (y/n)?") %
            (values['PRODUCT'], values['COMPONENT'])) != 'y':
            ui.write(_("Exiting without creating bug\n"))
            return

        try:
            create_opts = {}
            if not opts['no_take_bug']:
                create_opts['assign_to'] = auth.username(api_server)
            result = bz.create_bug(auth,
                                   product=values['PRODUCT'],
                                   component=values['COMPONENT'],
                                   version=values['PRODVERSION'],
                                   title=values['BUGTITLE'],
                                   description=values['BUGCOMMENT0'],
                                   cc=cc,
                                   depends=depends,
                                   blocks=blocks,
                                   **create_opts)
            bug = result['id']
            ui.write("Created bug %s at %sshow_bug.cgi?id=%s\n" %
                     (bug, bugzilla, bug))
        except Exception, e:
            raise util.Abort(_("Error creating bug: %s\n" % str(e)))
Esempio n. 16
0
def dodiff(ui, repo, cmdline, pats, opts):
    '''Do the actual diff:

    - copy to a temp structure if diffing 2 internal revisions
    - copy to a temp structure if diffing working revision with
      another one and more than 1 file is changed
    - just invoke the diff for a single file in the working dir
    '''

    revs = opts.get('rev')
    change = opts.get('change')
    do3way = '$parent2' in cmdline

    if revs and change:
        msg = _('cannot specify --rev and --change at the same time')
        raise error.Abort(msg)
    elif change:
        node2 = scmutil.revsingle(repo, change, None).node()
        node1a, node1b = repo.changelog.parents(node2)
    else:
        node1a, node2 = scmutil.revpair(repo, revs)
        if not revs:
            node1b = repo.dirstate.p2()
        else:
            node1b = nullid

    # Disable 3-way merge if there is only one parent
    if do3way:
        if node1b == nullid:
            do3way = False

    subrepos = opts.get('subrepos')

    matcher = scmutil.match(repo[node2], pats, opts)

    if opts.get('patch'):
        if subrepos:
            raise error.Abort(_('--patch cannot be used with --subrepos'))
        if node2 is None:
            raise error.Abort(_('--patch requires two revisions'))
    else:
        mod_a, add_a, rem_a = list(
            map(set,
                repo.status(node1a, node2, matcher,
                            listsubrepos=subrepos)[:3]))
        if do3way:
            mod_b, add_b, rem_b = list(
                map(
                    set,
                    repo.status(node1b, node2, matcher,
                                listsubrepos=subrepos)[:3]))
        else:
            mod_b, add_b, rem_b = set(), set(), set()
        modadd = mod_a | add_a | mod_b | add_b
        common = modadd | rem_a | rem_b
        if not common:
            return 0

    tmproot = tempfile.mkdtemp(prefix='extdiff.')
    try:
        if not opts.get('patch'):
            # Always make a copy of node1a (and node1b, if applicable)
            dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
            dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
                             subrepos)[0]
            rev1a = '@%d' % repo[node1a].rev()
            if do3way:
                dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
                dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
                                 subrepos)[0]
                rev1b = '@%d' % repo[node1b].rev()
            else:
                dir1b = None
                rev1b = ''

            fnsandstat = []

            # If node2 in not the wc or there is >1 change, copy it
            dir2root = ''
            rev2 = ''
            if node2:
                dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
                rev2 = '@%d' % repo[node2].rev()
            elif len(common) > 1:
                #we only actually need to get the files to copy back to
                #the working dir in this case (because the other cases
                #are: diffing 2 revisions or single file -- in which case
                #the file is already directly passed to the diff tool).
                dir2, fnsandstat = snapshot(ui, repo, modadd, None, tmproot,
                                            subrepos)
            else:
                # This lets the diff tool open the changed file directly
                dir2 = ''
                dir2root = repo.root

            label1a = rev1a
            label1b = rev1b
            label2 = rev2

            # If only one change, diff the files instead of the directories
            # Handle bogus modifies correctly by checking if the files exist
            if len(common) == 1:
                common_file = util.localpath(common.pop())
                dir1a = os.path.join(tmproot, dir1a, common_file)
                label1a = common_file + rev1a
                if not os.path.isfile(dir1a):
                    dir1a = os.devnull
                if do3way:
                    dir1b = os.path.join(tmproot, dir1b, common_file)
                    label1b = common_file + rev1b
                    if not os.path.isfile(dir1b):
                        dir1b = os.devnull
                dir2 = os.path.join(dir2root, dir2, common_file)
                label2 = common_file + rev2
        else:
            template = 'hg-%h.patch'
            cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()],
                           fntemplate=repo.vfs.reljoin(tmproot, template),
                           match=matcher)
            label1a = cmdutil.makefilename(repo, template, node1a)
            label2 = cmdutil.makefilename(repo, template, node2)
            dir1a = repo.vfs.reljoin(tmproot, label1a)
            dir2 = repo.vfs.reljoin(tmproot, label2)
            dir1b = None
            label1b = None
            fnsandstat = []

        # Function to quote file/dir names in the argument string.
        # When not operating in 3-way mode, an empty string is
        # returned for parent2
        replace = {
            'parent': dir1a,
            'parent1': dir1a,
            'parent2': dir1b,
            'plabel1': label1a,
            'plabel2': label1b,
            'clabel': label2,
            'child': dir2,
            'root': repo.root
        }

        def quote(match):
            pre = match.group(2)
            key = match.group(3)
            if not do3way and key == 'parent2':
                return pre
            return pre + util.shellquote(replace[key])

        # Match parent2 first, so 'parent1?' will match both parent1 and parent
        regex = (r'''(['"]?)([^\s'"$]*)'''
                 r'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
        if not do3way and not re.search(regex, cmdline):
            cmdline += ' $parent1 $child'
        cmdline = re.sub(regex, quote, cmdline)

        ui.debug('running %r in %s\n' % (cmdline, tmproot))
        ui.system(cmdline, cwd=tmproot, blockedtag='extdiff')

        for copy_fn, working_fn, st in fnsandstat:
            cpstat = os.lstat(copy_fn)
            # Some tools copy the file and attributes, so mtime may not detect
            # all changes.  A size check will detect more cases, but not all.
            # The only certain way to detect every case is to diff all files,
            # which could be expensive.
            # copyfile() carries over the permission, so the mode check could
            # be in an 'elif' branch, but for the case where the file has
            # changed without affecting mtime or size.
            if (cpstat.st_mtime != st.st_mtime or cpstat.st_size != st.st_size
                    or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100)):
                ui.debug('file changed while diffing. '
                         'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
                util.copyfile(copy_fn, working_fn)

        return 1
    finally:
        ui.note(_('cleaning up temp directory\n'))
        shutil.rmtree(tmproot)
Esempio n. 17
0
def dodiff(ui, repo, cmdline, pats, opts):
    '''Do the actual diff:

    - copy to a temp structure if diffing 2 internal revisions
    - copy to a temp structure if diffing working revision with
      another one and more than 1 file is changed
    - just invoke the diff for a single file in the working dir
    '''

    revs = opts.get('rev')
    change = opts.get('change')
    do3way = '$parent2' in cmdline

    if revs and change:
        msg = _('cannot specify --rev and --change at the same time')
        raise error.Abort(msg)
    elif change:
        node2 = scmutil.revsingle(repo, change, None).node()
        node1a, node1b = repo.changelog.parents(node2)
    else:
        node1a, node2 = scmutil.revpair(repo, revs)
        if not revs:
            node1b = repo.dirstate.p2()
        else:
            node1b = nullid

    # Disable 3-way merge if there is only one parent
    if do3way:
        if node1b == nullid:
            do3way = False

    subrepos=opts.get('subrepos')

    matcher = scmutil.match(repo[node2], pats, opts)

    if opts.get('patch'):
        if subrepos:
            raise error.Abort(_('--patch cannot be used with --subrepos'))
        if node2 is None:
            raise error.Abort(_('--patch requires two revisions'))
    else:
        mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher,
                                                   listsubrepos=subrepos)[:3])
        if do3way:
            mod_b, add_b, rem_b = map(set,
                                      repo.status(node1b, node2, matcher,
                                                  listsubrepos=subrepos)[:3])
        else:
            mod_b, add_b, rem_b = set(), set(), set()
        modadd = mod_a | add_a | mod_b | add_b
        common = modadd | rem_a | rem_b
        if not common:
            return 0

    tmproot = tempfile.mkdtemp(prefix='extdiff.')
    try:
        if not opts.get('patch'):
            # Always make a copy of node1a (and node1b, if applicable)
            dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
            dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
                             subrepos)[0]
            rev1a = '@%d' % repo[node1a].rev()
            if do3way:
                dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
                dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
                                 subrepos)[0]
                rev1b = '@%d' % repo[node1b].rev()
            else:
                dir1b = None
                rev1b = ''

            fns_and_mtime = []

            # If node2 in not the wc or there is >1 change, copy it
            dir2root = ''
            rev2 = ''
            if node2:
                dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
                rev2 = '@%d' % repo[node2].rev()
            elif len(common) > 1:
                #we only actually need to get the files to copy back to
                #the working dir in this case (because the other cases
                #are: diffing 2 revisions or single file -- in which case
                #the file is already directly passed to the diff tool).
                dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot,
                                               subrepos)
            else:
                # This lets the diff tool open the changed file directly
                dir2 = ''
                dir2root = repo.root

            label1a = rev1a
            label1b = rev1b
            label2 = rev2

            # If only one change, diff the files instead of the directories
            # Handle bogus modifies correctly by checking if the files exist
            if len(common) == 1:
                common_file = util.localpath(common.pop())
                dir1a = os.path.join(tmproot, dir1a, common_file)
                label1a = common_file + rev1a
                if not os.path.isfile(dir1a):
                    dir1a = os.devnull
                if do3way:
                    dir1b = os.path.join(tmproot, dir1b, common_file)
                    label1b = common_file + rev1b
                    if not os.path.isfile(dir1b):
                        dir1b = os.devnull
                dir2 = os.path.join(dir2root, dir2, common_file)
                label2 = common_file + rev2
        else:
            template = 'hg-%h.patch'
            cmdutil.export(repo, [repo[node1a].rev(), repo[node2].rev()],
                           template=repo.vfs.reljoin(tmproot, template),
                           match=matcher)
            label1a = cmdutil.makefilename(repo, template, node1a)
            label2 = cmdutil.makefilename(repo, template, node2)
            dir1a = repo.vfs.reljoin(tmproot, label1a)
            dir2 = repo.vfs.reljoin(tmproot, label2)
            dir1b = None
            label1b = None
            fns_and_mtime = []

        # Function to quote file/dir names in the argument string.
        # When not operating in 3-way mode, an empty string is
        # returned for parent2
        replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
                   'plabel1': label1a, 'plabel2': label1b,
                   'clabel': label2, 'child': dir2,
                   'root': repo.root}
        def quote(match):
            pre = match.group(2)
            key = match.group(3)
            if not do3way and key == 'parent2':
                return pre
            return pre + util.shellquote(replace[key])

        # Match parent2 first, so 'parent1?' will match both parent1 and parent
        regex = (r'''(['"]?)([^\s'"$]*)'''
                 r'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
        if not do3way and not re.search(regex, cmdline):
            cmdline += ' $parent1 $child'
        cmdline = re.sub(regex, quote, cmdline)

        ui.debug('running %r in %s\n' % (cmdline, tmproot))
        ui.system(cmdline, cwd=tmproot)

        for copy_fn, working_fn, mtime in fns_and_mtime:
            if os.lstat(copy_fn).st_mtime != mtime:
                ui.debug('file changed while diffing. '
                         'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
                util.copyfile(copy_fn, working_fn)

        return 1
    finally:
        ui.note(_('cleaning up temp directory\n'))
        shutil.rmtree(tmproot)
Esempio n. 18
0
def createcmd(ui, repo, pats, opts):
    """subcommand that creates a new shelve"""

    def publicancestors(ctx):
        """Compute the public ancestors of a commit.

        Much faster than the revset ancestors(ctx) & draft()"""
        seen = set([nullrev])
        visit = util.deque()
        visit.append(ctx)
        while visit:
            ctx = visit.popleft()
            yield ctx.node()
            for parent in ctx.parents():
                rev = parent.rev()
                if rev not in seen:
                    seen.add(rev)
                    if parent.mutable():
                        visit.append(parent)

    wctx = repo[None]
    parents = wctx.parents()
    if len(parents) > 1:
        raise util.Abort(_('cannot shelve while merging'))
    parent = parents[0]

    # we never need the user, so we use a generic user for all shelve operations
    user = '******'
    label = repo._bookmarkcurrent or parent.branch() or 'default'

    # slashes aren't allowed in filenames, therefore we rename it
    label = label.replace('/', '_')

    def gennames():
        yield label
        for i in xrange(1, 100):
            yield '%s-%02d' % (label, i)

    shelvedfiles = []

    def commitfunc(ui, repo, message, match, opts):
        # check modified, added, removed, deleted only
        for flist in repo.status(match=match)[:4]:
            shelvedfiles.extend(flist)
        hasmq = util.safehasattr(repo, 'mq')
        if hasmq:
            saved, repo.mq.checkapplied = repo.mq.checkapplied, False
        try:
            return repo.commit(message, user, opts.get('date'), match,
                               editor=cmdutil.getcommiteditor(**opts))
        finally:
            if hasmq:
                repo.mq.checkapplied = saved

    if parent.node() != nullid:
        desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
    else:
        desc = '(changes in empty repository)'

    if not opts['message']:
        opts['message'] = desc

    name = opts['name']

    wlock = lock = tr = bms = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()

        bms = repo._bookmarks.copy()
        # use an uncommitted transaction to generate the bundle to avoid
        # pull races. ensure we don't print the abort message to stderr.
        tr = repo.transaction('commit', report=lambda x: None)

        if name:
            if shelvedfile(repo, name, 'hg').exists():
                raise util.Abort(_("a shelved change named '%s' already exists")
                                 % name)
        else:
            for n in gennames():
                if not shelvedfile(repo, n, 'hg').exists():
                    name = n
                    break
            else:
                raise util.Abort(_("too many shelved changes named '%s'") %
                                 label)

        # ensure we are not creating a subdirectory or a hidden file
        if '/' in name or '\\' in name:
            raise util.Abort(_('shelved change names may not contain slashes'))
        if name.startswith('.'):
            raise util.Abort(_("shelved change names may not start with '.'"))

        node = cmdutil.commit(ui, repo, commitfunc, pats, opts)

        if not node:
            stat = repo.status(match=scmutil.match(repo[None], pats, opts))
            if stat[3]:
                ui.status(_("nothing changed (%d missing files, see "
                            "'hg status')\n") % len(stat[3]))
            else:
                ui.status(_("nothing changed\n"))
            return 1

        phases.retractboundary(repo, phases.secret, [node])

        fp = shelvedfile(repo, name, 'files').opener('wb')
        fp.write('\0'.join(shelvedfiles))

        bases = list(publicancestors(repo[node]))
        cg = changegroup.changegroupsubset(repo, bases, [node], 'shelve')
        shelvedfile(repo, name, 'hg').writebundle(cg)
        cmdutil.export(repo, [node],
                       fp=shelvedfile(repo, name, 'patch').opener('wb'),
                       opts=mdiff.diffopts(git=True))


        if ui.formatted():
            desc = util.ellipsis(desc, ui.termwidth())
        ui.status(_('shelved as %s\n') % name)
        hg.update(repo, parent.node())
    finally:
        if bms:
            # restore old bookmarks
            repo._bookmarks.update(bms)
            repo._bookmarks.write()
        if tr:
            tr.abort()
        lockmod.release(lock, wlock)
Esempio n. 19
0
def _shelvecreatedcommit(repo, node, name):
    bases = list(mutableancestors(repo[node]))
    shelvedfile(repo, name, 'hg').writebundle(bases, node)
    cmdutil.export(repo, [node],
                   fp=shelvedfile(repo, name, patchextension).opener('wb'),
                   opts=mdiff.diffopts(git=True))
Esempio n. 20
0
def _docreatecmd(ui, repo, pats, opts):
    def mutableancestors(ctx):
        """return all mutable ancestors for ctx (included)

        Much faster than the revset ancestors(ctx) & draft()"""
        seen = set([nodemod.nullrev])
        visit = collections.deque()
        visit.append(ctx)
        while visit:
            ctx = visit.popleft()
            yield ctx.node()
            for parent in ctx.parents():
                rev = parent.rev()
                if rev not in seen:
                    seen.add(rev)
                    if parent.mutable():
                        visit.append(parent)

    wctx = repo[None]
    parents = wctx.parents()
    if len(parents) > 1:
        raise error.Abort(_('cannot shelve while merging'))
    parent = parents[0]
    origbranch = wctx.branch()

    # we never need the user, so we use a generic user for all shelve operations
    user = '******'
    label = repo._activebookmark or parent.branch() or 'default'

    # slashes aren't allowed in filenames, therefore we rename it
    label = label.replace('/', '_')

    def gennames():
        yield label
        for i in xrange(1, 100):
            yield '%s-%02d' % (label, i)

    if parent.node() != nodemod.nullid:
        desc = "changes to: %s" % parent.description().split('\n', 1)[0]
    else:
        desc = '(changes in empty repository)'

    if not opts.get('message'):
        opts['message'] = desc

    name = opts.get('name')

    lock = tr = None
    try:
        lock = repo.lock()

        # use an uncommitted transaction to generate the bundle to avoid
        # pull races. ensure we don't print the abort message to stderr.
        tr = repo.transaction('commit', report=lambda x: None)

        if name:
            if shelvedfile(repo, name, 'hg').exists():
                raise error.Abort(
                    _("a shelved change named '%s' already exists") % name)
        else:
            for n in gennames():
                if not shelvedfile(repo, n, 'hg').exists():
                    name = n
                    break
            else:
                raise error.Abort(
                    _("too many shelved changes named '%s'") % label)

        # ensure we are not creating a subdirectory or a hidden file
        if '/' in name or '\\' in name:
            raise error.Abort(
                _('shelved change names may not contain slashes'))
        if name.startswith('.'):
            raise error.Abort(_("shelved change names may not start with '.'"))
        interactive = opts.get('interactive', False)
        includeunknown = (opts.get('unknown', False)
                          and not opts.get('addremove', False))

        extra = {}
        if includeunknown:
            s = repo.status(match=scmutil.match(repo[None], pats, opts),
                            unknown=True)
            if s.unknown:
                extra['shelve_unknown'] = '\0'.join(s.unknown)
                repo[None].add(s.unknown)

        if _iswctxonnewbranch(repo) and not _isbareshelve(pats, opts):
            # In non-bare shelve we don't store newly created branch
            # at bundled commit
            repo.dirstate.setbranch(repo['.'].branch())

        def commitfunc(ui, repo, message, match, opts):
            hasmq = util.safehasattr(repo, 'mq')
            if hasmq:
                saved, repo.mq.checkapplied = repo.mq.checkapplied, False
            backup = repo.ui.backupconfig('phases', 'new-commit')
            try:
                repo.ui.setconfig('phases', 'new-commit', phases.secret)
                editor = cmdutil.getcommiteditor(editform='shelve.shelve',
                                                 **opts)
                return repo.commit(message,
                                   user,
                                   opts.get('date'),
                                   match,
                                   editor=editor,
                                   extra=extra)
            finally:
                repo.ui.restoreconfig(backup)
                if hasmq:
                    repo.mq.checkapplied = saved

        def interactivecommitfunc(ui, repo, *pats, **opts):
            match = scmutil.match(repo['.'], pats, {})
            message = opts['message']
            return commitfunc(ui, repo, message, match, opts)

        if not interactive:
            node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
        else:
            node = cmdutil.dorecord(ui, repo, interactivecommitfunc, None,
                                    False, cmdutil.recordfilter, *pats, **opts)
        if not node:
            stat = repo.status(match=scmutil.match(repo[None], pats, opts))
            if stat.deleted:
                ui.status(
                    _("nothing changed (%d missing files, see "
                      "'hg status')\n") % len(stat.deleted))
            else:
                ui.status(_("nothing changed\n"))
            return 1

        bases = list(mutableancestors(repo[node]))
        shelvedfile(repo, name, 'hg').writebundle(bases, node)
        cmdutil.export(repo, [node],
                       fp=shelvedfile(repo, name, 'patch').opener('wb'),
                       opts=mdiff.diffopts(git=True))

        if ui.formatted():
            desc = util.ellipsis(desc, ui.termwidth())
        ui.status(_('shelved as %s\n') % name)
        hg.update(repo, parent.node())
        if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts):
            repo.dirstate.setbranch(origbranch)

        _aborttransaction(repo)
    finally:
        lockmod.release(tr, lock)
Esempio n. 21
0
def createcmd(ui, repo, pats, opts):
    """subcommand that creates a new shelve"""
    def publicancestors(ctx):
        """Compute the public ancestors of a commit.

        Much faster than the revset ancestors(ctx) & draft()"""
        seen = set([nullrev])
        visit = util.deque()
        visit.append(ctx)
        while visit:
            ctx = visit.popleft()
            yield ctx.node()
            for parent in ctx.parents():
                rev = parent.rev()
                if rev not in seen:
                    seen.add(rev)
                    if parent.mutable():
                        visit.append(parent)

    wctx = repo[None]
    parents = wctx.parents()
    if len(parents) > 1:
        raise util.Abort(_('cannot shelve while merging'))
    parent = parents[0]

    # we never need the user, so we use a generic user for all shelve operations
    user = '******'
    label = repo._bookmarkcurrent or parent.branch() or 'default'

    # slashes aren't allowed in filenames, therefore we rename it
    label = label.replace('/', '_')

    def gennames():
        yield label
        for i in xrange(1, 100):
            yield '%s-%02d' % (label, i)

    shelvedfiles = []

    def commitfunc(ui, repo, message, match, opts):
        # check modified, added, removed, deleted only
        for flist in repo.status(match=match)[:4]:
            shelvedfiles.extend(flist)
        hasmq = util.safehasattr(repo, 'mq')
        if hasmq:
            saved, repo.mq.checkapplied = repo.mq.checkapplied, False
        try:
            return repo.commit(message,
                               user,
                               opts.get('date'),
                               match,
                               editor=cmdutil.getcommiteditor(**opts))
        finally:
            if hasmq:
                repo.mq.checkapplied = saved

    if parent.node() != nullid:
        desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
    else:
        desc = '(changes in empty repository)'

    if not opts['message']:
        opts['message'] = desc

    name = opts['name']

    wlock = lock = tr = bms = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()

        bms = repo._bookmarks.copy()
        # use an uncommitted transaction to generate the bundle to avoid
        # pull races. ensure we don't print the abort message to stderr.
        tr = repo.transaction('commit', report=lambda x: None)

        if name:
            if shelvedfile(repo, name, 'hg').exists():
                raise util.Abort(
                    _("a shelved change named '%s' already exists") % name)
        else:
            for n in gennames():
                if not shelvedfile(repo, n, 'hg').exists():
                    name = n
                    break
            else:
                raise util.Abort(
                    _("too many shelved changes named '%s'") % label)

        # ensure we are not creating a subdirectory or a hidden file
        if '/' in name or '\\' in name:
            raise util.Abort(_('shelved change names may not contain slashes'))
        if name.startswith('.'):
            raise util.Abort(_("shelved change names may not start with '.'"))

        node = cmdutil.commit(ui, repo, commitfunc, pats, opts)

        if not node:
            stat = repo.status(match=scmutil.match(repo[None], pats, opts))
            if stat[3]:
                ui.status(
                    _("nothing changed (%d missing files, see "
                      "'hg status')\n") % len(stat[3]))
            else:
                ui.status(_("nothing changed\n"))
            return 1

        phases.retractboundary(repo, phases.secret, [node])

        fp = shelvedfile(repo, name, 'files').opener('wb')
        fp.write('\0'.join(shelvedfiles))

        bases = list(publicancestors(repo[node]))
        cg = changegroup.changegroupsubset(repo, bases, [node], 'shelve')
        shelvedfile(repo, name, 'hg').writebundle(cg)
        cmdutil.export(repo, [node],
                       fp=shelvedfile(repo, name, 'patch').opener('wb'),
                       opts=mdiff.diffopts(git=True))

        if ui.formatted():
            desc = util.ellipsis(desc, ui.termwidth())
        ui.status(_('shelved as %s\n') % name)
        hg.update(repo, parent.node())
    finally:
        if bms:
            # restore old bookmarks
            repo._bookmarks.update(bms)
            repo._bookmarks.write()
        if tr:
            tr.abort()
        lockmod.release(lock, wlock)
Esempio n. 22
0
 def getpatches(revs):
     for r in scmutil.revrange(repo, revs):
         output = cStringIO.StringIO()
         cmdutil.export(repo, [r], fp=output,
                      opts=patch.diffopts(ui, opts))
         yield output.getvalue().split('\n')
Esempio n. 23
0
def bzexport(ui, repo, *args, **opts):
    """
    Export changesets to bugzilla attachments.

    The -e option may be used to bring up an editor that will allow editing all
    fields of the attachment and bug (if creating one).

    The --new option may be used to create a new bug rather than using an
    existing bug. See the newbug command for details.

    The -u (--update) option is equivalent to setting both 'update-patch'
    and 'rename-patch' to True in the [bzexport] section of your config file.
    """
    auth, api_server, bugzilla = bugzilla_info(ui, opts.get('ffprofile'))

    rev, bug = infer_arguments(ui, repo, args, opts)

    if not opts['new']:
        for o in ('cc', 'depends', 'blocks'):
            if opts[o]:
                ui.write("Warning: ignoring --%s option when not creating a bug\n" % o)

    contents = StringIO()
    diffopts = patch.diffopts(ui, opts)
    context = ui.config("bzexport", "unified", ui.config("diff", "unified", None))
    if context:
        diffopts.context = int(context)
    if rev in repo:
        description_from_patch = repo[rev].description().decode('utf-8')
        if hasattr(cmdutil, "export"):
            cmdutil.export(repo, [rev], fp=contents, opts=diffopts)
        else:
            # Support older hg versions
            patch.export(repo, [rev], fp=contents, opts=diffopts)
    else:
        q = repo.mq
        contents = q.opener(q.lookup(rev), "r")
        description_from_patch = '\n'.join(mq.patchheader(q.join(rev), q.plainmode).message)

    # Just always use the rev name as the patch name. Doesn't matter much,
    # unless you want to avoid obsoleting existing patches when uploading a
    # version that doesn't include whitespace changes.
    filename = rev
    if opts['ignore_all_space']:
        filename += "_ws"

    patch_comment = None
    reviewers = []
    orig_desc = opts['description'] or description_from_patch
    if not orig_desc or orig_desc.startswith('[mq]'):
        desc = '<required>'
    else:
        # Lightly reformat changeset messages into attachment descriptions.
        # Only use the first line of the provided description for our actual
        # description - use the rest for the patch/bug comment.
        parts = orig_desc.split('\n', 1)
        firstline = parts[0]
        if len(parts) == 2:
            patch_comment = parts[1].strip()

        # Attempt to split the firstline into a bug number, and strip()ed
        # description with that bug number string removed.
        desc_bug_number, desc = extract_bug_num_and_desc(firstline)

        # Failing that try looking in the commit description for a bug number,
        # since orig_desc could have come from the command line instead.
        if not desc_bug_number:
            commit_firstline = description_from_patch.split('\n', 1)[0]
            desc_bug_number, __ = extract_bug_num_and_desc(commit_firstline)

        if desc_bug_number:
            if bug and bug != desc_bug_number:
                ui.warn("Warning: Bug number %s from commandline doesn't match "
                        "bug number %s from changeset description\n"
                        % (bug, desc_bug_number))
            else:
                bug = desc_bug_number

        # Strip any remaining leading separator and whitespace,
        # if the original was something like "bug NNN - "
        if desc[0] in ['-', ':', '.']:
            desc = desc[1:].lstrip()

        # Next strip off review and approval annotations, grabbing the
        # reviewers from the patch comments only if -r auto was given
        def grab_reviewer(m):
            if opts['review'] == 'auto':
                reviewers.append(m.group(1))
            return ''
        desc = review_re.sub(grab_reviewer, desc).rstrip()

        # Strip any trailing separators, if the original was something like:
        # "Desc; r=foo" or "Desc. r=foo"
        if desc and desc[-1] in (';', '.'):
            desc = desc[:-1].rstrip()

        if len(reviewers) > 0:
            opts['review'] = ''

    attachment_comment = opts['comment']
    bug_comment = opts['bug_description']

    if not attachment_comment:
        # New bugs get first shot at the patch comment
        if not opts['new'] or bug_comment:
            attachment_comment = patch_comment

    if not bug_comment and opts['new']:
        bug_comment = patch_comment

    if opts["review"]:
        search_strings = opts["review"].split(",")
        valid_users = validate_users(ui, api_server, auth, search_strings, multi_user_prompt, 'reviewer')
        reviewers = select_users(valid_users, search_strings)
    elif len(reviewers) > 0:
        # Pulled reviewers out of commit message
        valid_users = validate_users(ui, api_server, auth, reviewers, multi_user_prompt, 'reviewer')
        reviewers = select_users(valid_users, reviewers)

    if reviewers is None:
        raise util.Abort(_("Invalid reviewers"))

    feedback = []
    if opts["feedback"]:
        search_strings = opts["feedback"].split(",")
        valid_users = validate_users(ui, api_server, auth, search_strings, multi_user_prompt, 'feedback from')
        feedback = select_users(valid_users, search_strings)

    values = {'BUGNUM': bug,
              'ATTACHMENT_FILENAME': filename,
              'ATTACHMENT_DESCRIPTION': desc,
              'ATTACHCOMMENT': attachment_comment,
              'REVIEWERS': reviewers,
              'FEEDBACK': feedback,
              }

    cc = []
    depends = opts["depends"].split(",")
    blocks = opts["blocks"].split(",")
    if opts['new']:
        if opts["cc"]:
            search_strings = opts["cc"].split(",")
            valid_users = validate_users(ui, api_server, auth, search_strings, multi_user_prompt, 'CC')
            cc = select_users(valid_users, search_strings)

        values['BUGTITLE'] = opts['title'] or desc
        values['PRODUCT'] = opts.get('product', '') or ui.config("bzexport", "product", '<choose-from-menu>')
        values['COMPONENT'] = opts.get('component', '') or ui.config("bzexport", "component", '<choose-from-menu>')
        values['PRODVERSION'] = opts.get('prodversion', '') or ui.config("bzexport", "prodversion", '<default>')
        values['BUGCOMMENT0'] = bug_comment
        values['CC'] = cc
        values['BLOCKS'] = blocks
        values['DEPENDS'] = depends

    values = fill_values(values, ui, api_server, finalize=False)

    if opts['edit']:
        if opts['new']:
            values = edit_form(ui, repo, values, 'new_both_template')
        else:
            values = edit_form(ui, repo, values, 'existing_bug_template')
            bug = values['BUGNUM']

        search_strings = []
        for key in ('REVIEWERS', 'CC', 'FEEDBACK'):
            # TODO: Handle <choose-from-menu>
            search_strings.extend(values.get(key, []))
        users = validate_users(ui, api_server, auth, search_strings, multi_user_prompt, 'reviewer')
        if users is None:
            raise util.Abort("Invalid users")

        if 'REVIEWERS' in values:  # Always true
            reviewers = select_users(users, values['REVIEWERS'])
        if 'CC' in values:         # Only when opts['new']
            cc = select_users(users, values['CC'])
        if 'BLOCKS' in values:     # Only when opts['new']
            blocks = values['BLOCKS']
        if 'DEPENDS' in values:    # Only when opts['new']
            depends = values['DEPENDS']
        if 'FEEDBACK' in values:   # Always true
            feedback = select_users(users, values['FEEDBACK'])
        if 'ATTACHMENT_FILENAME' in values:
            filename = values['ATTACHMENT_FILENAME']

    values = fill_values(values, ui, api_server, finalize=True)

    if opts["new"]:
        if bug is not None:
            raise util.Abort("Bug %s given but creation of new bug requested!" % bug)

        if opts['interactive'] and ui.prompt(_("Create bug in '%s' :: '%s' (y/n)?") %
                                             (values['PRODUCT'], values['COMPONENT'])) != 'y':
            ui.write(_("Exiting without creating bug\n"))
            return

        try:
            create_opts = {}
            if not opts['no_take_bug']:
                create_opts['assign_to'] = auth.username(api_server)
            result = bz.create_bug(auth,
                                product=values['PRODUCT'],
                                component=values['COMPONENT'],
                                version=values['PRODVERSION'],
                                title=values['BUGTITLE'],
                                description=values['BUGCOMMENT0'],
                                cc=cc,
                                depends=depends,
                                blocks=blocks,
                                **create_opts)
            bug = result['id']
            ui.write("Created bug %s at %sshow_bug.cgi?id=%s\n" % (bug, bugzilla, bug))
        except Exception, e:
            raise util.Abort(_("Error creating bug: %s\n" % str(e)))
Esempio n. 24
0
def dodiff(ui, repo, cmdline, pats, opts, guitool=False):
    '''Do the actual diff:

    - copy to a temp structure if diffing 2 internal revisions
    - copy to a temp structure if diffing working revision with
      another one and more than 1 file is changed
    - just invoke the diff for a single file in the working dir
    '''

    revs = opts.get(b'rev')
    change = opts.get(b'change')
    do3way = b'$parent2' in cmdline

    if revs and change:
        msg = _(b'cannot specify --rev and --change at the same time')
        raise error.Abort(msg)
    elif change:
        ctx2 = scmutil.revsingle(repo, change, None)
        ctx1a, ctx1b = ctx2.p1(), ctx2.p2()
    else:
        ctx1a, ctx2 = scmutil.revpair(repo, revs)
        if not revs:
            ctx1b = repo[None].p2()
        else:
            ctx1b = repo[nullid]

    perfile = opts.get(b'per_file')
    confirm = opts.get(b'confirm')

    node1a = ctx1a.node()
    node1b = ctx1b.node()
    node2 = ctx2.node()

    # Disable 3-way merge if there is only one parent
    if do3way:
        if node1b == nullid:
            do3way = False

    subrepos = opts.get(b'subrepos')

    matcher = scmutil.match(repo[node2], pats, opts)

    if opts.get(b'patch'):
        if subrepos:
            raise error.Abort(_(b'--patch cannot be used with --subrepos'))
        if perfile:
            raise error.Abort(_(b'--patch cannot be used with --per-file'))
        if node2 is None:
            raise error.Abort(_(b'--patch requires two revisions'))
    else:
        mod_a, add_a, rem_a = map(
            set,
            repo.status(node1a, node2, matcher, listsubrepos=subrepos)[:3])
        if do3way:
            mod_b, add_b, rem_b = map(
                set,
                repo.status(node1b, node2, matcher, listsubrepos=subrepos)[:3],
            )
        else:
            mod_b, add_b, rem_b = set(), set(), set()
        modadd = mod_a | add_a | mod_b | add_b
        common = modadd | rem_a | rem_b
        if not common:
            return 0

    tmproot = pycompat.mkdtemp(prefix=b'extdiff.')
    try:
        if not opts.get(b'patch'):
            # Always make a copy of node1a (and node1b, if applicable)
            dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
            dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot,
                             subrepos)[0]
            rev1a = b'@%d' % repo[node1a].rev()
            if do3way:
                dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
                dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot,
                                 subrepos)[0]
                rev1b = b'@%d' % repo[node1b].rev()
            else:
                dir1b = None
                rev1b = b''

            fnsandstat = []

            # If node2 in not the wc or there is >1 change, copy it
            dir2root = b''
            rev2 = b''
            if node2:
                dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
                rev2 = b'@%d' % repo[node2].rev()
            elif len(common) > 1:
                # we only actually need to get the files to copy back to
                # the working dir in this case (because the other cases
                # are: diffing 2 revisions or single file -- in which case
                # the file is already directly passed to the diff tool).
                dir2, fnsandstat = snapshot(ui, repo, modadd, None, tmproot,
                                            subrepos)
            else:
                # This lets the diff tool open the changed file directly
                dir2 = b''
                dir2root = repo.root

            label1a = rev1a
            label1b = rev1b
            label2 = rev2

            # If only one change, diff the files instead of the directories
            # Handle bogus modifies correctly by checking if the files exist
            if len(common) == 1:
                common_file = util.localpath(common.pop())
                dir1a = os.path.join(tmproot, dir1a, common_file)
                label1a = common_file + rev1a
                if not os.path.isfile(dir1a):
                    dir1a = os.devnull
                if do3way:
                    dir1b = os.path.join(tmproot, dir1b, common_file)
                    label1b = common_file + rev1b
                    if not os.path.isfile(dir1b):
                        dir1b = os.devnull
                dir2 = os.path.join(dir2root, dir2, common_file)
                label2 = common_file + rev2
        else:
            template = b'hg-%h.patch'
            with formatter.nullformatter(ui, b'extdiff', {}) as fm:
                cmdutil.export(
                    repo,
                    [repo[node1a].rev(), repo[node2].rev()],
                    fm,
                    fntemplate=repo.vfs.reljoin(tmproot, template),
                    match=matcher,
                )
            label1a = cmdutil.makefilename(repo[node1a], template)
            label2 = cmdutil.makefilename(repo[node2], template)
            dir1a = repo.vfs.reljoin(tmproot, label1a)
            dir2 = repo.vfs.reljoin(tmproot, label2)
            dir1b = None
            label1b = None
            fnsandstat = []

        if not perfile:
            # Run the external tool on the 2 temp directories or the patches
            cmdline = formatcmdline(
                cmdline,
                repo.root,
                do3way=do3way,
                parent1=dir1a,
                plabel1=label1a,
                parent2=dir1b,
                plabel2=label1b,
                child=dir2,
                clabel=label2,
            )
            ui.debug(b'running %r in %s\n' %
                     (pycompat.bytestr(cmdline), tmproot))
            ui.system(cmdline, cwd=tmproot, blockedtag=b'extdiff')
        else:
            # Run the external tool once for each pair of files
            _runperfilediff(
                cmdline,
                repo.root,
                ui,
                guitool=guitool,
                do3way=do3way,
                confirm=confirm,
                commonfiles=common,
                tmproot=tmproot,
                dir1a=dir1a,
                dir1b=dir1b,
                dir2root=dir2root,
                dir2=dir2,
                rev1a=rev1a,
                rev1b=rev1b,
                rev2=rev2,
            )

        for copy_fn, working_fn, st in fnsandstat:
            cpstat = os.lstat(copy_fn)
            # Some tools copy the file and attributes, so mtime may not detect
            # all changes.  A size check will detect more cases, but not all.
            # The only certain way to detect every case is to diff all files,
            # which could be expensive.
            # copyfile() carries over the permission, so the mode check could
            # be in an 'elif' branch, but for the case where the file has
            # changed without affecting mtime or size.
            if (cpstat[stat.ST_MTIME] != st[stat.ST_MTIME]
                    or cpstat.st_size != st.st_size
                    or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100)):
                ui.debug(b'file changed while diffing. '
                         b'Overwriting: %s (src: %s)\n' %
                         (working_fn, copy_fn))
                util.copyfile(copy_fn, working_fn)

        return 1
    finally:
        ui.note(_(b'cleaning up temp directory\n'))
        shutil.rmtree(tmproot)
Esempio n. 25
0
 def getpatches(revs):
     for r in scmutil.revrange(repo, revs):
         output = cStringIO.StringIO()
         cmdutil.export(repo, [r], fp=output, opts=patch.diffopts(ui, opts))
         yield output.getvalue().split('\n')
Esempio n. 26
0
def createcmd(ui, repo, pats, opts):
    """subcommand that creates a new shelve"""

    def mutableancestors(ctx):
        """return all mutable ancestors for ctx (included)

        Much faster than the revset ancestors(ctx) & draft()"""
        seen = set([nullrev])
        visit = collections.deque()
        visit.append(ctx)
        while visit:
            ctx = visit.popleft()
            yield ctx.node()
            for parent in ctx.parents():
                rev = parent.rev()
                if rev not in seen:
                    seen.add(rev)
                    if parent.mutable():
                        visit.append(parent)

    wctx = repo[None]
    parents = wctx.parents()
    if len(parents) > 1:
        raise error.Abort(_('cannot shelve while merging'))
    parent = parents[0]

    # we never need the user, so we use a generic user for all shelve operations
    user = '******'
    label = repo._activebookmark or parent.branch() or 'default'

    # slashes aren't allowed in filenames, therefore we rename it
    label = label.replace('/', '_')

    def gennames():
        yield label
        for i in xrange(1, 100):
            yield '%s-%02d' % (label, i)

    def commitfunc(ui, repo, message, match, opts):
        hasmq = util.safehasattr(repo, 'mq')
        if hasmq:
            saved, repo.mq.checkapplied = repo.mq.checkapplied, False
        backup = repo.ui.backupconfig('phases', 'new-commit')
        try:
            repo.ui. setconfig('phases', 'new-commit', phases.secret)
            editor = cmdutil.getcommiteditor(editform='shelve.shelve', **opts)
            return repo.commit(message, user, opts.get('date'), match,
                               editor=editor)
        finally:
            repo.ui.restoreconfig(backup)
            if hasmq:
                repo.mq.checkapplied = saved

    if parent.node() != nullid:
        desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
    else:
        desc = '(changes in empty repository)'

    if not opts['message']:
        opts['message'] = desc

    name = opts['name']

    wlock = lock = tr = None
    try:
        wlock = repo.wlock()
        lock = repo.lock()

        # use an uncommitted transaction to generate the bundle to avoid
        # pull races. ensure we don't print the abort message to stderr.
        tr = repo.transaction('commit', report=lambda x: None)

        if name:
            if shelvedfile(repo, name, 'hg').exists():
                raise error.Abort(_("a shelved change named '%s' already exists"
                                   ) % name)
        else:
            for n in gennames():
                if not shelvedfile(repo, n, 'hg').exists():
                    name = n
                    break
            else:
                raise error.Abort(_("too many shelved changes named '%s'") %
                                 label)

        # ensure we are not creating a subdirectory or a hidden file
        if '/' in name or '\\' in name:
            raise error.Abort(_('shelved change names may not contain slashes'))
        if name.startswith('.'):
            raise error.Abort(_("shelved change names may not start with '.'"))
        interactive = opts.get('interactive', False)

        def interactivecommitfunc(ui, repo, *pats, **opts):
            match = scmutil.match(repo['.'], pats, {})
            message = opts['message']
            return commitfunc(ui, repo, message, match, opts)
        if not interactive:
            node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
        else:
            node = cmdutil.dorecord(ui, repo, interactivecommitfunc, None,
                                    False, cmdutil.recordfilter, *pats, **opts)
        if not node:
            stat = repo.status(match=scmutil.match(repo[None], pats, opts))
            if stat.deleted:
                ui.status(_("nothing changed (%d missing files, see "
                            "'hg status')\n") % len(stat.deleted))
            else:
                ui.status(_("nothing changed\n"))
            return 1

        bases = list(mutableancestors(repo[node]))
        shelvedfile(repo, name, 'hg').writebundle(bases, node)
        cmdutil.export(repo, [node],
                       fp=shelvedfile(repo, name, 'patch').opener('wb'),
                       opts=mdiff.diffopts(git=True))


        if ui.formatted():
            desc = util.ellipsis(desc, ui.termwidth())
        ui.status(_('shelved as %s\n') % name)
        hg.update(repo, parent.node())

        _aborttransaction(repo)
    finally:
        lockmod.release(tr, lock, wlock)