def duplicatecopies(repo, rev, p1, p2):
    "Reproduce copies found in the source revision in the dirstate for grafts"
    # Here we simulate the copies and renames in the source changeset
    cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
    m1 = repo[rev].manifest()
    m2 = repo[p1].manifest()
    for k, v in cop.iteritems():
        if k in m1:
            if v in m1 or v in m2:
                repo.dirstate.copy(v, k)
                if v in m2 and v not in m1 and k in m2:
                    repo.dirstate.remove(v)
Example #2
0
def duplicatecopies(repo, rev, p1, p2):
    "Reproduce copies found in the source revision in the dirstate for grafts"
    # Here we simulate the copies and renames in the source changeset
    cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
    m1 = repo[rev].manifest()
    m2 = repo[p1].manifest()
    for k, v in cop.iteritems():
        if k in m1:
            if v in m1 or v in m2:
                repo.dirstate.copy(v, k)
                if v in m2 and v not in m1 and k in m2:
                    repo.dirstate.remove(v)
Example #3
0
def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None):
    '''yields diff of changes to files between two nodes, or node and
    working directory.

    if node1 is None, use first dirstate parent instead.
    if node2 is None, compare node1 with working directory.'''

    if opts is None:
        opts = mdiff.defaultopts

    if not node1:
        node1 = repo.dirstate.parents()[0]

    def lrugetfilectx():
        cache = {}
        order = []
        def getfilectx(f, ctx):
            fctx = ctx.filectx(f, filelog=cache.get(f))
            if f not in cache:
                if len(cache) > 20:
                    del cache[order.pop(0)]
                cache[f] = fctx._filelog
            else:
                order.remove(f)
            order.append(f)
            return fctx
        return getfilectx
    getfilectx = lrugetfilectx()

    ctx1 = repo[node1]
    ctx2 = repo[node2]

    if not changes:
        changes = repo.status(ctx1, ctx2, match=match)
    modified, added, removed = changes[:3]

    if not modified and not added and not removed:
        return

    date1 = util.datestr(ctx1.date())
    man1 = ctx1.manifest()

    if repo.ui.quiet:
        r = None
    else:
        hexfunc = repo.ui.debugflag and hex or short
        r = [hexfunc(node) for node in [node1, node2] if node]

    if opts.git:
        copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
        copy = copy.copy()
        for k, v in copy.items():
            copy[v] = k

    gone = set()
    gitmode = {'l': '120000', 'x': '100755', '': '100644'}

    for f in sorted(modified + added + removed):
        to = None
        tn = None
        dodiff = True
        header = []
        if f in man1:
            to = getfilectx(f, ctx1).data()
        if f not in removed:
            tn = getfilectx(f, ctx2).data()
        a, b = f, f
        if opts.git:
            if f in added:
                mode = gitmode[ctx2.flags(f)]
                if f in copy:
                    a = copy[f]
                    omode = gitmode[man1.flags(a)]
                    _addmodehdr(header, omode, mode)
                    if a in removed and a not in gone:
                        op = 'rename'
                        gone.add(a)
                    else:
                        op = 'copy'
                    header.append('%s from %s\n' % (op, a))
                    header.append('%s to %s\n' % (op, f))
                    to = getfilectx(a, ctx1).data()
                else:
                    header.append('new file mode %s\n' % mode)
                if util.binary(tn):
                    dodiff = 'binary'
            elif f in removed:
                # have we already reported a copy above?
                if f in copy and copy[f] in added and copy[copy[f]] == f:
                    dodiff = False
                else:
                    header.append('deleted file mode %s\n' %
                                  gitmode[man1.flags(f)])
            else:
                omode = gitmode[man1.flags(f)]
                nmode = gitmode[ctx2.flags(f)]
                _addmodehdr(header, omode, nmode)
                if util.binary(to) or util.binary(tn):
                    dodiff = 'binary'
            r = None
            header.insert(0, mdiff.diffline(r, a, b, opts))
        if dodiff:
            if dodiff == 'binary':
                text = b85diff(to, tn)
            else:
                text = mdiff.unidiff(to, date1,
                                    # ctx2 date may be dynamic
                                    tn, util.datestr(ctx2.date()),
                                    a, b, r, opts=opts)
            if header and (text or len(header) > 1):
                yield ''.join(header)
            if text:
                yield text
