##// END OF EJS Templates
localrepo.commit: normalize commit message even for rawcommit....
localrepo.commit: normalize commit message even for rawcommit. This normalization consists of: - stripping trailing whitespace - always using "\n" as the line separator I think the main reason rawcommit was skipping this normalization was an attempt to preserve hashes during an hg->hg conversion. While this is a nice goal, it's not particularly interesting in practice. Since SHA-1 is so strong, the only safe way to do it is to have absolutely identical revisions. But: - if the original revision was created with a recent version of hg, the commit message will be the same, with or without that normalization - if it was created with an ancient version of hg that didn't do any normalization, even if the commit message is identical, the file list in the changelog is likely to be different (e.g. no removed files), and there were some old issues with e.g. extra file merging, which will end up changing the hash anyway - in any case, if one *really* has to preserve hashes, it's easier (and faster) to fake a partial conversion using something like: hg clone -U -r rev orig-repo new-repo hg -R new-repo log --template '#node# #node#\n' > new-repo/.hg/shamap Additionally, we've had some reports of problems arising from this lack of normalization - e.g. issue871, and a user that was wondering why hg export/hg import was not preserving hashes when there was nothing unusual going on (it was just import doing the normalization that had been skipped). This also means that it's even more unlikely to get identical revisions when going $VCS->hg->$VCS.

File last commit:

