summaryrefslogtreecommitdiff
path: root/mercurial/merge.py
diff options
context:
space:
mode:
Diffstat (limited to 'mercurial/merge.py')
-rw-r--r--mercurial/merge.py124
1 files changed, 34 insertions, 90 deletions
diff --git a/mercurial/merge.py b/mercurial/merge.py
index 19ffb28..240081f 100644
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -7,7 +7,7 @@
from node import nullid, nullrev, hex, bin
from i18n import _
-import error, scmutil, util, filemerge, copies, subrepo
+import scmutil, util, filemerge, copies, subrepo, encoding
import errno, os, shutil
class mergestate(object):
@@ -81,50 +81,23 @@ class mergestate(object):
self.mark(dfile, 'r')
return r
-def _checkunknownfile(repo, wctx, mctx, f):
- return (not repo.dirstate._ignore(f)
- and os.path.isfile(repo.wjoin(f))
- and repo.dirstate.normalize(f) not in repo.dirstate
- and mctx[f].cmp(wctx[f]))
-
-def _checkunknown(repo, wctx, mctx):
+def _checkunknown(wctx, mctx):
"check for collisions between unknown files and files in mctx"
+ for f in wctx.unknown():
+ if f in mctx and mctx[f].cmp(wctx[f]):
+ raise util.Abort(_("untracked file in working directory differs"
+ " from file in requested revision: '%s'") % f)
- error = False
- for f in mctx:
- if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
- error = True
- wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
- if error:
- raise util.Abort(_("untracked files in working directory differ "
- "from files in requested revision"))
-
-def _checkcollision(mctx, wctx):
+def _checkcollision(mctx):
"check for case folding collisions in the destination context"
folded = {}
for fn in mctx:
- fold = util.normcase(fn)
+ fold = encoding.lower(fn)
if fold in folded:
raise util.Abort(_("case-folding collision between %s and %s")
% (fn, folded[fold]))
folded[fold] = fn
- if wctx:
- # class to delay looking up copy mapping
- class pathcopies(object):
- @util.propertycache
- def map(self):
- # {dst@mctx: src@wctx} copy mapping
- return copies.pathcopies(wctx, mctx)
- pc = pathcopies()
-
- for fn in wctx:
- fold = util.normcase(fn)
- mfn = folded.get(fold, None)
- if mfn and mfn != fn and pc.map.get(mfn) != fn:
- raise util.Abort(_("case-folding collision between %s and %s")
- % (mfn, fn))
-
def _forgetremoved(wctx, mctx, branchmerge):
"""
Forget removed files
@@ -179,11 +152,6 @@ def manifestmerge(repo, p1, p2, pa, overwrite, partial):
if m and m != a: # changed from a to m
return m
if n and n != a: # changed from a to n
- if (n == 'l' or a == 'l') and m1.get(f) != ma.get(f):
- # can't automatically merge symlink flag when there
- # are file-level conflicts here, let filemerge take
- # care of it
- return m
return n
return '' # flag was cleared
@@ -198,16 +166,14 @@ def manifestmerge(repo, p1, p2, pa, overwrite, partial):
elif pa == p2: # backwards
pa = p1.p1()
elif pa and repo.ui.configbool("merge", "followcopies", True):
- copy, diverge, renamedelete = copies.mergecopies(repo, p1, p2, pa)
+ 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)
- for of, fl in renamedelete.iteritems():
- act("rename and delete", "rd", of, fl)
repo.ui.note(_("resolving manifests\n"))
- repo.ui.debug(" overwrite: %s, partial: %s\n"
- % (bool(overwrite), bool(partial)))
- repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, p1, p2))
+ 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())
@@ -256,7 +222,7 @@ def manifestmerge(repo, p1, p2, pa, overwrite, partial):
act("prompt keep", "a", f)
elif n[20:] == "a": # added, no remote
act("remote deleted", "f", f)
- else:
+ elif n[20:] != "u":
act("other deleted", "r", f)
for f, n in m2.iteritems():
@@ -276,13 +242,7 @@ def manifestmerge(repo, p1, p2, pa, overwrite, partial):
act("remote moved to " + f, "m",
f2, f, f, fmerge(f2, f, f2), True)
elif f not in ma:
- if (not overwrite
- and _checkunknownfile(repo, p1, p2, f)):
- rflags = fmerge(f, f, f)
- act("remote differs from untracked local",
- "m", f, f, f, rflags, False)
- else:
- act("remote created", "g", f, m2.flags(f))
+ act("remote created", "g", f, m2.flags(f))
elif n != ma[f]:
if repo.ui.promptchoice(
_("remote changed %s which local deleted\n"
@@ -313,6 +273,7 @@ def applyupdates(repo, action, wctx, mctx, actx, overwrite):
action.sort(key=actionkey)
# prescan for merges
+ u = repo.ui
for a in action:
f, m = a[:2]
if m == 'm': # merge
@@ -347,8 +308,8 @@ def applyupdates(repo, action, wctx, mctx, actx, overwrite):
numupdates = len(action)
for i, a in enumerate(action):
f, m = a[:2]
- repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
- unit=_('files'))
+ u.progress(_('updating'), i + 1, item=f, total=numupdates,
+ unit=_('files'))
if f and f[0] == "/":
continue
if m == "r": # remove
@@ -365,8 +326,7 @@ def applyupdates(repo, action, wctx, mctx, actx, overwrite):
removed += 1
elif m == "m": # merge
if f == '.hgsubstate': # subrepo states need updating
- subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
- overwrite)
+ subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite)
continue
f2, fd, flags, move = a[2:]
repo.wopener.audit(fd)
@@ -378,6 +338,7 @@ def applyupdates(repo, action, wctx, mctx, actx, overwrite):
updated += 1
else:
merged += 1
+ util.setflags(repo.wjoin(fd), 'l' in flags, 'x' in flags)
if (move and repo.dirstate.normalize(fd) != f
and os.path.lexists(repo.wjoin(f))):
repo.ui.debug("removing %s\n" % f)
@@ -411,18 +372,12 @@ def applyupdates(repo, action, wctx, mctx, actx, overwrite):
"multiple times to:\n") % f)
for nf in fl:
repo.ui.warn(" %s\n" % nf)
- elif m == "rd": # rename and delete
- fl = a[2]
- repo.ui.warn(_("note: possible conflict - %s was deleted "
- "and renamed to:\n") % f)
- for nf in fl:
- repo.ui.warn(" %s\n" % nf)
elif m == "e": # exec
flags = a[2]
repo.wopener.audit(f)
util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
ms.commit()
- repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files'))
+ u.progress(_('updating'), None, total=numupdates, unit=_('files'))
return updated, merged, removed, unresolved
@@ -488,8 +443,7 @@ def recordupdates(repo, action, branchmerge):
if f:
repo.dirstate.drop(f)
-def update(repo, node, branchmerge, force, partial, ancestor=None,
- mergeancestor=False):
+def update(repo, node, branchmerge, force, partial, ancestor=None):
"""
Perform a merge between the working directory and the given node
@@ -497,10 +451,6 @@ def update(repo, node, branchmerge, force, partial, ancestor=None,
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)
- mergeancestor = if false, merging with an ancestor (fast-forward)
- is only allowed between different named branches. This flag
- is used by rebase extension as a temporary fix and should be
- avoided in general.
The table below shows all the behaviors of the update command
given the -c and -C or no options, whether the working directory
@@ -537,8 +487,8 @@ def update(repo, node, branchmerge, force, partial, ancestor=None,
if node is None:
# tip of current branch
try:
- node = repo.branchtip(wc.branch())
- except error.RepoLookupError:
+ node = repo.branchtags()[wc.branch()]
+ except KeyError:
if wc.branch() == "default": # no default branch!
node = repo.lookup("tip") # update to tip
else:
@@ -561,13 +511,12 @@ def update(repo, node, branchmerge, force, partial, ancestor=None,
raise util.Abort(_("merging with a working directory ancestor"
" has no effect"))
elif pa == p1:
- if not mergeancestor and p1.branch() == p2.branch():
- raise util.Abort(_("nothing to merge"),
- hint=_("use 'hg update' "
- "or check 'hg heads'"))
+ if p1.branch() == p2.branch():
+ raise util.Abort(_("nothing to merge (use 'hg update'"
+ " or check 'hg heads')"))
if not force and (wc.files() or wc.deleted()):
- raise util.Abort(_("outstanding uncommitted changes"),
- hint=_("use 'hg status' to list changes"))
+ raise util.Abort(_("outstanding uncommitted changes "
+ "(use 'hg status' to list changes)"))
for s in wc.substate:
if wc.sub(s).dirty():
raise util.Abort(_("outstanding uncommitted changes in "
@@ -584,20 +533,15 @@ def update(repo, node, branchmerge, force, partial, ancestor=None,
" --check to force update)"))
else:
# Allow jumping branches if clean and specific rev given
- pa = p1
+ overwrite = True
### calculate phase
action = []
- folding = not util.checkcase(repo.path)
- if folding:
- # collision check is not needed for clean update
- if (not branchmerge and
- (force or not wc.dirty(missing=True, branch=False))):
- _checkcollision(p2, None)
- else:
- _checkcollision(p2, wc)
+ wc.status(unknown=True) # prime cache
if not force:
- _checkunknown(repo, wc, p2)
+ _checkunknown(wc, p2)
+ if not util.checkcase(repo.path):
+ _checkcollision(p2)
action += _forgetremoved(wc, p2, branchmerge)
action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
@@ -610,7 +554,7 @@ def update(repo, node, branchmerge, force, partial, ancestor=None,
stats = applyupdates(repo, action, wc, p2, pa, overwrite)
if not partial:
- repo.setparents(fp1, fp2)
+ repo.dirstate.setparents(fp1, fp2)
recordupdates(repo, action, branchmerge)
if not branchmerge:
repo.dirstate.setbranch(p2.branch())