Example #4
0
def manifestmerge(repo, p1, p2, pa, overwrite, partial):
    """
    Merge p1 and p2 with ancestor ma and generate merge action list

    overwrite = whether we clobber working files
    partial = function to filter file lists
    """

    repo.ui.note(_("resolving manifests\n"))
    repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
    repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))

    m1 = p1.manifest()
    m2 = p2.manifest()
    ma = pa.manifest()
    backwards = (pa == p2)
    action = []
    copy, copied, diverge = {}, {}, {}

    def fmerge(f, f2=None, fa=None):
        """merge flags"""
        if not f2:
            f2 = f
            fa = f
        a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
        if m == n: # flags agree
            return m # unchanged
        if m and n: # flags are set but don't agree
            if not a: # both differ from parent
                r = repo.ui.prompt(
                    _(" conflicting flags for %s\n"
                      "(n)one, e(x)ec or sym(l)ink?") % f, "[nxl]", "n")
                return r != "n" and r or ''
            if m == a:
                return n # changed from m to n
            return m # changed from n to m
        if m and m != a: # changed from a to m
            return m
        if n and n != a: # changed from a to n
            return n
        return '' # flag was cleared

    def act(msg, m, f, *args):
        repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
        action.append((f, m) + args)

    if pa and not (backwards or overwrite):
        if repo.ui.configbool("merge", "followcopies", True):
            dirs = repo.ui.configbool("merge", "followdirs", True)
            copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
        copied = dict.fromkeys(copy.values())
        for of, fl in diverge.items():
            act("divergent renames", "dr", of, fl)

    # Compare manifests
    for f, n in m1.iteritems():
        if partial and not partial(f):
            continue
        if f in m2:
            if overwrite or backwards:
                rflags = m2.flags(f)
            else:
                rflags = fmerge(f)
            # are files different?
            if n != m2[f]:
                a = ma.get(f, nullid)
                # are we clobbering?
                if overwrite:
                    act("clobbering", "g", f, rflags)
                # or are we going back in time and clean?
                elif backwards and not n[20:]:
                    act("reverting", "g", f, rflags)
                # are both different from the ancestor?
                elif n != a and m2[f] != a:
                    act("versions differ", "m", f, f, f, rflags, False)
                # is remote's version newer?
                elif m2[f] != a:
                    act("remote is newer", "g", f, rflags)
                # local is newer, not overwrite, check mode bits
                elif m1.flags(f) != rflags:
                    act("update permissions", "e", f, rflags)
            # contents same, check mode bits
            elif m1.flags(f) != rflags:
                act("update permissions", "e", f, rflags)
        elif f in copied:
            continue
        elif f in copy:
            f2 = copy[f]
            if f2 not in m2: # directory rename
                act("remote renamed directory to " + f2, "d",
                    f, None, f2, m1.flags(f))
            elif f2 in m1: # case 2 A,B/B/B
                act("local copied to " + f2, "m",
                    f, f2, f, fmerge(f, f2, f2), False)
            else: # case 4,21 A/B/B
                act("local moved to " + f2, "m",
                    f, f2, f, fmerge(f, f2, f2), False)
        elif f in ma:
            if n != ma[f] and not overwrite:
                if repo.ui.prompt(
                    _(" local changed %s which remote deleted\n"
                      "use (c)hanged version or (d)elete?") % f,
                    _("[cd]"), _("c")) == _("d"):
                    act("prompt delete", "r", f)
            else:
                act("other deleted", "r", f)
        else:
            # file is created on branch or in working directory
            if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
                act("remote deleted", "r", f)

    for f, n in m2.iteritems():
        if partial and not partial(f):
            continue
        if f in m1:
            continue
        if f in copied:
            continue
        if f in copy:
            f2 = copy[f]
            if f2 not in m1: # directory rename
                act("local renamed directory to " + f2, "d",
                    None, f, f2, m2.flags(f))
            elif f2 in m2: # rename case 1, A/A,B/A
                act("remote copied to " + f, "m",
                    f2, f, f, fmerge(f2, f, f2), False)
            else: # case 3,20 A/B/A
                act("remote moved to " + f, "m",
                    f2, f, f, fmerge(f2, f, f2), True)
        elif f in ma:
            if overwrite or backwards:
                act("recreating", "g", f, m2.flags(f))
            elif n != ma[f]:
                if repo.ui.prompt(
                    _("remote changed %s which local deleted\n"
                      "use (c)hanged version or leave (d)eleted?") % f,
                    _("[cd]"), _("c")) == _("c"):
                    act("prompt recreating", "g", f, m2.flags(f))
        else:
            act("remote created", "g", f, m2.flags(f))

    return action
