##// END OF EJS Templates
hgweb: remove some legacy code
hgweb: remove some legacy code

File last commit:

r5857:c704b038 default
r5886:dd1998dd default
Show More
merge.py
687 lines | 22.8 KiB | text/x-python | PythonLexer
Matt Mackall
Move merge code to its own module...
r2775 # merge.py - directory-level update/merge handling for Mercurial
#
Thomas Arendsen Hein
Updated copyright notices and add "and others" to "hg version"
r4635 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
Matt Mackall
Move merge code to its own module...
r2775 #
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
from node import *
Matt Mackall
Simplify i18n imports
r3891 from i18n import _
Alexis S. L. Carvalho
merge: fix a copy detection bug (issue672)...
r5096 import errno, util, os, tempfile, context, heapq
Matt Mackall
Move merge code to its own module...
r2775
Matt Mackall
merge: do early copy to deal with issue636...
r5042 def filemerge(repo, fw, fd, fo, wctx, mctx):
Matt Mackall
merge: extend file merge function for renames
r3211 """perform a 3-way merge in the working directory
Matt Mackall
Move merge code to its own module...
r2775
Matt Mackall
merge: do early copy to deal with issue636...
r5042 fw = original filename in the working directory
fd = destination filename in the working directory
Matt Mackall
merge: extend file merge function for renames
r3211 fo = filename in other parent
Matt Mackall
merge: pass contexts to applyupdates
r3297 wctx, mctx = working and merge changecontexts
Matt Mackall
merge: extend file merge function for renames
r3211 """
def temp(prefix, ctx):
pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
Matt Mackall
Move merge code to its own module...
r2775 (fd, name) = tempfile.mkstemp(prefix=pre)
Matt Mackall
replace filehandle version of wwrite with wwritedata
r4005 data = repo.wwritedata(ctx.path(), ctx.data())
Matt Mackall
Move merge code to its own module...
r2775 f = os.fdopen(fd, "wb")
Matt Mackall
replace filehandle version of wwrite with wwritedata
r4005 f.write(data)
Matt Mackall
Move merge code to its own module...
r2775 f.close()
return name
Matt Mackall
filemerge: use contexts rather than my and other
r3299 fcm = wctx.filectx(fw)
Matt Mackall
merge: do early copy to deal with issue636...
r5042 fcmdata = wctx.filectx(fd).data()
Matt Mackall
filemerge: use contexts rather than my and other
r3299 fco = mctx.filectx(fo)
Matt Mackall
merge: shortcircuit filemerge for identical files...
r3311
Matt Mackall
merge: do early copy to deal with issue636...
r5042 if not fco.cmp(fcmdata): # files identical?
Matt Mackall
merge: if filemerge skips merge, report as updated
r3400 return None
Matt Mackall
merge: shortcircuit filemerge for identical files...
r3311
Matt Mackall
merge: extend file merge function for renames
r3211 fca = fcm.ancestor(fco)
if not fca:
Thomas Arendsen Hein
Define and use nullrev (revision of nullid) instead of -1.
r3578 fca = repo.filectx(fw, fileid=nullrev)
Matt Mackall
merge: do early copy to deal with issue636...
r5042 a = repo.wjoin(fd)
Matt Mackall
merge: extend file merge function for renames
r3211 b = temp("base", fca)
c = temp("other", fco)
Matt Mackall
Move merge code to its own module...
r2775
Matt Mackall
merge: shortcircuit filemerge for identical files...
r3311 if fw != fo:
repo.ui.status(_("merging %s and %s\n") % (fw, fo))
else:
repo.ui.status(_("merging %s\n") % fw)
Matt Mackall
merge: extend file merge function for renames
r3211 repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcm, fco, fca))
Matt Mackall
Move merge code to its own module...
r2775
cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge")
or "hgmerge")
r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root,
Matt Mackall
merge: do early copy to deal with issue636...
r5042 environ={'HG_FILE': fd,
Matt Mackall
merge: pass contexts to applyupdates
r3297 'HG_MY_NODE': str(wctx.parents()[0]),
Patrick Mezard
merge: provide *_ISLINK environment vars to merge helper...
r5390 'HG_OTHER_NODE': str(mctx),
'HG_MY_ISLINK': fcm.islink(),
'HG_OTHER_ISLINK': fco.islink(),
'HG_BASE_ISLINK': fca.islink(),})
Matt Mackall
Move merge code to its own module...
r2775 if r:
Matt Mackall
merge: do early copy to deal with issue636...
r5042 repo.ui.warn(_("merging %s failed!\n") % fd)
Matt Mackall
Move merge code to its own module...
r2775
os.unlink(b)
os.unlink(c)
return r
Matt Mackall
merge: use contexts in checkunknown and forgetremoved
r3312 def checkunknown(wctx, mctx):
Matt Mackall
merge: update some docstrings
r3315 "check for collisions between unknown files and files in mctx"
Matt Mackall
merge: use contexts in checkunknown and forgetremoved
r3312 man = mctx.manifest()
Matt Mackall
merge: use new working context object in update
r3218 for f in wctx.unknown():
Matt Mackall
merge: use contexts in checkunknown and forgetremoved
r3312 if f in man:
if mctx.filectx(f).cmp(wctx.filectx(f).data()):
Thomas Arendsen Hein
Fix misleading error and prompts during update/merge (issue556)
r5670 raise util.Abort(_("untracked file in working directory differs"
" from file in requested revision: '%s'")
% f)
Matt Mackall
merge: pull manifest checks and updates into separate functions
r3107
Matt Mackall
imported patch collision
r3785 def checkcollision(mctx):
"check for case folding collisions in the destination context"
folded = {}
for fn in mctx.manifest():
fold = fn.lower()
if fold in folded:
raise util.Abort(_("case-folding collision between %s and %s")
% (fn, folded[fold]))
folded[fold] = fn
Matt Mackall
merge: use contexts in checkunknown and forgetremoved
r3312 def forgetremoved(wctx, mctx):
Matt Mackall
merge: pull manifest checks and updates into separate functions
r3107 """
Forget removed files
If we're jumping between revisions (as opposed to merging), and if
neither the working directory nor the target rev has the file,
then we need to remove it from the dirstate, to prevent the
dirstate from listing the file when it is no longer in the
manifest.
"""
action = []
Matt Mackall
merge: use contexts in checkunknown and forgetremoved
r3312 man = mctx.manifest()
Matt Mackall
merge: use new working context object in update
r3218 for f in wctx.deleted() + wctx.removed():
Matt Mackall
merge: use contexts in checkunknown and forgetremoved
r3312 if f not in man:
Matt Mackall
merge: pull manifest checks and updates into separate functions
r3107 action.append((f, "f"))
return action
Matt Mackall
merge: turn followcopies on by default
r3371 def findcopies(repo, m1, m2, ma, limit):
Matt Mackall
Add core copy detection algorithm...
r3153 """
Find moves and copies between m1 and m2 back to limit linkrev
"""
Matt Mackall
merge: reorganize some hunks in findcopies
r4400 def nonoverlap(d1, d2, d3):
"Return list of elements in d1 not in d2 or d3"
l = [d for d in d1 if d not in d3 and d not in d2]
l.sort()
return l
Matt Mackall
merge: fix a bug detecting directory moves...
r4397 def dirname(f):
s = f.rfind("/")
if s == -1:
return ""
return f[:s]
def dirs(files):
d = {}
for f in files:
f = dirname(f)
while f not in d:
d[f] = True
f = dirname(f)
return d
Matt Mackall
merge: fix spurious merges for copies in linear updates...
r4416 wctx = repo.workingctx()
def makectx(f, n):
if len(n) == 20:
return repo.filectx(f, fileid=n)
return wctx.filectx(f)
ctx = util.cachefunc(makectx)
Matt Mackall
merge: pull findcopies helpers inside, refactor checkpair to checkcopies
r3732 def findold(fctx):
"find files that path was copied from, back to linkrev limit"
old = {}
Matt Mackall
merge: fix quadratic behavior in find-copies
r4350 seen = {}
Matt Mackall
merge: pull findcopies helpers inside, refactor checkpair to checkcopies
r3732 orig = fctx.path()
visit = [fctx]
while visit:
fc = visit.pop()
Matt Mackall
merge: fix quadratic behavior in find-copies
r4350 s = str(fc)
if s in seen:
continue
seen[s] = 1
Matt Mackall
Fix copy detection corner case...
r3875 if fc.path() != orig and fc.path() not in old:
old[fc.path()] = 1
Matt Mackall
merge: pull findcopies helpers inside, refactor checkpair to checkcopies
r3732 if fc.rev() < limit:
continue
visit += fc.parents()
old = old.keys()
old.sort()
return old
Matt Mackall
merge: reorganize some hunks in findcopies
r4400 copy = {}
fullcopy = {}
Matt Mackall
merge: warn user about divergent renames
r4674 diverge = {}
Matt Mackall
merge: pull findcopies helpers inside, refactor checkpair to checkcopies
r3732
Matt Mackall
merge: fix unnecessary rename merges on linear update (issue631)...
r4884 def checkcopies(c, man, aman):
Matt Mackall
merge: pull findcopies helpers inside, refactor checkpair to checkcopies
r3732 '''check possible copies for filectx c'''
for of in findold(c):
Matt Mackall
merge: warn user about divergent renames
r4674 fullcopy[c.path()] = of # remember for dir rename detection
Matt Mackall
merge: clarify the findcopies code
r4396 if of not in man: # original file not in other manifest?
Matt Mackall
merge: warn user about divergent renames
r4674 if of in ma:
diverge.setdefault(of, []).append(c.path())
Matt Mackall
merge: fix a bug where copies were ignored
r4304 continue
Matt Mackall
merge: fix unnecessary rename merges on linear update (issue631)...
r4884 # if the original file is unchanged on the other branch,
# no merge needed
if man[of] == aman.get(of):
continue
Matt Mackall
merge: pull findcopies helpers inside, refactor checkpair to checkcopies
r3732 c2 = ctx(of, man[of])
ca = c.ancestor(c2)
Matt Mackall
merge: clarify the findcopies code
r4396 if not ca: # unrelated?
Matt Mackall
merge: fix a bug where copies were ignored
r4304 continue
Matt Mackall
merge: clarify the findcopies code
r4396 # named changed on only one side?
Matt Mackall
merge: pull findcopies helpers inside, refactor checkpair to checkcopies
r3732 if ca.path() == c.path() or ca.path() == c2.path():
Matt Mackall
findcopies: fix rename bug...
r5433 if c == ca and c2 == ca: # no merge needed, ignore copy
Matt Mackall
merge: fix a bug where copies were ignored
r4304 continue
Matt Mackall
merge: pull findcopies helpers inside, refactor checkpair to checkcopies
r3732 copy[c.path()] = of
Matt Mackall
merge: turn followcopies on by default
r3371 if not repo.ui.configbool("merge", "followcopies", True):
Matt Mackall
merge: warn user about divergent renames
r4674 return {}, {}
Matt Mackall
merge: add rename following...
r3249
Matt Mackall
findcopies: shortcut for empty working dir
r3160 # avoid silly behavior for update from empty dir
Matt Mackall
merge: move check for empty ancestor into findcopies
r3731 if not m1 or not m2 or not ma:
Matt Mackall
merge: warn user about divergent renames
r4674 return {}, {}
Matt Mackall
findcopies: shortcut for empty working dir
r3160
Matt Mackall
merge: add debug diagnostics for findcopies
r5371 repo.ui.debug(_(" searching for copies back to rev %d\n") % limit)
Matt Mackall
merge: turn followcopies on by default
r3371 u1 = nonoverlap(m1, m2, ma)
u2 = nonoverlap(m2, m1, ma)
Matt Mackall
Add core copy detection algorithm...
r3153
Matt Mackall
merge: add debug diagnostics for findcopies
r5371 if u1:
repo.ui.debug(_(" unmatched files in local:\n %s\n")
% "\n ".join(u1))
if u2:
repo.ui.debug(_(" unmatched files in other:\n %s\n")
% "\n ".join(u2))
Matt Mackall
Add core copy detection algorithm...
r3153 for f in u1:
Matt Mackall
merge: fix unnecessary rename merges on linear update (issue631)...
r4884 checkcopies(ctx(f, m1[f]), m2, ma)
Matt Mackall
Add core copy detection algorithm...
r3153
for f in u2:
Matt Mackall
merge: fix unnecessary rename merges on linear update (issue631)...
r4884 checkcopies(ctx(f, m2[f]), m1, ma)
Matt Mackall
Add core copy detection algorithm...
r3153
Matt Mackall
merge: add a bit more sanity to divergent copy checks
r5857 diverge2 = {}
Matt Mackall
merge: warn user about divergent renames
r4674 for of, fl in diverge.items():
Matt Mackall
merge: add a bit more sanity to divergent copy checks
r5857 if len(fl) == 1:
del diverge[of] # not actually divergent
else:
diverge2.update(dict.fromkeys(fl)) # reverse map for below
Matt Mackall
merge: warn user about divergent renames
r4674
Matt Mackall
merge: add debug diagnostics for findcopies
r5371 if fullcopy:
repo.ui.debug(_(" all copies found (* = to merge, ! = divergent):\n"))
for f in fullcopy:
note = ""
if f in copy: note += "*"
Matt Mackall
merge: add a bit more sanity to divergent copy checks
r5857 if f in diverge2: note += "!"
Matt Mackall
merge: add debug diagnostics for findcopies
r5371 repo.ui.debug(_(" %s -> %s %s\n") % (f, fullcopy[f], note))
Matt Mackall
merge: add a bit more sanity to divergent copy checks
r5857 del diverge2
Matt Mackall
merge: handle directory renames...
r3733 if not fullcopy or not repo.ui.configbool("merge", "followdirs", True):
Matt Mackall
merge: warn user about divergent renames
r4674 return copy, diverge
Matt Mackall
merge: handle directory renames...
r3733
Matt Mackall
merge: add debug diagnostics for findcopies
r5371 repo.ui.debug(_(" checking for directory renames\n"))
Matt Mackall
merge: handle directory renames...
r3733 # generate a directory move map
d1, d2 = dirs(m1), dirs(m2)
invalid = {}
dirmove = {}
Matt Mackall
merge: clarify the findcopies code
r4396 # examine each file copy for a potential directory move, which is
# when all the files in a directory are moved to a new directory
Matt Mackall
merge: handle directory renames...
r3733 for dst, src in fullcopy.items():
Matt Mackall
merge: fix a bug detecting directory moves...
r4397 dsrc, ddst = dirname(src), dirname(dst)
Matt Mackall
merge: handle directory renames...
r3733 if dsrc in invalid:
Matt Mackall
merge: clarify the findcopies code
r4396 # already seen to be uninteresting
Matt Mackall
merge: handle directory renames...
r3733 continue
Matt Mackall
merge: clarify the findcopies code
r4396 elif dsrc in d1 and ddst in d1:
# directory wasn't entirely moved locally
invalid[dsrc] = True
elif dsrc in d2 and ddst in d2:
# directory wasn't entirely moved remotely
Matt Mackall
merge: handle directory renames...
r3733 invalid[dsrc] = True
elif dsrc in dirmove and dirmove[dsrc] != ddst:
Matt Mackall
merge: clarify the findcopies code
r4396 # files from the same directory moved to two different places
Matt Mackall
merge: handle directory renames...
r3733 invalid[dsrc] = True
else:
Matt Mackall
merge: clarify the findcopies code
r4396 # looks good so far
Matt Mackall
merge: fix renaming of subdirectories under renamed directories
r4115 dirmove[dsrc + "/"] = ddst + "/"
Matt Mackall
merge: handle directory renames...
r3733
Matt Mackall
merge: expand and simplify the invalid handling for directory moves
r4398 for i in invalid:
if i in dirmove:
del dirmove[i]
Matt Mackall
merge: handle directory renames...
r3733 del d1, d2, invalid
if not dirmove:
Matt Mackall
merge: warn user about divergent renames
r4674 return copy, diverge
Matt Mackall
merge: handle directory renames...
r3733
Matt Mackall
merge: add debug diagnostics for findcopies
r5371 for d in dirmove:
repo.ui.debug(_(" dir %s -> %s\n") % (d, dirmove[d]))
Matt Mackall
merge: clarify the findcopies code
r4396 # check unaccounted nonoverlapping files against directory moves
Matt Mackall
merge: handle directory renames...
r3733 for f in u1 + u2:
if f not in fullcopy:
Matt Mackall
merge: fix renaming of subdirectories under renamed directories
r4115 for d in dirmove:
if f.startswith(d):
Matt Mackall
merge: clarify the findcopies code
r4396 # new file added in a directory that was moved, move it
Matt Mackall
merge: fix renaming of subdirectories under renamed directories
r4115 copy[f] = dirmove[d] + f[len(d):]
Matt Mackall
merge: add debug diagnostics for findcopies
r5371 repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f]))
Matt Mackall
merge: fix renaming of subdirectories under renamed directories
r4115 break
Matt Mackall
merge: handle directory renames...
r3733
Matt Mackall
merge: warn user about divergent renames
r4674 return copy, diverge
Matt Mackall
Add core copy detection algorithm...
r3153
Alexis S. L. Carvalho
merge: fix a copy detection bug (issue672)...
r5096 def symmetricdifference(repo, rev1, rev2):
"""symmetric difference of the sets of ancestors of rev1 and rev2
Matt Mackall
findcopies: fix rename bug...
r5433
Alexis S. L. Carvalho
merge: fix a copy detection bug (issue672)...
r5096 I.e. revisions that are ancestors of rev1 or rev2, but not both.
"""
# basic idea:
# - mark rev1 and rev2 with different colors
# - walk the graph in topological order with the help of a heap;
# for each revision r:
# - if r has only one color, we want to return it
# - add colors[r] to its parents
#
# We keep track of the number of revisions in the heap that
# we may be interested in. We stop walking the graph as soon
# as this number reaches 0.
WHITE = 1
BLACK = 2
ALLCOLORS = WHITE | BLACK
colors = {rev1: WHITE, rev2: BLACK}
cl = repo.changelog
visit = [-rev1, -rev2]
heapq.heapify(visit)
n_wanted = len(visit)
ret = []
while n_wanted:
r = -heapq.heappop(visit)
wanted = colors[r] != ALLCOLORS
n_wanted -= wanted
if wanted:
ret.append(r)
for p in cl.parentrevs(r):
if p == nullrev:
continue
if p not in colors:
# first time we see p; add it to visit
n_wanted += wanted
colors[p] = colors[r]
heapq.heappush(visit, -p)
elif colors[p] != ALLCOLORS and colors[p] != colors[r]:
# at first we thought we wanted p, but now
# we know we don't really want it
n_wanted -= 1
colors[p] |= colors[r]
del colors[r]
return ret
Matt Mackall
merge: use contexts for manifestmerge...
r3295 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 """
Matt Mackall
merge: update some docstrings
r3315 Merge p1 and p2 with ancestor ma and generate merge action list
overwrite = whether we clobber working files
partial = function to filter file lists
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 """
Matt Mackall
merge: various tidying...
r3314 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))
Matt Mackall
merge: use contexts for manifestmerge...
r3295 m1 = p1.manifest()
m2 = p2.manifest()
ma = pa.manifest()
backwards = (pa == p2)
Matt Mackall
merge: various tidying...
r3314 action = []
copy = {}
Matt Mackall
merge: warn user about divergent renames
r4674 diverge = {}
Matt Mackall
merge: use contexts for manifestmerge...
r3295
Matt Mackall
merge: add rename following...
r3249 def fmerge(f, f2=None, fa=None):
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 """merge flags"""
Matt Mackall
merge: add rename following...
r3249 if not f2:
f2 = f
fa = f
Matt Mackall
merge: add flag merging intelligence...
r5708 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
Matt Mackall
merge: simplify exec flag handling
r3118
Matt Mackall
merge: swap file and mode args for act()
r3307 def act(msg, m, f, *args):
Matt Mackall
merge: use contexts for manifestmerge...
r3295 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
Matt Mackall
merge: simplify actions with helper function
r3121 action.append((f, m) + args)
Matt Mackall
merge: move check for empty ancestor into findcopies
r3731 if not (backwards or overwrite):
Alexis S. L. Carvalho
merge: fix a copy detection bug (issue672)...
r5096 rev1 = p1.rev()
if rev1 is None:
# p1 is a workingctx
rev1 = p1.parents()[0].rev()
limit = min(symmetricdifference(repo, rev1, p2.rev()))
copy, diverge = findcopies(repo, m1, m2, ma, limit)
Matt Mackall
merge: warn user about divergent renames
r4674
for of, fl in diverge.items():
act("divergent renames", "dr", of, fl)
Matt Mackall
merge: only store one direction of copies in the copy map...
r3730 copied = dict.fromkeys(copy.values())
Matt Mackall
merge: use contexts for manifestmerge...
r3295
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 # Compare manifests
for f, n in m1.iteritems():
Matt Mackall
merge: reduce manifest copying
r3248 if partial and not partial(f):
continue
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 if f in m2:
Matt Mackall
merge: add flag merging intelligence...
r5708 if overwrite or backwards:
rflags = m2.flags(f)
else:
rflags = fmerge(f)
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 # are files different?
if n != m2[f]:
a = ma.get(f, nullid)
Matt Mackall
merge: simplify merge tests, fix exec flag bug...
r5700 # are we clobbering?
if overwrite:
Matt Mackall
merge: add flag merging intelligence...
r5708 act("clobbering", "g", f, rflags)
Matt Mackall
merge: simplify merge tests, fix exec flag bug...
r5700 # or are we going back in time and clean?
elif backwards and not n[20:]:
Matt Mackall
merge: add flag merging intelligence...
r5708 act("reverting", "g", f, rflags)
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 # are both different from the ancestor?
Matt Mackall
merge: simplify merge tests, fix exec flag bug...
r5700 elif n != a and m2[f] != a:
Matt Mackall
merge: add flag merging intelligence...
r5708 act("versions differ", "m", f, f, f, rflags, False)
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 # is remote's version newer?
Matt Mackall
merge: simplify merge tests, fix exec flag bug...
r5700 elif m2[f] != a:
Matt Mackall
merge: add flag merging intelligence...
r5708 act("remote is newer", "g", f, rflags)
Matt Mackall
merge: eliminate confusing queued variable
r3113 # local is newer, not overwrite, check mode bits
Matt Mackall
merge: add flag merging intelligence...
r5708 elif m1.flags(f) != rflags:
act("update permissions", "e", f, rflags)
Matt Mackall
merge: eliminate confusing queued variable
r3113 # contents same, check mode bits
Matt Mackall
merge: add flag merging intelligence...
r5708 elif m1.flags(f) != rflags:
act("update permissions", "e", f, rflags)
Matt Mackall
merge: only store one direction of copies in the copy map...
r3730 elif f in copied:
continue
Matt Mackall
merge: add rename following...
r3249 elif f in copy:
f2 = copy[f]
Matt Mackall
merge: handle directory renames...
r3733 if f2 not in m2: # directory rename
act("remote renamed directory to " + f2, "d",
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 f, None, f2, m1.flags(f))
Matt Mackall
merge: handle directory renames...
r3733 elif f2 in m1: # case 2 A,B/B/B
Matt Mackall
merge: only store one direction of copies in the copy map...
r3730 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)
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 elif f in ma:
Matt Mackall
merge: simplify tests for local changed/remote deleted
r3117 if n != ma[f] and not overwrite:
Matt Mackall
merge: use contexts for manifestmerge...
r3295 if repo.ui.prompt(
Thomas Arendsen Hein
Fix misleading error and prompts during update/merge (issue556)
r5670 _(" local changed %s which remote deleted\n"
"use (c)hanged version or (d)elete?") % f,
_("[cd]"), _("c")) == _("d"):
Matt Mackall
merge: swap file and mode args for act()
r3307 act("prompt delete", "r", f)
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 else:
Matt Mackall
merge: swap file and mode args for act()
r3307 act("other deleted", "r", f)
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 else:
# file is created on branch or in working directory
Matt Mackall
merge: simplify local created logic
r3120 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
Matt Mackall
merge: swap file and mode args for act()
r3307 act("remote deleted", "r", f)
Matt Mackall
merge: pull manifest comparison out into separate function
r3105
for f, n in m2.iteritems():
Matt Mackall
merge: reduce manifest copying
r3248 if partial and not partial(f):
continue
if f in m1:
continue
Matt Mackall
merge: add copied hash to simplify copy logic
r3729 if f in copied:
continue
Matt Mackall
merge: add rename following...
r3249 if f in copy:
f2 = copy[f]
Matt Mackall
merge: handle directory renames...
r3733 if f2 not in m1: # directory rename
act("local renamed directory to " + f2, "d",
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 None, f, f2, m2.flags(f))
Matt Mackall
merge: handle directory renames...
r3733 elif f2 in m2: # rename case 1, A/A,B/A
Matt Mackall
merge: only store one direction of copies in the copy map...
r3730 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)
Matt Mackall
merge: add rename following...
r3249 elif f in ma:
Matt Mackall
merge: more simplification of m2 manifest scanning
r3116 if overwrite or backwards:
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 act("recreating", "g", f, m2.flags(f))
Matt Mackall
merge: more simplification of m2 manifest scanning
r3116 elif n != ma[f]:
Matt Mackall
merge: use contexts for manifestmerge...
r3295 if repo.ui.prompt(
Thomas Arendsen Hein
Fix misleading error and prompts during update/merge (issue556)
r5670 _("remote changed %s which local deleted\n"
"use (c)hanged version or leave (d)eleted?") % f,
_("[cd]"), _("c")) == _("c"):
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 act("prompt recreating", "g", f, m2.flags(f))
Matt Mackall
merge: reorder tests on m2 items in manifestmerge
r3115 else:
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 act("remote created", "g", f, m2.flags(f))
Matt Mackall
merge: pull manifest comparison out into separate function
r3105
return action
Matt Mackall
merge: pass contexts to applyupdates
r3297 def applyupdates(repo, action, wctx, mctx):
Matt Mackall
merge: update some docstrings
r3315 "apply the merge action list to the working directory"
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 updated, merged, removed, unresolved = 0, 0, 0, 0
action.sort()
Matt Mackall
merge: do early copy to deal with issue636...
r5042 # prescan for copy/renames
for a in action:
f, m = a[:2]
if m == 'm': # merge
f2, fd, flags, move = a[2:]
if f != fd:
repo.ui.debug(_("copying %s to %s\n") % (f, fd))
repo.wwrite(fd, repo.wread(f), flags)
Bryan O'Sullivan
Make audit_path more stringent....
r5158 audit_path = util.path_auditor(repo.root)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 for a in action:
f, m = a[:2]
Matt Mackall
merge: handle directory renames...
r3733 if f and f[0] == "/":
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 continue
if m == "r": # remove
repo.ui.note(_("removing %s\n") % f)
Bryan O'Sullivan
Make audit_path more stringent....
r5158 audit_path(f)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 try:
util.unlink(repo.wjoin(f))
except OSError, inst:
if inst.errno != errno.ENOENT:
repo.ui.warn(_("update failed to remove %s: %s!\n") %
(f, inst.strerror))
Thomas Arendsen Hein
white space and line break cleanups
r3673 removed += 1
Matt Mackall
merge: unify merge and copy actions
r3308 elif m == "m": # merge
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 f2, fd, flags, move = a[2:]
Matt Mackall
merge: do early copy to deal with issue636...
r5042 r = filemerge(repo, f, fd, f2, wctx, mctx)
Matt Mackall
merge: if filemerge skips merge, report as updated
r3400 if r > 0:
Matt Mackall
merge: add rename following...
r3249 unresolved += 1
Matt Mackall
merge: pull file copy/move out of filemerge
r3309 else:
Matt Mackall
merge: if filemerge skips merge, report as updated
r3400 if r is None:
updated += 1
else:
merged += 1
Matt Mackall
merge: use util.set_flags
r5704 util.set_flags(repo.wjoin(fd), flags)
Matt Mackall
merge: avoid double deletion mentioned in issue636
r5059 if f != fd and move and util.lexists(repo.wjoin(f)):
Matt Mackall
merge: do early copy to deal with issue636...
r5042 repo.ui.debug(_("removing %s\n") % f)
os.unlink(repo.wjoin(f))
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 elif m == "g": # get
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 flags = a[2]
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 repo.ui.note(_("getting %s\n") % f)
Matt Mackall
merge: eliminate nodes from action list...
r3303 t = mctx.filectx(f).data()
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 repo.wwrite(f, t, flags)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 updated += 1
Matt Mackall
merge: handle directory renames...
r3733 elif m == "d": # directory rename
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 f2, fd, flags = a[2:]
Matt Mackall
merge: handle directory renames...
r3733 if f:
repo.ui.note(_("moving %s to %s\n") % (f, fd))
t = wctx.filectx(f).data()
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 repo.wwrite(fd, t, flags)
Matt Mackall
merge: handle directory renames...
r3733 util.unlink(repo.wjoin(f))
if f2:
repo.ui.note(_("getting %s to %s\n") % (f2, fd))
t = mctx.filectx(f2).data()
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 repo.wwrite(fd, t, flags)
Matt Mackall
merge: handle directory renames...
r3733 updated += 1
Matt Mackall
merge: warn user about divergent renames
r4674 elif m == "dr": # divergent renames
fl = a[2]
repo.ui.warn("warning: detected divergent renames of %s to:\n" % f)
for nf in fl:
repo.ui.warn(" %s\n" % nf)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 elif m == "e": # exec
Matt Mackall
symlinks: minimal support for symlinks in merge/update...
r4007 flags = a[2]
Matt Mackall
merge: use util.set_flags
r5704 util.set_flags(repo.wjoin(f), flags)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111
return updated, merged, removed, unresolved
Matt Mackall
merge: update dirstate correctly for non-branchmerge updates...
r3372 def recordupdates(repo, action, branchmerge):
Matt Mackall
merge: update some docstrings
r3315 "record merge actions to the dirstate"
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 for a in action:
f, m = a[:2]
if m == "r": # remove
if branchmerge:
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.remove(f)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 else:
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.forget(f)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 elif m == "f": # forget
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.forget(f)
Matt Mackall
merge: don't forget to update the dirstate for exec bit changes
r4997 elif m in "ge": # get or exec change
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 if branchmerge:
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.normaldirty(f)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 else:
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.normal(f)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111 elif m == "m": # merge
Matt Mackall
merge: eliminate nodes from action list...
r3303 f2, fd, flag, move = a[2:]
Matt Mackall
merge: fixes for merge+rename...
r3251 if branchmerge:
# We've done a branch merge, mark this file as merged
# so that we properly record the merger later
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.merge(fd)
Matt Mackall
merge: update dirstate correctly for non-branchmerge updates...
r3372 if f != f2: # copy/rename
if move:
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.remove(f)
Matt Mackall
merge: update dirstate correctly for non-branchmerge updates...
r3372 if f != fd:
repo.dirstate.copy(f, fd)
else:
repo.dirstate.copy(f2, fd)
Matt Mackall
merge: fixes for merge+rename...
r3251 else:
# We've update-merged a locally modified file, so
# we set the dirstate to emulate a normal checkout
# of that file some time in the past. Thus our
# merge will appear as a normal local file
# modification.
Alexis S. L. Carvalho
merge: forcefully mark files that we get from the second parent as dirty...
r5210 repo.dirstate.normallookup(fd)
Matt Mackall
merge: unify merge and copy actions
r3308 if move:
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.forget(f)
Matt Mackall
merge: handle directory renames...
r3733 elif m == "d": # directory rename
f2, fd, flag = a[2:]
Matt Mackall
merge: fix adding untracked files on directory rename (issue612)...
r4819 if not f2 and f not in repo.dirstate:
# untracked file moved
continue
Matt Mackall
merge: handle directory renames...
r3733 if branchmerge:
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.add(fd)
Matt Mackall
merge: handle directory renames...
r3733 if f:
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.remove(f)
Matt Mackall
merge: handle directory renames...
r3733 repo.dirstate.copy(f, fd)
if f2:
repo.dirstate.copy(f2, fd)
else:
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.normal(fd)
Matt Mackall
merge: handle directory renames...
r3733 if f:
Matt Mackall
dirstate: break update into separate functions
r4904 repo.dirstate.forget(f)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111
Matt Mackall
Make repo locks recursive, eliminate all passing of lock/wlock
r4917 def update(repo, node, branchmerge, force, partial):
Matt Mackall
merge: update some docstrings
r3315 """
Perform a merge between the working directory and the given node
branchmerge = whether to merge between branches
force = whether to force branch merging or file overwriting
partial = a function to filter file lists (dirstate not updated)
"""
Matt Mackall
Merge: combine force and forcemerge arguments
r2815
Matt Mackall
Make repo locks recursive, eliminate all passing of lock/wlock
r4917 wlock = repo.wlock()
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 try:
wc = repo.workingctx()
if node is None:
# tip of current branch
try:
node = repo.branchtags()[wc.branch()]
except KeyError:
Matt Mackall
update: default to tipmost branch if default branch doesn't exist
r5570 if wc.branch() == "default": # no default branch!
node = repo.lookup("tip") # update to tip
else:
raise util.Abort(_("branch %s not found") % wc.branch())
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 overwrite = force and not branchmerge
forcemerge = force and branchmerge
pl = wc.parents()
p1, p2 = pl[0], repo.changectx(node)
pa = p1.ancestor(p2)
fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
fastforward = False
Matt Mackall
merge: various tidying...
r3314
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 ### check phase
if not overwrite and len(pl) > 1:
raise util.Abort(_("outstanding uncommitted merges"))
if pa == p1 or pa == p2: # is there a linear path from p1 to p2?
if branchmerge:
if p1.branch() != p2.branch() and pa != p2:
fastforward = True
else:
raise util.Abort(_("there is nothing to merge, just use "
"'hg update' or look at 'hg heads'"))
elif not (overwrite or branchmerge):
raise util.Abort(_("update spans branches, use 'hg merge' "
"or 'hg update -C' to lose changes"))
if branchmerge and not forcemerge:
if wc.files():
raise util.Abort(_("outstanding uncommitted changes"))
Matt Mackall
Merge: move most tests to the beginning
r2814
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 ### calculate phase
action = []
if not force:
checkunknown(wc, p2)
if not util.checkfolding(repo.path):
checkcollision(p2)
if not branchmerge:
action += forgetremoved(wc, p2)
action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
Matt Mackall
Move merge code to its own module...
r2775
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 ### apply phase
if not branchmerge: # just jump to the new rev
fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
if not partial:
repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
Matt Mackall
Move merge code to its own module...
r2775
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 stats = applyupdates(repo, action, wc, p2)
Matt Mackall
merge: consolidate dirstate updates
r2899
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 if not partial:
recordupdates(repo, action, branchmerge)
repo.dirstate.setparents(fp1, fp2)
if not branchmerge and not fastforward:
repo.dirstate.setbranch(p2.branch())
repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
Matt Mackall
merge: various tidying...
r3314
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 return stats
finally:
del wlock