r6212:e75aab65 default
r6254:3667b6e4 default
Show More
filemerge.py
217 lines | 7.2 KiB | text/x-python | PythonLexer
Matt Mackall
filemerge: pull file-merging code into its own module
r6003 # filemerge.py - file-level merge handling for Mercurial
#
# Copyright 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
Joel Rosdahl
Expand import * to allow Pyflakes to find problems
r6211 from node import nullrev
Matt Mackall
filemerge: pull file-merging code into its own module
r6003 from i18n import _
Joel Rosdahl
Remove unused imports
r6212 import util, os, tempfile, simplemerge, re, filecmp
Matt Mackall
merge: allow smarter tool configuration...
r6004
Matt Mackall
filemerge: handle missing regappend
r6013 def _toolstr(ui, tool, part, default=""):
Matt Mackall
merge: allow smarter tool configuration...
r6004 return ui.config("merge-tools", tool + "." + part, default)
def _toolbool(ui, tool, part, default=False):
return ui.configbool("merge-tools", tool + "." + part, default)
def _findtool(ui, tool):
Matt Mackall
merge: add registry look up bits to tool search
r6006 k = _toolstr(ui, tool, "regkey")
if k:
p = util.lookup_reg(k, _toolstr(ui, tool, "regname"))
if p:
p = util.find_exe(p + _toolstr(ui, tool, "regappend"))
if p:
return p
Matt Mackall
merge: allow smarter tool configuration...
r6004 return util.find_exe(_toolstr(ui, tool, "executable", tool))
def _picktool(repo, ui, path, binary, symlink):
def check(tool, pat, symlink, binary):
tmsg = tool
if pat:
tmsg += " specified for " + pat
if pat and not _findtool(ui, tool): # skip search if not matching
ui.warn(_("couldn't find merge tool %s\n") % tmsg)
elif symlink and not _toolbool(ui, tool, "symlink"):
ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
elif binary and not _toolbool(ui, tool, "binary"):
ui.warn(_("tool %s can't handle binary\n") % tmsg)
Matt Mackall
filemerge: add config item for GUI tools...
r6007 elif not util.gui() and _toolbool(ui, tool, "gui"):
ui.warn(_("tool %s requires a GUI\n") % tmsg)
Matt Mackall
merge: allow smarter tool configuration...
r6004 else:
return True
return False
# HGMERGE takes precedence
Steve Borho
filemerge: wrap quotes around tool path
r6025 hgmerge = os.environ.get("HGMERGE")
if hgmerge:
return (hgmerge, hgmerge)
Matt Mackall
merge: allow smarter tool configuration...
r6004
# then patterns
dhruva
filemerge: fix pattern matching
r6016 for pat, tool in ui.configitems("merge-patterns"):
Matt Mackall
merge: allow smarter tool configuration...
r6004 mf = util.matcher(repo.root, "", [pat], [], [])[1]
if mf(path) and check(tool, pat, symlink, False):
Steve Borho
filemerge: wrap quotes around tool path
r6025 toolpath = _findtool(ui, tool)
return (tool, '"' + toolpath + '"')
Matt Mackall
merge: allow smarter tool configuration...
r6004
# then merge tools
tools = {}
for k,v in ui.configitems("merge-tools"):
t = k.split('.')[0]
if t not in tools:
tools[t] = int(_toolstr(ui, t, "priority", "0"))
Steve Borho
filemerge: more backwards compatible behavior for ui.merge...
r6076 names = tools.keys()
Matt Mackall
merge: allow smarter tool configuration...
r6004 tools = [(-p,t) for t,p in tools.items()]
tools.sort()
Steve Borho
filemerge: more backwards compatible behavior for ui.merge...
r6076 uimerge = ui.config("ui", "merge")
if uimerge:
if uimerge not in names:
return (uimerge, uimerge)
tools.insert(0, (None, uimerge)) # highest priority
Matt Mackall
merge: allow smarter tool configuration...
r6004 tools.append((None, "hgmerge")) # the old default, if found
for p,t in tools:
Steve Borho
filemerge: wrap quotes around tool path
r6025 toolpath = _findtool(ui, t)
if toolpath and check(t, None, symlink, binary):
return (t, '"' + toolpath + '"')
# internal merge as last resort
return (not (symlink or binary) and "internal:merge" or None, None)
Matt Mackall
filemerge: pull file-merging code into its own module
r6003
Matt Mackall
merge: add support for tool EOL fixups...
r6005 def _eoltype(data):
"Guess the EOL type of a file"
if '\0' in data: # binary
return None
if '\r\n' in data: # Windows
return '\r\n'
if '\r' in data: # Old Mac
return '\r'
if '\n' in data: # UNIX
return '\n'
return None # unknown
def _matcheol(file, origfile):
"Convert EOL markers in a file to match origfile"
tostyle = _eoltype(open(origfile, "rb").read())
if tostyle:
data = open(file, "rb").read()
style = _eoltype(data)
if style:
newdata = data.replace(style, tostyle)
if newdata != data:
open(file, "wb").write(newdata)
Matt Mackall
filemerge: pull file-merging code into its own module
r6003 def filemerge(repo, fw, fd, fo, wctx, mctx):
"""perform a 3-way merge in the working directory
fw = original filename in the working directory
fd = destination filename in the working directory
fo = filename in other parent
wctx, mctx = working and merge changecontexts
"""
def temp(prefix, ctx):
pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
(fd, name) = tempfile.mkstemp(prefix=pre)
data = repo.wwritedata(ctx.path(), ctx.data())
f = os.fdopen(fd, "wb")
f.write(data)
f.close()
return name
Matt Mackall
merge: allow smarter tool configuration...
r6004 def isbin(ctx):
try:
return util.binary(ctx.data())
except IOError:
return False
Matt Mackall
filemerge: pull file-merging code into its own module
r6003 fco = mctx.filectx(fo)
Matt Mackall
merge: allow smarter tool configuration...
r6004 if not fco.cmp(wctx.filectx(fd).data()): # files identical?
Matt Mackall
filemerge: pull file-merging code into its own module
r6003 return None
Matt Mackall
merge: allow smarter tool configuration...
r6004 ui = repo.ui
fcm = wctx.filectx(fw)
fca = fcm.ancestor(fco) or repo.filectx(fw, fileid=nullrev)
binary = isbin(fcm) or isbin(fco) or isbin(fca)
symlink = fcm.islink() or fco.islink()
Steve Borho
filemerge: wrap quotes around tool path
r6025 tool, toolpath = _picktool(repo, ui, fw, binary, symlink)
Matt Mackall
merge: allow smarter tool configuration...
r6004 ui.debug(_("picked tool '%s' for %s (binary %s symlink %s)\n") %
(tool, fw, binary, symlink))
if not tool:
tool = "internal:local"
if ui.prompt(_(" no tool found to merge %s\n"
"keep (l)ocal or take (o)ther?") % fw,
_("[lo]"), _("l")) != _("l"):
tool = "internal:other"
if tool == "internal:local":
return 0
if tool == "internal:other":
repo.wwrite(fd, fco.data(), fco.fileflags())
return 0
if tool == "internal:fail":
return 1
# do the actual merge
Matt Mackall
filemerge: pull file-merging code into its own module
r6003 a = repo.wjoin(fd)
b = temp("base", fca)
c = temp("other", fco)
Matt Mackall
merge: allow smarter tool configuration...
r6004 out = ""
back = a + ".orig"
util.copyfile(a, back)
Matt Mackall
filemerge: pull file-merging code into its own module
r6003
if fw != fo:
repo.ui.status(_("merging %s and %s\n") % (fw, fo))
else:
repo.ui.status(_("merging %s\n") % fw)
repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcm, fco, fca))
Matt Mackall
merge: allow smarter tool configuration...
r6004 # do we attempt to simplemerge first?
if _toolbool(ui, tool, "premerge", not (binary or symlink)):
r = simplemerge.simplemerge(a, b, c, quiet=True)
if not r:
ui.debug(_(" premerge successful\n"))
os.unlink(back)
os.unlink(b)
os.unlink(c)
return 0
util.copyfile(back, a) # restore from backup and try again
env = dict(HG_FILE=fd,
HG_MY_NODE=str(wctx.parents()[0]),
HG_OTHER_NODE=str(mctx),
HG_MY_ISLINK=fcm.islink(),
HG_OTHER_ISLINK=fco.islink(),
HG_BASE_ISLINK=fca.islink())
if tool == "internal:merge":
r = simplemerge.simplemerge(a, b, c, label=['local', 'other'])
else:
args = _toolstr(ui, tool, "args", '$local $base $other')
if "$output" in args:
out, a = a, back # read input from backup, write to original
replace = dict(local=a, base=b, other=c, output=out)
args = re.sub("\$(local|base|other|output)",
lambda x: '"%s"' % replace[x.group()[1:]], args)
r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env)
if not r and _toolbool(ui, tool, "checkconflicts"):
if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcm.data()):
r = 1
Steve Borho
filemerge: add 'checkchanged' merge tool property
r6075 if not r and _toolbool(ui, tool, "checkchanged"):
if filecmp.cmp(repo.wjoin(fd), back):
if ui.prompt(_(" output file %s appears unchanged\n"
"was merge successful (yn)?") % fd,
_("[yn]"), _("n")) != _("y"):
r = 1
Matt Mackall
merge: add support for tool EOL fixups...
r6005 if _toolbool(ui, tool, "fixeol"):
Lee Cantey
filemerge: fix path to working file when fixeol is enabled
r6015 _matcheol(repo.wjoin(fd), back)
Matt Mackall
merge: add support for tool EOL fixups...
r6005
Matt Mackall
filemerge: pull file-merging code into its own module
r6003 if r:
repo.ui.warn(_("merging %s failed!\n") % fd)
Matt Mackall
merge: allow smarter tool configuration...
r6004 else:
os.unlink(back)
Matt Mackall
filemerge: pull file-merging code into its own module
r6003
os.unlink(b)
os.unlink(c)
return r