Example #5
0
def manifestmerge(repo, p1, p2, pa, overwrite, partial):
    """
    Merge p1 and p2 with ancestor ma and generate merge action list

    overwrite = whether we clobber working files
    partial = function to filter file lists
    """
    def fmerge(f, f2, fa):
        """merge flags"""
        a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
        if m == n:  # flags agree
            return m  # unchanged
        if m and n and not a:  # flags set, don't agree, differ from parent
            r = repo.ui.promptchoice(
                _(" conflicting flags for %s\n"
                  "(n)one, e(x)ec or sym(l)ink?") % f,
                (_("&None"), _("E&xec"), _("Sym&link")), 0)
            if r == 1: return "x"  # Exec
            if r == 2: return "l"  # Symlink
            return ""
        if m and m != a:  # changed from a to m
            return m
        if n and n != a:  # changed from a to n
            return n
        return ''  # flag was cleared

    def act(msg, m, f, *args):
        repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
        action.append((f, m) + args)

    action, copy = [], {}

    if overwrite:
        pa = p1
    elif pa == p2:  # backwards
        pa = p1.p1()
    elif pa and repo.ui.configbool("merge", "followcopies", True):
        dirs = repo.ui.configbool("merge", "followdirs", True)
        copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
        for of, fl in diverge.iteritems():
            act("divergent renames", "dr", of, fl)

    repo.ui.note(_("resolving manifests\n"))
    repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
    repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))

    m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
    copied = set(copy.values())

    # Compare manifests
    for f, n in m1.iteritems():
        if partial and not partial(f):
            continue
        if f in m2:
            rflags = fmerge(f, f, f)
            a = ma.get(f, nullid)
            if n == m2[f] or m2[f] == a:  # same or local newer
                if m1.flags(f) != rflags:
                    act("update permissions", "e", f, rflags)
            elif n == a:  # remote newer
                act("remote is newer", "g", f, rflags)
            else:  # both changed
                act("versions differ", "m", f, f, f, rflags, False)
        elif f in copied:  # files we'll deal with on m2 side
            pass
        elif f in copy:
            f2 = copy[f]
            if f2 not in m2:  # directory rename
                act("remote renamed directory to " + f2, "d", f, None, f2,
                    m1.flags(f))
            else:  # case 2 A,B/B/B or case 4,21 A/B/B
                act("local copied/moved to " + f2, "m", f, f2, f,
                    fmerge(f, f2, f2), False)
        elif f in ma:  # clean, a different, no remote
            if n != ma[f]:
                if repo.ui.promptchoice(
                        _(" local changed %s which remote deleted\n"
                          "use (c)hanged version or (d)elete?") % f,
                    (_("&Changed"), _("&Delete")), 0):
                    act("prompt delete", "r", f)
                else:
                    act("prompt keep", "a", f)
            elif n[20:] == "a":  # added, no remote
                act("remote deleted", "f", f)
            elif n[20:] != "u":
                act("other deleted", "r", f)

    for f, n in m2.iteritems():
        if partial and not partial(f):
            continue
        if f in m1 or f in copied:  # files already visited
            continue
        if f in copy:
            f2 = copy[f]
            if f2 not in m1:  # directory rename
                act("local renamed directory to " + f2, "d", None, f, f2,
                    m2.flags(f))
            elif f2 in m2:  # rename case 1, A/A,B/A
                act("remote copied to " + f, "m", f2, f, f, fmerge(f2, f, f2),
                    False)
            else:  # case 3,20 A/B/A
                act("remote moved to " + f, "m", f2, f, f, fmerge(f2, f, f2),
                    True)
        elif f not in ma:
            act("remote created", "g", f, m2.flags(f))
        elif n != ma[f]:
            if repo.ui.promptchoice(
                    _("remote changed %s which local deleted\n"
                      "use (c)hanged version or leave (d)eleted?") % f,
                (_("&Changed"), _("&Deleted")), 0) == 0:
                act("prompt recreating", "g", f, m2.flags(f))

    return action
