def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): r = _premerge(repo, toolconf, files, labels=labels) if r: tool, toolpath, binary, symlink = toolconf a, b, c, back = files out = "" env = { "HG_FILE": fcd.path(), "HG_MY_NODE": short(mynode), "HG_OTHER_NODE": str(fco.changectx()), "HG_BASE_NODE": str(fca.changectx()), "HG_MY_ISLINK": "l" in fcd.flags(), "HG_OTHER_ISLINK": "l" in fco.flags(), "HG_BASE_ISLINK": "l" in fca.flags(), } ui = repo.ui args = _toolstr(ui, tool, "args", "$local $base $other") if "$output" in args: out, a = a, back # read input from backup, write to original replace = {"local": a, "base": b, "other": c, "output": out} args = util.interpolate(r"\$", replace, args, lambda s: util.shellquote(util.localpath(s))) cmd = toolpath + " " + args repo.ui.debug("launching merge tool: %s\n" % cmd) r = ui.system(cmd, cwd=repo.root, environ=env) repo.ui.debug("merge tool returned: %s\n" % r) return True, r return False, 0
def evalpath(striplen): score = 0 for s in srcs: t = os.path.join(dest, util.localpath(s[0])[striplen:]) if os.path.lexists(t): score += 1 return score
def evalpath(striplen): score = 0 for s in srcs: t = os.path.join(dest, util.localpath(s[0])[striplen:]) if os.path.exists(t): score += 1 return score
def tidyprefix(dest, kind, prefix): '''choose prefix to use for names in archive. make sure prefix is safe for consumers.''' if prefix: prefix = util.normpath(prefix) else: if not isinstance(dest, str): raise ValueError('dest must be string if no prefix') prefix = os.path.basename(dest) lower = prefix.lower() for sfx in exts.get(kind, []): if lower.endswith(sfx): prefix = prefix[:-len(sfx)] break lpfx = os.path.normpath(util.localpath(prefix)) prefix = util.pconvert(lpfx) if not prefix.endswith('/'): prefix += '/' # Drop the leading '.' path component if present, so Windows can read the # zip files (issue4634) if prefix.startswith('./'): prefix = prefix[2:] if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix: raise util.Abort(_('archive prefix contains illegal components')) return prefix
def systemrcpath(): '''return default os-specific hgrc search path''' rcpath = [] filename = util.executablepath() # Use mercurial.ini found in directory with hg.exe progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini') rcpath.append(progrc) # Use hgrc.d found in directory with hg.exe progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d') if os.path.isdir(progrcd): for f, kind in osutil.listdir(progrcd): if f.endswith('.rc'): rcpath.append(os.path.join(progrcd, f)) # else look for a system rcpath in the registry value = util.lookupreg('SOFTWARE\\Mercurial', None, _winreg.HKEY_LOCAL_MACHINE) if not isinstance(value, str) or not value: return rcpath value = util.localpath(value) for p in value.split(os.pathsep): if p.lower().endswith('mercurial.ini'): rcpath.append(p) elif os.path.isdir(p): for f, kind in osutil.listdir(p): if f.endswith('.rc'): rcpath.append(os.path.join(p, f)) return rcpath
def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): r = _premerge(repo, toolconf, files, labels=labels) if r: tool, toolpath, binary, symlink = toolconf a, b, c, back = files out = "" env = { 'HG_FILE': fcd.path(), 'HG_MY_NODE': short(mynode), 'HG_OTHER_NODE': str(fco.changectx()), 'HG_BASE_NODE': str(fca.changectx()), 'HG_MY_ISLINK': 'l' in fcd.flags(), 'HG_OTHER_ISLINK': 'l' in fco.flags(), 'HG_BASE_ISLINK': 'l' in fca.flags(), } ui = repo.ui args = _toolstr(ui, tool, "args", '$local $base $other') if "$output" in args: out, a = a, back # read input from backup, write to original replace = {'local': a, 'base': b, 'other': c, 'output': out} args = util.interpolate(r'\$', replace, args, lambda s: util.shellquote(util.localpath(s))) r = ui.system(toolpath + ' ' + args, cwd=repo.root, environ=env) return True, r return False, 0
def systemrcpath(): '''return default os-specific hgrc search path''' rcpath = [] filename = util.executablepath() # Use mercurial.ini found in directory with hg.exe progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini') if os.path.isfile(progrc): rcpath.append(progrc) return rcpath # Use hgrc.d found in directory with hg.exe progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d') if os.path.isdir(progrcd): for f, kind in osutil.listdir(progrcd): if f.endswith('.rc'): rcpath.append(os.path.join(progrcd, f)) return rcpath # else look for a system rcpath in the registry value = util.lookupreg('SOFTWARE\\Mercurial', None, _winreg.HKEY_LOCAL_MACHINE) if not isinstance(value, str) or not value: return rcpath value = util.localpath(value) for p in value.split(os.pathsep): if p.lower().endswith('mercurial.ini'): rcpath.append(p) elif os.path.isdir(p): for f, kind in osutil.listdir(p): if f.endswith('.rc'): rcpath.append(os.path.join(p, f)) return rcpath
def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): r = _premerge(repo, toolconf, files, labels=labels) if r: tool, toolpath, binary, symlink = toolconf a, b, c, back = files out = "" env = {'HG_FILE': fcd.path(), 'HG_MY_NODE': short(mynode), 'HG_OTHER_NODE': str(fco.changectx()), 'HG_BASE_NODE': str(fca.changectx()), 'HG_MY_ISLINK': 'l' in fcd.flags(), 'HG_OTHER_ISLINK': 'l' in fco.flags(), 'HG_BASE_ISLINK': 'l' in fca.flags(), } ui = repo.ui args = _toolstr(ui, tool, "args", '$local $base $other') if "$output" in args: out, a = a, back # read input from backup, write to original replace = {'local': a, 'base': b, 'other': c, 'output': out} args = util.interpolate(r'\$', replace, args, lambda s: util.shellquote(util.localpath(s))) r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env, out=ui.fout) return True, r return False, 0
def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files): r = _premerge(repo, toolconf, files) if r: tool, toolpath, binary, symlink = toolconf a, b, c, back = files out = "" env = dict(HG_FILE=fcd.path(), HG_MY_NODE=short(mynode), HG_OTHER_NODE=str(fco.changectx()), HG_BASE_NODE=str(fca.changectx()), HG_MY_ISLINK='l' in fcd.flags(), HG_OTHER_ISLINK='l' in fco.flags(), HG_BASE_ISLINK='l' in fca.flags()) ui = repo.ui 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 = util.interpolate(r'\$', replace, args, lambda s: '"%s"' % util.localpath(s)) r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env, out=ui.fout) return True, r return False, 0
def __call__(self, path): '''Check the relative path. path may contain a pattern (e.g. foodir/**.txt)''' path = util.localpath(path) normpath = self.normcase(path) if normpath in self.audited: return # AIX ignores "/" at end of path, others raise EISDIR. if util.endswithsep(path): raise util.Abort(_("path ends in directory separator: %s") % path) parts = util.splitpath(path) if (os.path.splitdrive(path)[0] or parts[0].lower() in ('.hg', '.hg.', '') or os.pardir in parts): raise util.Abort(_("path contains illegal component: %s") % path) if '.hg' in path.lower(): lparts = [p.lower() for p in parts] for p in '.hg', '.hg.': if p in lparts[1:]: pos = lparts.index(p) base = os.path.join(*parts[:pos]) raise util.Abort( _("path '%s' is inside nested repo %r") % (path, base)) normparts = util.splitpath(normpath) assert len(parts) == len(normparts) parts.pop() normparts.pop() prefixes = [] while parts: prefix = os.sep.join(parts) normprefix = os.sep.join(normparts) if normprefix in self.auditeddir: break curpath = os.path.join(self.root, prefix) try: st = os.lstat(curpath) except OSError, err: # EINVAL can be raised as invalid path syntax under win32. # They must be ignored for patterns can be checked too. if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL): raise else: if stat.S_ISLNK(st.st_mode): raise util.Abort( _('path %r traverses symbolic link %r') % (path, prefix)) elif (stat.S_ISDIR(st.st_mode) and os.path.isdir(os.path.join(curpath, '.hg'))): if not self.callback or not self.callback(curpath): raise util.Abort( _("path '%s' is inside nested " "repo %r") % (path, prefix)) prefixes.append(normprefix) parts.pop() normparts.pop()
def targetpathfn(pat, dest, srcs): if os.path.isdir(pat): abspfx = util.canonpath(repo.root, cwd, pat) abspfx = util.localpath(abspfx) if destdirexists: striplen = len(os.path.split(abspfx)[0]) else: striplen = len(abspfx) if striplen: striplen += len(os.sep) res = lambda p: os.path.join(dest, util.localpath(p)[striplen:]) elif destdirexists: res = lambda p: os.path.join(dest, os.path.basename(util.localpath(p))) else: res = lambda p: dest return res
def __call__(self, path): '''Check the relative path. path may contain a pattern (e.g. foodir/**.txt)''' path = util.localpath(path) normpath = self.normcase(path) if normpath in self.audited: return # AIX ignores "/" at end of path, others raise EISDIR. if util.endswithsep(path): raise util.Abort(_("path ends in directory separator: %s") % path) parts = util.splitpath(path) if (os.path.splitdrive(path)[0] or parts[0].lower() in ('.hg', '.hg.', '') or os.pardir in parts): raise util.Abort(_("path contains illegal component: %s") % path) if '.hg' in path.lower(): lparts = [p.lower() for p in parts] for p in '.hg', '.hg.': if p in lparts[1:]: pos = lparts.index(p) base = os.path.join(*parts[:pos]) raise util.Abort(_("path '%s' is inside nested repo %r") % (path, base)) normparts = util.splitpath(normpath) assert len(parts) == len(normparts) parts.pop() normparts.pop() prefixes = [] while parts: prefix = os.sep.join(parts) normprefix = os.sep.join(normparts) if normprefix in self.auditeddir: break curpath = os.path.join(self.root, prefix) try: st = os.lstat(curpath) except OSError, err: # EINVAL can be raised as invalid path syntax under win32. # They must be ignored for patterns can be checked too. if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL): raise else: if stat.S_ISLNK(st.st_mode): raise util.Abort( _('path %r traverses symbolic link %r') % (path, prefix)) elif (stat.S_ISDIR(st.st_mode) and os.path.isdir(os.path.join(curpath, '.hg'))): if not self.callback or not self.callback(curpath): raise util.Abort(_("path '%s' is inside nested " "repo %r") % (path, prefix)) prefixes.append(normprefix) parts.pop() normparts.pop()
def __call__(self, path): """Check the relative path. path may contain a pattern (e.g. foodir/**.txt)""" path = util.localpath(path) normpath = self.normcase(path) if normpath in self.audited: return # AIX ignores "/" at end of path, others raise EISDIR. if util.endswithsep(path): raise util.Abort(_("path ends in directory separator: %s") % path) parts = util.splitpath(path) if os.path.splitdrive(path)[0] or _lowerclean(parts[0]) in (".hg", ".hg.", "") or os.pardir in parts: raise util.Abort(_("path contains illegal component: %s") % path) # Windows shortname aliases for p in parts: if "~" in p: first, last = p.split("~", 1) if last.isdigit() and first.upper() in ["HG", "HG8B6C"]: raise util.Abort(_("path contains illegal component: %s") % path) if ".hg" in _lowerclean(path): lparts = [_lowerclean(p.lower()) for p in parts] for p in ".hg", ".hg.": if p in lparts[1:]: pos = lparts.index(p) base = os.path.join(*parts[:pos]) raise util.Abort(_("path '%s' is inside nested repo %r") % (path, base)) normparts = util.splitpath(normpath) assert len(parts) == len(normparts) parts.pop() normparts.pop() prefixes = [] while parts: prefix = os.sep.join(parts) normprefix = os.sep.join(normparts) if normprefix in self.auditeddir: break curpath = os.path.join(self.root, prefix) try: st = os.lstat(curpath) except OSError, err: # EINVAL can be raised as invalid path syntax under win32. # They must be ignored for patterns can be checked too. if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL): raise else: if stat.S_ISLNK(st.st_mode): raise util.Abort(_("path %r traverses symbolic link %r") % (path, prefix)) elif stat.S_ISDIR(st.st_mode) and os.path.isdir(os.path.join(curpath, ".hg")): if not self.callback or not self.callback(curpath): raise util.Abort(_("path '%s' is inside nested " "repo %r") % (path, prefix)) prefixes.append(normprefix) parts.pop() normparts.pop()
def targetpathafterfn(pat, dest, srcs): if matchmod.patkind(pat): # a mercurial pattern res = lambda p: os.path.join(dest, os.path.basename(util.localpath(p))) else: abspfx = util.canonpath(repo.root, cwd, pat) if len(abspfx) < len(srcs[0][0]): # A directory. Either the target path contains the last # component of the source path or it does not. def evalpath(striplen): score = 0 for s in srcs: t = os.path.join(dest, util.localpath(s[0])[striplen:]) if os.path.lexists(t): score += 1 return score abspfx = util.localpath(abspfx) striplen = len(abspfx) if striplen: striplen += len(os.sep) if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])): score = evalpath(striplen) striplen1 = len(os.path.split(abspfx)[0]) if striplen1: striplen1 += len(os.sep) if evalpath(striplen1) > score: striplen = striplen1 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:]) else: # a file if destdirexists: res = lambda p: os.path.join(dest, os.path.basename(util.localpath(p))) else: res = lambda p: dest return res
def targetpathafterfn(pat, dest, srcs): if util.patkind(pat, None)[0]: # a mercurial pattern res = lambda p: os.path.join(dest, os.path.basename(util.localpath(p))) else: abspfx = util.canonpath(repo.root, cwd, pat) if len(abspfx) < len(srcs[0][0]): # A directory. Either the target path contains the last # component of the source path or it does not. def evalpath(striplen): score = 0 for s in srcs: t = os.path.join(dest, util.localpath(s[0])[striplen:]) if os.path.exists(t): score += 1 return score abspfx = util.localpath(abspfx) striplen = len(abspfx) if striplen: striplen += len(os.sep) if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])): score = evalpath(striplen) striplen1 = len(os.path.split(abspfx)[0]) if striplen1: striplen1 += len(os.sep) if evalpath(striplen1) > score: striplen = striplen1 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:]) else: # a file if destdirexists: res = lambda p: os.path.join( dest, os.path.basename(util.localpath(p))) else: res = lambda p: dest return res
def tidyprefix(dest, prefix, suffixes): '''choose prefix to use for names in archive. make sure prefix is safe for consumers.''' if prefix: prefix = util.normpath(prefix) else: if not isinstance(dest, str): raise ValueError('dest must be string if no prefix') prefix = os.path.basename(dest) lower = prefix.lower() for sfx in suffixes: if lower.endswith(sfx): prefix = prefix[:-len(sfx)] break lpfx = os.path.normpath(util.localpath(prefix)) prefix = util.pconvert(lpfx) if not prefix.endswith('/'): prefix += '/' if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix: raise util.Abort(_('archive prefix contains illegal components')) return prefix
def tidyprefix(dest, kind, prefix): """choose prefix to use for names in archive. make sure prefix is safe for consumers.""" if prefix: prefix = util.normpath(prefix) else: if not isinstance(dest, str): raise ValueError("dest must be string if no prefix") prefix = os.path.basename(dest) lower = prefix.lower() for sfx in exts.get(kind, []): if lower.endswith(sfx): prefix = prefix[: -len(sfx)] break lpfx = os.path.normpath(util.localpath(prefix)) prefix = util.pconvert(lpfx) if not prefix.endswith("/"): prefix += "/" if prefix.startswith("../") or os.path.isabs(lpfx) or "/../" in prefix: raise util.Abort(_("archive prefix contains illegal components")) return prefix
def filemerge(repo, mynode, orig, fcd, fco, fca): """perform a 3-way merge in the working directory mynode = parent node before merge orig = original local filename before merge fco = other file context fca = ancestor file context fcd = local file context for current/destination file """ 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 if not fco.cmp(fcd): # files identical? return None ui = repo.ui fd = fcd.path() binary = fcd.isbinary() or fco.isbinary() or fca.isbinary() symlink = "l" in fcd.flags() + fco.flags() tool, toolpath = _picktool(repo, ui, fd, binary, symlink) ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" % (tool, fd, binary, symlink)) if not tool or tool == "internal:prompt": tool = "internal:local" if ui.promptchoice( _(" no tool found to merge %s\n" "keep (l)ocal or take (o)ther?") % fd, (_("&Local"), _("&Other")), 0 ): tool = "internal:other" if tool == "internal:local": return 0 if tool == "internal:other": repo.wwrite(fd, fco.data(), fco.flags()) return 0 if tool == "internal:fail": return 1 # do the actual merge a = repo.wjoin(fd) b = temp("base", fca) c = temp("other", fco) out = "" back = a + ".orig" util.copyfile(a, back) if orig != fco.path(): ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd)) else: ui.status(_("merging %s\n") % fd) ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca)) # do we attempt to simplemerge first? try: premerge = _toolbool(ui, tool, "premerge", not (binary or symlink)) except error.ConfigError: premerge = _toolstr(ui, tool, "premerge").lower() valid = "keep".split() if premerge not in valid: _valid = ", ".join(["'" + v + "'" for v in valid]) raise error.ConfigError( _("%s.premerge not valid " "('%s' is neither boolean nor %s)") % (tool, premerge, _valid) ) if premerge: r = simplemerge.simplemerge(ui, a, b, c, quiet=True) if not r: ui.debug(" premerge successful\n") os.unlink(back) os.unlink(b) os.unlink(c) return 0 if premerge != "keep": util.copyfile(back, a) # restore from backup and try again env = dict( HG_FILE=fd, HG_MY_NODE=short(mynode), HG_OTHER_NODE=str(fco.changectx()), HG_BASE_NODE=str(fca.changectx()), HG_MY_ISLINK="l" in fcd.flags(), HG_OTHER_ISLINK="l" in fco.flags(), HG_BASE_ISLINK="l" in fca.flags(), ) if tool == "internal:merge": r = simplemerge.simplemerge(ui, a, b, c, label=["local", "other"]) elif tool == "internal:dump": a = repo.wjoin(fd) util.copyfile(a, a + ".local") repo.wwrite(fd + ".other", fco.data(), fco.flags()) repo.wwrite(fd + ".base", fca.data(), fca.flags()) os.unlink(b) os.unlink(c) return 1 # unresolved 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 = util.interpolate(r"\$", replace, args, lambda s: '"%s"' % util.localpath(s)) r = util.system(toolpath + " " + args, cwd=repo.root, environ=env, out=ui.fout) if not r and (_toolbool(ui, tool, "checkconflicts") or "conflicts" in _toollist(ui, tool, "check")): if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(), re.MULTILINE): r = 1 checked = False if "prompt" in _toollist(ui, tool, "check"): checked = True if ui.promptchoice(_("was merge of '%s' successful (yn)?") % fd, (_("&Yes"), _("&No")), 1): r = 1 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or "changed" in _toollist(ui, tool, "check")): if filecmp.cmp(repo.wjoin(fd), back): if ui.promptchoice( _(" output file %s appears unchanged\n" "was merge successful (yn)?") % fd, (_("&Yes"), _("&No")), 1 ): r = 1 if _toolbool(ui, tool, "fixeol"): _matcheol(repo.wjoin(fd), back) if r: if tool == "internal:merge": ui.warn(_("merging %s incomplete! " "(edit conflicts, then use 'hg resolve --mark')\n") % fd) else: ui.warn(_("merging %s failed!\n") % fd) else: os.unlink(back) os.unlink(b) os.unlink(c) return r
def __call__(self, path): '''Check the relative path. path may contain a pattern (e.g. foodir/**.txt)''' path = util.localpath(path) normpath = self.normcase(path) if normpath in self.audited: return # AIX ignores "/" at end of path, others raise EISDIR. if util.endswithsep(path): raise util.Abort(_("path ends in directory separator: %s") % path) parts = util.splitpath(path) if (os.path.splitdrive(path)[0] or _lowerclean(parts[0]) in ('.hg', '.hg.', '') or os.pardir in parts): raise util.Abort(_("path contains illegal component: %s") % path) # Windows shortname aliases for p in parts: if "~" in p: first, last = p.split("~", 1) if last.isdigit() and first.upper() in ["HG", "HG8B6C"]: raise util.Abort(_("path contains illegal component: %s") % path) if '.hg' in _lowerclean(path): lparts = [_lowerclean(p.lower()) for p in parts] for p in '.hg', '.hg.': if p in lparts[1:]: pos = lparts.index(p) base = os.path.join(*parts[:pos]) raise util.Abort(_("path '%s' is inside nested repo %r") % (path, base)) normparts = util.splitpath(normpath) assert len(parts) == len(normparts) parts.pop() normparts.pop() prefixes = [] while parts: prefix = os.sep.join(parts) normprefix = os.sep.join(normparts) if normprefix in self.auditeddir: break curpath = os.path.join(self.root, prefix) try: st = os.lstat(curpath) except OSError as err: # EINVAL can be raised as invalid path syntax under win32. # They must be ignored for patterns can be checked too. if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL): raise else: if stat.S_ISLNK(st.st_mode): raise util.Abort( _('path %r traverses symbolic link %r') % (path, prefix)) elif (stat.S_ISDIR(st.st_mode) and os.path.isdir(os.path.join(curpath, '.hg'))): if not self.callback or not self.callback(curpath): raise util.Abort(_("path '%s' is inside nested " "repo %r") % (path, prefix)) prefixes.append(normprefix) parts.pop() normparts.pop() self.audited.add(normpath) # only add prefixes to the cache after checking everything: we don't # want to add "foo/bar/baz" before checking if there's a "foo/.hg" self.auditeddir.update(prefixes)
def filemerge(repo, mynode, orig, fcd, fco, fca): """perform a 3-way merge in the working directory mynode = parent node before merge orig = original local filename before merge fco = other file context fca = ancestor file context fcd = local file context for current/destination file """ 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 def isbin(ctx): try: return util.binary(ctx.data()) except IOError: return False if not fco.cmp(fcd.data()): # files identical? return None if fca == fco: # backwards, use working dir parent as ancestor fca = fcd.parents()[0] ui = repo.ui fd = fcd.path() binary = isbin(fcd) or isbin(fco) or isbin(fca) symlink = 'l' in fcd.flags() + fco.flags() tool, toolpath = _picktool(repo, ui, fd, binary, symlink) ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" % (tool, fd, binary, symlink)) if not tool or tool == 'internal:prompt': tool = "internal:local" if ui.promptchoice(_(" no tool found to merge %s\n" "keep (l)ocal or take (o)ther?") % fd, (_("&Local"), _("&Other")), 0): tool = "internal:other" if tool == "internal:local": return 0 if tool == "internal:other": repo.wwrite(fd, fco.data(), fco.flags()) return 0 if tool == "internal:fail": return 1 # do the actual merge a = repo.wjoin(fd) b = temp("base", fca) c = temp("other", fco) out = "" back = a + ".orig" util.copyfile(a, back) if orig != fco.path(): ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd)) else: ui.status(_("merging %s\n") % fd) ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca)) # do we attempt to simplemerge first? if _toolbool(ui, tool, "premerge", not (binary or symlink)): r = simplemerge.simplemerge(ui, 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=short(mynode), HG_OTHER_NODE=str(fco.changectx()), HG_BASE_NODE=str(fca.changectx()), HG_MY_ISLINK='l' in fcd.flags(), HG_OTHER_ISLINK='l' in fco.flags(), HG_BASE_ISLINK='l' in fca.flags()) if tool == "internal:merge": r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other']) elif tool == 'internal:dump': a = repo.wjoin(fd) util.copyfile(a, a + ".local") repo.wwrite(fd + ".other", fco.data(), fco.flags()) repo.wwrite(fd + ".base", fca.data(), fca.flags()) return 1 # unresolved 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"' % util.localpath(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("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data()): r = 1 if not r and _toolbool(ui, tool, "checkchanged"): if filecmp.cmp(repo.wjoin(fd), back): if ui.promptchoice(_(" output file %s appears unchanged\n" "was merge successful (yn)?") % fd, (_("&Yes"), _("&No")), 1): r = 1 if _toolbool(ui, tool, "fixeol"): _matcheol(repo.wjoin(fd), back) if r: ui.warn(_("merging %s failed!\n") % fd) else: os.unlink(back) os.unlink(b) os.unlink(c) return r
def filemerge(repo, mynode, orig, fcd, fco, fca): """perform a 3-way merge in the working directory mynode = parent node before merge orig = original local filename before merge fco = other file context fca = ancestor file context fcd = local file context for current/destination file """ 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 def isbin(ctx): try: return util.binary(ctx.data()) except IOError: return False if not fco.cmp(fcd.data()): # files identical? return None if fca == fco: # backwards, use working dir parent as ancestor fca = fcd.parents()[0] ui = repo.ui fd = fcd.path() binary = isbin(fcd) or isbin(fco) or isbin(fca) symlink = 'l' in fcd.flags() + fco.flags() tool, toolpath = _picktool(repo, ui, fd, binary, symlink) ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" % (tool, fd, binary, symlink)) if not tool or tool == 'internal:prompt': tool = "internal:local" if ui.promptchoice( _(" no tool found to merge %s\n" "keep (l)ocal or take (o)ther?") % fd, (_("&Local"), _("&Other")), 0): tool = "internal:other" if tool == "internal:local": return 0 if tool == "internal:other": repo.wwrite(fd, fco.data(), fco.flags()) return 0 if tool == "internal:fail": return 1 # do the actual merge a = repo.wjoin(fd) b = temp("base", fca) c = temp("other", fco) out = "" back = a + ".orig" util.copyfile(a, back) if orig != fco.path(): ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd)) else: ui.status(_("merging %s\n") % fd) ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca)) # do we attempt to simplemerge first? if _toolbool(ui, tool, "premerge", not (binary or symlink)): r = simplemerge.simplemerge(ui, 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=short(mynode), HG_OTHER_NODE=str(fco.changectx()), HG_BASE_NODE=str(fca.changectx()), HG_MY_ISLINK='l' in fcd.flags(), HG_OTHER_ISLINK='l' in fco.flags(), HG_BASE_ISLINK='l' in fca.flags()) if tool == "internal:merge": r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other']) elif tool == 'internal:dump': a = repo.wjoin(fd) util.copyfile(a, a + ".local") repo.wwrite(fd + ".other", fco.data(), fco.flags()) repo.wwrite(fd + ".base", fca.data(), fca.flags()) return 1 # unresolved 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"' % util.localpath(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("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data()): r = 1 if not r and _toolbool(ui, tool, "checkchanged"): if filecmp.cmp(repo.wjoin(fd), back): if ui.promptchoice( _(" output file %s appears unchanged\n" "was merge successful (yn)?") % fd, (_("&Yes"), _("&No")), 1): r = 1 if _toolbool(ui, tool, "fixeol"): _matcheol(repo.wjoin(fd), back) if r: ui.warn(_("merging %s failed!\n") % fd) else: os.unlink(back) os.unlink(b) os.unlink(c) return r
def filemerge(repo, mynode, orig, fcd, fco, fca): """perform a 3-way merge in the working directory mynode = parent node before merge orig = original local filename before merge fco = other file context fca = ancestor file context fcd = local file context for current/destination file """ 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 if not fco.cmp(fcd): # files identical? return None ui = repo.ui fd = fcd.path() binary = fcd.isbinary() or fco.isbinary() or fca.isbinary() symlink = 'l' in fcd.flags() + fco.flags() tool, toolpath = _picktool(repo, ui, fd, binary, symlink) ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" % (tool, fd, binary, symlink)) if not tool or tool == 'internal:prompt': tool = "internal:local" if ui.promptchoice( _(" no tool found to merge %s\n" "keep (l)ocal or take (o)ther?") % fd, (_("&Local"), _("&Other")), 0): tool = "internal:other" if tool == "internal:local": return 0 if tool == "internal:other": repo.wwrite(fd, fco.data(), fco.flags()) return 0 if tool == "internal:fail": return 1 # do the actual merge a = repo.wjoin(fd) b = temp("base", fca) c = temp("other", fco) out = "" back = a + ".orig" util.copyfile(a, back) if orig != fco.path(): ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd)) else: ui.status(_("merging %s\n") % fd) ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca)) # do we attempt to simplemerge first? try: premerge = _toolbool(ui, tool, "premerge", not (binary or symlink)) except error.ConfigError: premerge = _toolstr(ui, tool, "premerge").lower() valid = 'keep'.split() if premerge not in valid: _valid = ', '.join(["'" + v + "'" for v in valid]) raise error.ConfigError( _("%s.premerge not valid " "('%s' is neither boolean nor %s)") % (tool, premerge, _valid)) if premerge: r = simplemerge.simplemerge(ui, a, b, c, quiet=True) if not r: ui.debug(" premerge successful\n") os.unlink(back) os.unlink(b) os.unlink(c) return 0 if premerge != 'keep': util.copyfile(back, a) # restore from backup and try again env = dict(HG_FILE=fd, HG_MY_NODE=short(mynode), HG_OTHER_NODE=str(fco.changectx()), HG_BASE_NODE=str(fca.changectx()), HG_MY_ISLINK='l' in fcd.flags(), HG_OTHER_ISLINK='l' in fco.flags(), HG_BASE_ISLINK='l' in fca.flags()) if tool == "internal:merge": r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other']) elif tool == 'internal:dump': a = repo.wjoin(fd) util.copyfile(a, a + ".local") repo.wwrite(fd + ".other", fco.data(), fco.flags()) repo.wwrite(fd + ".base", fca.data(), fca.flags()) os.unlink(b) os.unlink(c) return 1 # unresolved 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 = util.interpolate(r'\$', replace, args, lambda s: '"%s"' % util.localpath(s)) r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env, out=ui.fout) if not r and (_toolbool(ui, tool, "checkconflicts") or 'conflicts' in _toollist(ui, tool, "check")): if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(), re.MULTILINE): r = 1 checked = False if 'prompt' in _toollist(ui, tool, "check"): checked = True if ui.promptchoice( _("was merge of '%s' successful (yn)?") % fd, (_("&Yes"), _("&No")), 1): r = 1 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or 'changed' in _toollist(ui, tool, "check")): if filecmp.cmp(repo.wjoin(fd), back): if ui.promptchoice( _(" output file %s appears unchanged\n" "was merge successful (yn)?") % fd, (_("&Yes"), _("&No")), 1): r = 1 if _toolbool(ui, tool, "fixeol"): _matcheol(repo.wjoin(fd), back) if r: if tool == "internal:merge": ui.warn( _("merging %s incomplete! " "(edit conflicts, then use 'hg resolve --mark')\n") % fd) else: ui.warn(_("merging %s failed!\n") % fd) else: os.unlink(back) os.unlink(b) os.unlink(c) return r