Example #6
0
def diff(repo, node1=None, node2=None, files=None, match=util.always,
         fp=None, changes=None, opts=None):
    '''print diff of changes to files between two nodes, or node and
    working directory.

    if node1 is None, use first dirstate parent instead.
    if node2 is None, compare node1 with working directory.'''

    if opts is None:
        opts = mdiff.defaultopts
    if fp is None:
        fp = repo.ui

    if not node1:
        node1 = repo.dirstate.parents()[0]

    ccache = {}
    def getctx(r):
        if r not in ccache:
            ccache[r] = context.changectx(repo, r)
        return ccache[r]

    flcache = {}
    def getfilectx(f, ctx):
        flctx = ctx.filectx(f, filelog=flcache.get(f))
        if f not in flcache:
            flcache[f] = flctx._filelog
        return flctx

    # reading the data for node1 early allows it to play nicely
    # with repo.status and the revlog cache.
    ctx1 = context.changectx(repo, node1)
    # force manifest reading
    man1 = ctx1.manifest()
    date1 = util.datestr(ctx1.date())

    if not changes:
        changes = repo.status(node1, node2, files, match=match)[:5]
    modified, added, removed, deleted, unknown = changes

    if not modified and not added and not removed:
        return

    if node2:
        ctx2 = context.changectx(repo, node2)
        execf2 = ctx2.manifest().execf
        linkf2 = ctx2.manifest().linkf
    else:
        ctx2 = context.workingctx(repo)
        execf2 = util.execfunc(repo.root, None)
        linkf2 = util.linkfunc(repo.root, None)
        if execf2 is None:
            mc = ctx2.parents()[0].manifest().copy()
            execf2 = mc.execf
            linkf2 = mc.linkf

    if repo.ui.quiet:
        r = None
    else:
        hexfunc = repo.ui.debugflag and hex or short
        r = [hexfunc(node) for node in [node1, node2] if node]

    if opts.git:
        copy, diverge = copies.copies(repo, ctx1, ctx2, repo.changectx(nullid))
        for k, v in copy.items():
            copy[v] = k

    all = modified + added + removed
    all.sort()
    gone = {}

    for f in all:
        to = None
        tn = None
        dodiff = True
        header = []
        if f in man1:
            to = getfilectx(f, ctx1).data()
        if f not in removed:
            tn = getfilectx(f, ctx2).data()
        a, b = f, f
        if opts.git:
            def gitmode(x, l):
                return l and '120000' or (x and '100755' or '100644')
            def addmodehdr(header, omode, nmode):
                if omode != nmode:
                    header.append('old mode %s\n' % omode)
                    header.append('new mode %s\n' % nmode)

            if f in added:
                mode = gitmode(execf2(f), linkf2(f))
                if f in copy:
                    a = copy[f]
                    omode = gitmode(man1.execf(a), man1.linkf(a))
                    addmodehdr(header, omode, mode)
                    if a in removed and a not in gone:
                        op = 'rename'
                        gone[a] = 1
                    else:
                        op = 'copy'
                    header.append('%s from %s\n' % (op, a))
                    header.append('%s to %s\n' % (op, f))
                    to = getfilectx(a, ctx1).data()
                else:
                    header.append('new file mode %s\n' % mode)
                if util.binary(tn):
                    dodiff = 'binary'
            elif f in removed:
                # have we already reported a copy above?
                if f in copy and copy[f] in added and copy[copy[f]] == f:
                    dodiff = False
                else:
                    mode = gitmode(man1.execf(f), man1.linkf(f))
                    header.append('deleted file mode %s\n' % mode)
            else:
                omode = gitmode(man1.execf(f), man1.linkf(f))
                nmode = gitmode(execf2(f), linkf2(f))
                addmodehdr(header, omode, nmode)
                if util.binary(to) or util.binary(tn):
                    dodiff = 'binary'
            r = None
            header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
        if dodiff:
            if dodiff == 'binary':
                text = b85diff(to, tn)
            else:
                text = mdiff.unidiff(to, date1,
                                    # ctx2 date may be dynamic
                                    tn, util.datestr(ctx2.date()),
                                    a, b, r, opts=opts)
            if text or len(header) > 1:
                fp.write(''.join(header))
            fp.write(text)
Example #7
0
def manifestmerge(repo, p1, p2, pa, overwrite, partial):
    """
    Merge p1 and p2 with ancestor pa and generate merge action list

    overwrite = whether we clobber working files
    partial = function to filter file lists
    """

    def fmerge(f, f2, fa):
        """merge flags"""
        a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
        if m == n: # flags agree
            return m # unchanged
        if m and n and not a: # flags set, don't agree, differ from parent
            r = repo.ui.promptchoice(
                _(" conflicting flags for %s\n"
                  "(n)one, e(x)ec or sym(l)ink?") % f,
                (_("&None"), _("E&xec"), _("Sym&link")), 0)
            if r == 1:
                return "x" # Exec
            if r == 2:
                return "l" # Symlink
            return ""
        if m and m != a: # changed from a to m
            return m
        if n and n != a: # changed from a to n
            return n
        return '' # flag was cleared

    def act(msg, m, f, *args):
        repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
        action.append((f, m) + args)

    action, copy = [], {}

    if overwrite:
        pa = p1
    elif pa == p2: # backwards
        pa = p1.p1()
    elif pa and repo.ui.configbool("merge", "followcopies", True):
        dirs = repo.ui.configbool("merge", "followdirs", True)
        copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
        for of, fl in diverge.iteritems():
            act("divergent renames", "dr", of, fl)

    repo.ui.note(_("resolving manifests\n"))
    repo.ui.debug(" overwrite %s partial %s\n" % (overwrite, bool(partial)))
    repo.ui.debug(" ancestor %s local %s remote %s\n" % (pa, p1, p2))

    m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
    copied = set(copy.values())

    if '.hgsubstate' in m1:
        # check whether sub state is modified
        for s in p1.substate:
            if p1.sub(s).dirty():
                m1['.hgsubstate'] += "+"
                break

    # Compare manifests
    for f, n in m1.iteritems():
        if partial and not partial(f):
            continue
        if f in m2:
            rflags = fmerge(f, f, f)
            a = ma.get(f, nullid)
            if n == m2[f] or m2[f] == a: # same or local newer
                # is file locally modified or flags need changing?
                # dirstate flags may need to be made current
                if m1.flags(f) != rflags or n[20:]:
                    act("update permissions", "e", f, rflags)
            elif n == a: # remote newer
                act("remote is newer", "g", f, rflags)
            else: # both changed
                act("versions differ", "m", f, f, f, rflags, False)
        elif f in copied: # files we'll deal with on m2 side
            pass
        elif f in copy:
            f2 = copy[f]
            if f2 not in m2: # directory rename
                act("remote renamed directory to " + f2, "d",
                    f, None, f2, m1.flags(f))
            else: # case 2 A,B/B/B or case 4,21 A/B/B
                act("local copied/moved to " + f2, "m",
                    f, f2, f, fmerge(f, f2, f2), False)
        elif f in ma: # clean, a different, no remote
            if n != ma[f]:
                if repo.ui.promptchoice(
                    _(" local changed %s which remote deleted\n"
                      "use (c)hanged version or (d)elete?") % f,
                    (_("&Changed"), _("&Delete")), 0):
                    act("prompt delete", "r", f)
                else:
                    act("prompt keep", "a", f)
            elif n[20:] == "a": # added, no remote
                act("remote deleted", "f", f)
            elif n[20:] != "u":
                act("other deleted", "r", f)

    for f, n in m2.iteritems():
        if partial and not partial(f):
            continue
        if f in m1 or f in copied: # files already visited
            continue
        if f in copy:
            f2 = copy[f]
            if f2 not in m1: # directory rename
                act("local renamed directory to " + f2, "d",
                    None, f, f2, m2.flags(f))
            elif f2 in m2: # rename case 1, A/A,B/A
                act("remote copied to " + f, "m",
                    f2, f, f, fmerge(f2, f, f2), False)
            else: # case 3,20 A/B/A
                act("remote moved to " + f, "m",
                    f2, f, f, fmerge(f2, f, f2), True)
        elif f not in ma:
            act("remote created", "g", f, m2.flags(f))
        elif n != ma[f]:
            if repo.ui.promptchoice(
                _("remote changed %s which local deleted\n"
                  "use (c)hanged version or leave (d)eleted?") % f,
                (_("&Changed"), _("&Deleted")), 0) == 0:
                act("prompt recreating", "g", f, m2.flags(f))

    return action
Example #8
0
def diff(repo,
         node1=None,
         node2=None,
         files=None,
         match=util.always,
         fp=None,
         changes=None,
         opts=None):
    '''print diff of changes to files between two nodes, or node and
    working directory.

    if node1 is None, use first dirstate parent instead.
    if node2 is None, compare node1 with working directory.'''

    if opts is None:
        opts = mdiff.defaultopts
    if fp is None:
        fp = repo.ui

    if not node1:
        node1 = repo.dirstate.parents()[0]

    ccache = {}

    def getctx(r):
        if r not in ccache:
            ccache[r] = context.changectx(repo, r)
        return ccache[r]

    flcache = {}

    def getfilectx(f, ctx):
        flctx = ctx.filectx(f, filelog=flcache.get(f))
        if f not in flcache:
            flcache[f] = flctx._filelog
        return flctx

    # reading the data for node1 early allows it to play nicely
    # with repo.status and the revlog cache.
    ctx1 = context.changectx(repo, node1)
    # force manifest reading
    man1 = ctx1.manifest()
    date1 = util.datestr(ctx1.date())

    if not changes:
        changes = repo.status(node1, node2, files, match=match)[:5]
    modified, added, removed, deleted, unknown = changes

    if not modified and not added and not removed:
        return

    if node2:
        ctx2 = context.changectx(repo, node2)
        execf2 = ctx2.manifest().execf
        linkf2 = ctx2.manifest().linkf
    else:
        ctx2 = context.workingctx(repo)
        execf2 = util.execfunc(repo.root, None)
        linkf2 = util.linkfunc(repo.root, None)
        if execf2 is None:
            mc = ctx2.parents()[0].manifest().copy()
            execf2 = mc.execf
            linkf2 = mc.linkf

    if repo.ui.quiet:
        r = None
    else:
        hexfunc = repo.ui.debugflag and hex or short
        r = [hexfunc(node) for node in [node1, node2] if node]

    if opts.git:
        copy, diverge = copies.copies(repo, ctx1, ctx2, repo.changectx(nullid))
        for k, v in copy.items():
            copy[v] = k

    all = modified + added + removed
    all.sort()
    gone = {}

    for f in all:
        to = None
        tn = None
        dodiff = True
        header = []
        if f in man1:
            to = getfilectx(f, ctx1).data()
        if f not in removed:
            tn = getfilectx(f, ctx2).data()
        a, b = f, f
        if opts.git:

            def gitmode(x, l):
                return l and '120000' or (x and '100755' or '100644')

            def addmodehdr(header, omode, nmode):
                if omode != nmode:
                    header.append('old mode %s\n' % omode)
                    header.append('new mode %s\n' % nmode)

            if f in added:
                mode = gitmode(execf2(f), linkf2(f))
                if f in copy:
                    a = copy[f]
                    omode = gitmode(man1.execf(a), man1.linkf(a))
                    addmodehdr(header, omode, mode)
                    if a in removed and a not in gone:
                        op = 'rename'
                        gone[a] = 1
                    else:
                        op = 'copy'
                    header.append('%s from %s\n' % (op, a))
                    header.append('%s to %s\n' % (op, f))
                    to = getfilectx(a, ctx1).data()
                else:
                    header.append('new file mode %s\n' % mode)
                if util.binary(tn):
                    dodiff = 'binary'
            elif f in removed:
                # have we already reported a copy above?
                if f in copy and copy[f] in added and copy[copy[f]] == f:
                    dodiff = False
                else:
                    mode = gitmode(man1.execf(f), man1.linkf(f))
                    header.append('deleted file mode %s\n' % mode)
            else:
                omode = gitmode(man1.execf(f), man1.linkf(f))
                nmode = gitmode(execf2(f), linkf2(f))
                addmodehdr(header, omode, nmode)
                if util.binary(to) or util.binary(tn):
                    dodiff = 'binary'
            r = None
            header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
        if dodiff:
            if dodiff == 'binary':
                text = b85diff(to, tn)
            else:
                text = mdiff.unidiff(
                    to,
                    date1,
                    # ctx2 date may be dynamic
                    tn,
                    util.datestr(ctx2.date()),
                    a,
                    b,
                    r,
                    opts=opts)
            if text or len(header) > 1:
                fp.write(''.join(header))
            fp.write(text)
Example #9
0
def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None):
    """yields diff of changes to files between two nodes, or node and
    working directory.

    if node1 is None, use first dirstate parent instead.
    if node2 is None, compare node1 with working directory."""

    if opts is None:
        opts = mdiff.defaultopts

    if not node1:
        node1 = repo.dirstate.parents()[0]

    flcache = {}

    def getfilectx(f, ctx):
        flctx = ctx.filectx(f, filelog=flcache.get(f))
        if f not in flcache:
            flcache[f] = flctx._filelog
        return flctx

    ctx1 = repo[node1]
    ctx2 = repo[node2]

    if not changes:
        changes = repo.status(ctx1, ctx2, match=match)
    modified, added, removed = changes[:3]

    if not modified and not added and not removed:
        return

    date1 = util.datestr(ctx1.date())
    man1 = ctx1.manifest()

    if repo.ui.quiet:
        r = None
    else:
        hexfunc = repo.ui.debugflag and hex or short
        r = [hexfunc(node) for node in [node1, node2] if node]

    if opts.git:
        copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
        for k, v in copy.items():
            copy[v] = k

    gone = {}
    gitmode = {"l": "120000", "x": "100755", "": "100644"}

    for f in util.sort(modified + added + removed):
        to = None
        tn = None
        dodiff = True
        header = []
        if f in man1:
            to = getfilectx(f, ctx1).data()
        if f not in removed:
            tn = getfilectx(f, ctx2).data()
        a, b = f, f
        if opts.git:
            if f in added:
                mode = gitmode[ctx2.flags(f)]
                if f in copy:
                    a = copy[f]
                    omode = gitmode[man1.flags(a)]
                    _addmodehdr(header, omode, mode)
                    if a in removed and a not in gone:
                        op = "rename"
                        gone[a] = 1
                    else:
                        op = "copy"
                    header.append("%s from %s\n" % (op, a))
                    header.append("%s to %s\n" % (op, f))
                    to = getfilectx(a, ctx1).data()
                else:
                    header.append("new file mode %s\n" % mode)
                if util.binary(tn):
                    dodiff = "binary"
            elif f in removed:
                # have we already reported a copy above?
                if f in copy and copy[f] in added and copy[copy[f]] == f:
                    dodiff = False
                else:
                    header.append("deleted file mode %s\n" % gitmode[man1.flags(f)])
            else:
                omode = gitmode[man1.flags(f)]
                nmode = gitmode[ctx2.flags(f)]
                _addmodehdr(header, omode, nmode)
                if util.binary(to) or util.binary(tn):
                    dodiff = "binary"
            r = None
            header.insert(0, mdiff.diffline(r, a, b, opts))
        if dodiff:
            if dodiff == "binary":
                text = b85diff(to, tn)
            else:
                text = mdiff.unidiff(
                    to,
                    date1,
                    # ctx2 date may be dynamic
                    tn,
                    util.datestr(ctx2.date()),
                    a,
                    b,
                    r,
                    opts=opts,
                )
            if header and (text or len(header) > 1):
                yield "".join(header)
            if text:
                yield text
Example #10
0
def manifestmerge(repo, p1, p2, pa, overwrite, partial):
    """
    Merge p1 and p2 with ancestor ma and generate merge action list

    overwrite = whether we clobber working files
    partial = function to filter file lists
    """

    repo.ui.note(_("resolving manifests\n"))
    repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
    repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))

    m1 = p1.manifest()
    m2 = p2.manifest()
    ma = pa.manifest()
    backwards = (pa == p2)
    action = []
    copy, copied, diverge = {}, {}, {}

    def fmerge(f, f2=None, fa=None):
        """merge flags"""
        if not f2:
            f2 = f
            fa = f
        a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
        if m == n:  # flags agree
            return m  # unchanged
        if m and n:  # flags are set but don't agree
            if not a:  # both differ from parent
                r = repo.ui.prompt(
                    _(" conflicting flags for %s\n"
                      "(n)one, e(x)ec or sym(l)ink?") % f, "[nxl]", "n")
                return r != "n" and r or ''
            if m == a:
                return n  # changed from m to n
            return m  # changed from n to m
        if m and m != a:  # changed from a to m
            return m
        if n and n != a:  # changed from a to n
            return n
        return ''  # flag was cleared

    def act(msg, m, f, *args):
        repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
        action.append((f, m) + args)

    if pa and not (backwards or overwrite):
        if repo.ui.configbool("merge", "followcopies", True):
            dirs = repo.ui.configbool("merge", "followdirs", True)
            copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
        copied = dict.fromkeys(copy.values())
        for of, fl in diverge.items():
            act("divergent renames", "dr", of, fl)

    # Compare manifests
    for f, n in m1.iteritems():
        if partial and not partial(f):
            continue
        if f in m2:
            if overwrite or backwards:
                rflags = m2.flags(f)
            else:
                rflags = fmerge(f)
            # are files different?
            if n != m2[f]:
                a = ma.get(f, nullid)
                # are we clobbering?
                if overwrite:
                    act("clobbering", "g", f, rflags)
                # or are we going back in time and clean?
                elif backwards and not n[20:]:
                    act("reverting", "g", f, rflags)
                # are both different from the ancestor?
                elif n != a and m2[f] != a:
                    act("versions differ", "m", f, f, f, rflags, False)
                # is remote's version newer?
                elif m2[f] != a:
                    act("remote is newer", "g", f, rflags)
                # local is newer, not overwrite, check mode bits
                elif m1.flags(f) != rflags:
                    act("update permissions", "e", f, rflags)
            # contents same, check mode bits
            elif m1.flags(f) != rflags:
                act("update permissions", "e", f, rflags)
        elif f in copied:
            continue
        elif f in copy:
            f2 = copy[f]
            if f2 not in m2:  # directory rename
                act("remote renamed directory to " + f2, "d", f, None, f2,
                    m1.flags(f))
            elif f2 in m1:  # case 2 A,B/B/B
                act("local copied to " + f2, "m", f, f2, f, fmerge(f, f2, f2),
                    False)
            else:  # case 4,21 A/B/B
                act("local moved to " + f2, "m", f, f2, f, fmerge(f, f2, f2),
                    False)
        elif f in ma:
            if n != ma[f] and not overwrite:
                if repo.ui.prompt(
                        _(" local changed %s which remote deleted\n"
                          "use (c)hanged version or (d)elete?") % f, _("[cd]"),
                        _("c")) == _("d"):
                    act("prompt delete", "r", f)
            else:
                act("other deleted", "r", f)
        else:
            # file is created on branch or in working directory
            if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
                act("remote deleted", "r", f)

    for f, n in m2.iteritems():
        if partial and not partial(f):
            continue
        if f in m1:
            continue
        if f in copied:
            continue
        if f in copy:
            f2 = copy[f]
            if f2 not in m1:  # directory rename
                act("local renamed directory to " + f2, "d", None, f, f2,
                    m2.flags(f))
            elif f2 in m2:  # rename case 1, A/A,B/A
                act("remote copied to " + f, "m", f2, f, f, fmerge(f2, f, f2),
                    False)
            else:  # case 3,20 A/B/A
                act("remote moved to " + f, "m", f2, f, f, fmerge(f2, f, f2),
                    True)
        elif f in ma:
            if overwrite or backwards:
                act("recreating", "g", f, m2.flags(f))
            elif n != ma[f]:
                if repo.ui.prompt(
                        _("remote changed %s which local deleted\n"
                          "use (c)hanged version or leave (d)eleted?") % f,
                        _("[cd]"), _("c")) == _("c"):
                    act("prompt recreating", "g", f, m2.flags(f))
        else:
            act("remote created", "g", f, m2.flags(f))

    return action