# HG changeset patch # User Vadim Gelfer # Date 1152650871 25200 # Node ID 0a30407fff72a689f15715fbd51ed8f725d4fa63 # Parent 911b56853fddd4745bdd440ea3410b19c84dc681# Parent 457846f400e8da1f94aeb721693a5578f01509eb merge with crew. diff --git a/contrib/darcs2hg.py b/contrib/darcs2hg.py --- a/contrib/darcs2hg.py +++ b/contrib/darcs2hg.py @@ -4,12 +4,11 @@ # ----------------------------------------------------------------------------- # Project : Basic Darcs to Mercurial conversion script # ----------------------------------------------------------------------------- -# Author : Sebastien Pierre +# Authors : Sebastien Pierre +# TK Soh +# ----------------------------------------------------------------------------- # Creation : 24-May-2006 -# Last mod : 26-May-2006 -# History : -# 26-May-2006 - Updated -# 24-May-2006 - First implementation +# Last mod : 05-Jun-2006 # ----------------------------------------------------------------------------- import os, sys @@ -21,12 +20,14 @@ DARCS_REPO = None HG_REPO = None USAGE = """\ -%s DARCSREPO HGREPO +%s DARCSREPO HGREPO [SKIP] Converts the given Darcs repository to a new Mercurial repository. The given HGREPO must not exist, as it will be created and filled up (this will avoid overwriting valuable data. + In case an error occurs within the process, you can resume the process by + giving the last successfuly applied change number. """ % (os.path.basename(sys.argv[0])) # ------------------------------------------------------------------------------ @@ -35,7 +36,7 @@ USAGE = """\ # # ------------------------------------------------------------------------------ -def cmd(text, path=None): +def cmd(text, path=None, silent=False): """Executes a command, in the given directory (if any), and returns the command result as a string.""" cwd = None @@ -43,7 +44,7 @@ def cmd(text, path=None): path = os.path.abspath(path) cwd = os.getcwd() os.chdir(path) - print text + if not silent: print "> ", text res = os.popen(text).read() if path: os.chdir(cwd) @@ -53,6 +54,14 @@ def writefile(path, data): """Writes the given data into the given file.""" f = file(path, "w") ; f.write(data) ; f.close() +def error( *args ): + sys.stderr.write("ERROR: ") + for a in args: sys.stderr.write(str(a)) + sys.stderr.write("\n") + sys.stderr.write("You can make manual fixes if necessary and then resume by" + " giving the last changeset number") + sys.exit(-1) + # ------------------------------------------------------------------------------ # # Darcs interface @@ -64,7 +73,6 @@ def darcs_changes(darcsRepo): chronological list of changes as (change name, change summary).""" changes = cmd("darcs changes --reverse --xml-output", darcsRepo) doc = xml_dom.parseString(changes) - res = [] for patch_node in doc.childNodes[0].childNodes: name = filter(lambda n:n.nodeName == "name", patch_node.childNodes) comm = filter(lambda n:n.nodeName == "comment", patch_node.childNodes) @@ -73,11 +81,22 @@ def darcs_changes(darcsRepo): if not comm: comm = "" else: comm = comm[0].childNodes[0].data author = patch_node.getAttribute("author") - date = patch_node.getAttribute("date") - yield author, date, name, comm + date = patch_node.getAttribute("date") + chash = os.path.splitext(patch_node.getAttribute("hash"))[0] + yield author, date, name, chash, comm -def darcs_pull(hg_repo, darcs_repo, change): - cmd("darcs pull '%s' --all --patches='%s'" % (darcs_repo, change), hg_repo) +def darcs_tip(darcs_repo): + changes = cmd("darcs changes",darcs_repo,silent=True) + changes = filter(lambda l:l.strip().startswith("* "), changes.split("\n")) + return len(changes) + +def darcs_pull(hg_repo, darcs_repo, chash): + old_tip = darcs_tip(darcs_repo) + res = cmd("darcs pull '%s' --all --match='hash %s'" % (darcs_repo, chash), hg_repo) + print res + new_tip = darcs_tip(darcs_repo) + if not new_tip != old_tip + 1: + error("Darcs pull did not work as expected: " + res) # ------------------------------------------------------------------------------ # @@ -88,10 +107,24 @@ def darcs_pull(hg_repo, darcs_repo, chan def hg_commit( hg_repo, text, author, date ): fd, tmpfile = tempfile.mkstemp(prefix="darcs2hg_") writefile(tmpfile, text) + old_tip = hg_tip(hg_repo) cmd("hg add -X _darcs", hg_repo) cmd("hg remove -X _darcs --after", hg_repo) - cmd("hg commit -l %s -u '%s' -d '%s 0'" % (tmpfile, author, date), hg_repo) + res = cmd("hg commit -l %s -u '%s' -d '%s 0'" % (tmpfile, author, date), hg_repo) os.unlink(tmpfile) + new_tip = hg_tip(hg_repo) + if not new_tip == old_tip + 1: + # Sometimes we may have empty commits, we simply skip them + if res.strip().lower().find("nothing changed") != -1: + pass + else: + error("Mercurial commit did not work as expected: " + res) + +def hg_tip( hg_repo ): + """Returns the latest local revision number in the given repository.""" + tip = cmd("hg tip", hg_repo, silent=True) + tip = tip.split("\n")[0].split(":")[1].strip() + return int(tip) # ------------------------------------------------------------------------------ # @@ -105,26 +138,41 @@ if __name__ == "__main__": if len(args) == 2: darcs_repo = os.path.abspath(args[0]) hg_repo = os.path.abspath(args[1]) + skip = None + elif len(args) == 3: + darcs_repo = os.path.abspath(args[0]) + hg_repo = os.path.abspath(args[1]) + skip = int(args[2]) else: print USAGE sys.exit(-1) # Initializes the target repo if not os.path.isdir(darcs_repo + "/_darcs"): - print "No darcs directory found at: " + darc_repo + print "No darcs directory found at: " + darcs_repo sys.exit(-1) if not os.path.isdir(hg_repo): os.mkdir(hg_repo) - else: - print "Given HG repository must not exist. It will be created" + elif skip == None: + print "Given HG repository must not exist when no SKIP is specified." sys.exit(-1) - cmd("hg init '%s'" % (hg_repo)) - cmd("darcs initialize", hg_repo) + if skip == None: + cmd("hg init '%s'" % (hg_repo)) + cmd("darcs initialize", hg_repo) # Get the changes from the Darcs repository - for author, date, summary, description in darcs_changes(darcs_repo): - text = summary + "\n" + description - darcs_pull(hg_repo, darcs_repo, summary) - epoch = int(mktime(strptime(date, '%Y%m%d%H%M%S'))) - hg_commit(hg_repo, text, author, epoch) + change_number = 0 + for author, date, summary, chash, description in darcs_changes(darcs_repo): + print "== changeset", change_number, + if skip != None and change_number <= skip: + print "(skipping)" + else: + text = summary + "\n" + description + darcs_pull(hg_repo, darcs_repo, chash) + # The commit hash has a date like 20021020201112 + # --------------------------------YYYYMMDDHHMMSS + date = chash.split("-")[0] + epoch = int(mktime(strptime(date, '%Y%m%d%H%M%S'))) + hg_commit(hg_repo, text, author, epoch) + change_number += 1 + print "Darcs repository (_darcs) was not deleted. You can keep or remove it." # EOF - diff --git a/doc/hgrc.5.txt b/doc/hgrc.5.txt --- a/doc/hgrc.5.txt +++ b/doc/hgrc.5.txt @@ -309,6 +309,9 @@ smtp:: Optional. Password to authenticate to SMTP server with. If username is specified, password must also be specified. Default: none. + local_hostname;; + Optional. It's the hostname that the sender can use to identify itself + to the MTA. paths:: Assigns symbolic names to repositories. The left side is the diff --git a/mercurial/bdiff.c b/mercurial/bdiff.c --- a/mercurial/bdiff.c +++ b/mercurial/bdiff.c @@ -65,7 +65,7 @@ static inline uint32_t rol32(uint32_t wo int splitlines(const char *a, int len, struct line **lr) { - int h, i; + int g, h, i; const char *p, *b = a; struct line *l; @@ -82,7 +82,16 @@ int splitlines(const char *a, int len, s /* build the line array and calculate hashes */ h = 0; for (p = a; p < a + len; p++) { - h = *p + rol32(h, 7); /* a simple hash from GNU diff */ + /* + * a simple hash from GNU diff, with better collision + * resistance from hashpjw. this slows down common + * case by 10%, but speeds up worst case by 100x. + */ + h = *p + rol32(h, 7); + if ((g = h & 0xf0000000)) { + h ^= g >> 24; + h ^= g; + } if (*p == '\n' || p == a + len - 1) { l->len = p - b + 1; l->h = h * l->len; diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -231,7 +231,7 @@ def revrange(ui, repo, revs): """Yield revision as strings from a list of revision specifications.""" seen = {} for spec in revs: - if spec.find(revrangesep) >= 0: + if revrangesep in spec: start, end = spec.split(revrangesep, 1) start = revfix(repo, start, 0) end = revfix(repo, end, repo.changelog.count() - 1) @@ -405,23 +405,33 @@ def dodiff(fp, ui, repo, node1, node2, f diffopts = ui.diffopts() showfunc = opts.get('show_function') or diffopts['showfunc'] ignorews = opts.get('ignore_all_space') or diffopts['ignorews'] + ignorewsamount = opts.get('ignore_space_change') or \ + diffopts['ignorewsamount'] + ignoreblanklines = opts.get('ignore_blank_lines') or \ + diffopts['ignoreblanklines'] for f in modified: to = None if f in mmap: to = repo.file(f).read(mmap[f]) tn = read(f) fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text, - showfunc=showfunc, ignorews=ignorews)) + showfunc=showfunc, ignorews=ignorews, + ignorewsamount=ignorewsamount, + ignoreblanklines=ignoreblanklines)) for f in added: to = None tn = read(f) fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text, - showfunc=showfunc, ignorews=ignorews)) + showfunc=showfunc, ignorews=ignorews, + ignorewsamount=ignorewsamount, + ignoreblanklines=ignoreblanklines)) for f in removed: to = repo.file(f).read(mmap[f]) tn = None fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text, - showfunc=showfunc, ignorews=ignorews)) + showfunc=showfunc, ignorews=ignorews, + ignorewsamount=ignorewsamount, + ignoreblanklines=ignoreblanklines)) def trimuser(ui, name, rev, revcache): """trim the name of the user who committed a change""" @@ -941,6 +951,10 @@ def clone(ui, source, dest=None, **opts) hardlinking. See pull for valid source format details. + + It is possible to specify an ssh:// URL as the destination, but no + .hg/hgrc will be created on the remote side. Look at the help text + for the pull command for important details about ssh:// URLs. """ if dest is None: dest = os.path.basename(os.path.normpath(source)) @@ -1950,6 +1964,10 @@ def init(ui, dest="."): directory does not exist, it is created. If no directory is given, the current directory is used. + + It is possible to specify an ssh:// URL as the destination. + Look at the help text for the pull command for important details + about ssh:// URLs. """ hg.repository(ui, dest, create=1) @@ -2218,15 +2236,16 @@ def pull(ui, repo, source="default", **o Valid URLs are of the form: local/filesystem/path - http://[user@]host[:port][/path] - https://[user@]host[:port][/path] - ssh://[user@]host[:port][/path] + http://[user@]host[:port]/[path] + https://[user@]host[:port]/[path] + ssh://[user@]host[:port]/[path] Some notes about using SSH with Mercurial: - SSH requires an accessible shell account on the destination machine and a copy of hg in the remote path or specified with as remotecmd. - - /path is relative to the remote user's home directory by default. - Use two slashes at the start of a path to specify an absolute path. + - path is relative to the remote user's home directory by default. + Use an extra slash at the start of a path to specify an absolute path: + ssh://example.com//tmp/repository - Mercurial doesn't use its own compression via SSH; the right thing to do is to configure it in your ~/.ssh/ssh_config, e.g.: Host *.mylocalnetwork.example.com @@ -2270,10 +2289,13 @@ def push(ui, repo, dest=None, **opts): Valid URLs are of the form: local/filesystem/path - ssh://[user@]host[:port][/path] + ssh://[user@]host[:port]/[path] Look at the help text for the pull command for important details about ssh:// URLs. + + Pushing to http:// and https:// URLs is possible, too, if this + feature is enabled on the remote Mercurial server. """ dest = ui.expandpath(dest or 'default-push', dest or 'default') @@ -2742,7 +2764,7 @@ def tag(ui, repo, name, rev_=None, **opt disallowed = (revrangesep, '\r', '\n') for c in disallowed: - if name.find(c) >= 0: + if c in name: raise util.Abort(_("%s cannot be used in a tag name") % repr(c)) repo.hook('pretag', throw=True, node=r, tag=name, @@ -3018,6 +3040,10 @@ table = { _('show which function each change is in')), ('w', 'ignore-all-space', None, _('ignore white space when comparing lines')), + ('b', 'ignore-space-change', None, + _('ignore changes in the amount of white space')), + ('B', 'ignore-blank-lines', None, + _('ignore changes whose lines are all blank')), ('I', 'include', [], _('include names matching the given patterns')), ('X', 'exclude', [], _('exclude names matching the given patterns'))], _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')), diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -34,14 +34,14 @@ class filelog(revlog): t = self.revision(node) if not t.startswith('\1\n'): return t - s = t.find('\1\n', 2) + s = t.index('\1\n', 2) return t[s+2:] def readmeta(self, node): t = self.revision(node) if not t.startswith('\1\n'): return {} - s = t.find('\1\n', 2) + s = t.index('\1\n', 2) mt = t[2:s] m = {} for l in mt.splitlines(): diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -61,8 +61,7 @@ def repository(ui, path=None, create=0): if not path: path = '' scheme = path if scheme: - c = scheme.find(':') - scheme = c >= 0 and scheme[:c] + scheme = scheme.split(":", 1)[0] ctor = schemes.get(scheme) or schemes['file'] if create: try: diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py +++ b/mercurial/hgweb/hgweb_mod.py @@ -133,21 +133,29 @@ class hgweb(object): diffopts = self.repo.ui.diffopts() showfunc = diffopts['showfunc'] ignorews = diffopts['ignorews'] + ignorewsamount = diffopts['ignorewsamount'] + ignoreblanklines = diffopts['ignoreblanklines'] for f in modified: to = r.file(f).read(mmap1[f]) tn = r.file(f).read(mmap2[f]) yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, - showfunc=showfunc, ignorews=ignorews), f, tn) + showfunc=showfunc, ignorews=ignorews, + ignorewsamount=ignorewsamount, + ignoreblanklines=ignoreblanklines), f, tn) for f in added: to = None tn = r.file(f).read(mmap2[f]) yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, - showfunc=showfunc, ignorews=ignorews), f, tn) + showfunc=showfunc, ignorews=ignorews, + ignorewsamount=ignorewsamount, + ignoreblanklines=ignoreblanklines), f, tn) for f in removed: to = r.file(f).read(mmap1[f]) tn = None yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, - showfunc=showfunc, ignorews=ignorews), f, tn) + showfunc=showfunc, ignorews=ignorews, + ignorewsamount=ignorewsamount, + ignoreblanklines=ignoreblanklines), f, tn) def changelog(self, pos): def changenav(**map): @@ -462,7 +470,7 @@ class hgweb(object): continue remain = f[l:] if "/" in remain: - short = remain[:remain.find("/") + 1] # bleah + short = remain[:remain.index("/") + 1] # bleah files[short] = (f, None) else: short = os.path.basename(remain) diff --git a/mercurial/hgweb/server.py b/mercurial/hgweb/server.py --- a/mercurial/hgweb/server.py +++ b/mercurial/hgweb/server.py @@ -127,6 +127,11 @@ class _hgwebhandler(object, BaseHTTPServ if h[0].lower() == 'content-length': should_close = False self.length = int(h[1]) + # The value of the Connection header is a list of case-insensitive + # tokens separated by commas and optional whitespace. + if 'close' in [token.strip().lower() for token in + self.headers.get('connection', '').split(',')]: + should_close = True if should_close: self.send_header('Connection', 'close') self.close_connection = should_close diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -74,8 +74,8 @@ class localrepository(object): self.transhandle = None if create: - if not os.path.exists(path): - os.mkdir(path) + if not os.path.exists(path): + os.mkdir(path) os.mkdir(self.path) os.mkdir(self.join("data")) @@ -101,9 +101,13 @@ class localrepository(object): try: obj = __import__(modname) except ImportError: - raise util.Abort(_('%s hook is invalid ' - '(import of "%s" failed)') % - (hname, modname)) + try: + # extensions are loaded with hgext_ prefix + obj = __import__("hgext_%s" % modname) + except ImportError: + raise util.Abort(_('%s hook is invalid ' + '(import of "%s" failed)') % + (hname, modname)) try: for p in funcname.split('.')[1:]: obj = getattr(obj, p) diff --git a/mercurial/lock.py b/mercurial/lock.py --- a/mercurial/lock.py +++ b/mercurial/lock.py @@ -85,14 +85,14 @@ class lock(object): # see if locker is alive. if locker is on this machine but # not alive, we can safely break lock. locker = util.readlock(self.f) - c = locker.find(':') - if c == -1: + try: + host, pid = locker.split(":", 1) + except ValueError: return locker - host = locker[:c] if host != self.host: return locker try: - pid = int(locker[c+1:]) + pid = int(pid) except: return locker if util.testpid(pid): diff --git a/mercurial/mdiff.py b/mercurial/mdiff.py --- a/mercurial/mdiff.py +++ b/mercurial/mdiff.py @@ -20,7 +20,8 @@ def splitnewlines(text): return lines def unidiff(a, ad, b, bd, fn, r=None, text=False, - showfunc=False, ignorews=False): + showfunc=False, ignorews=False, ignorewsamount=False, + ignoreblanklines=False): if not a and not b: return "" epoch = util.datestr((0, 0)) @@ -49,7 +50,9 @@ def unidiff(a, ad, b, bd, fn, r=None, te al = splitnewlines(a) bl = splitnewlines(b) l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn, - showfunc=showfunc, ignorews=ignorews)) + showfunc=showfunc, ignorews=ignorews, + ignorewsamount=ignorewsamount, + ignoreblanklines=ignoreblanklines)) if not l: return "" # difflib uses a space, rather than a tab l[0] = "%s\t%s\n" % (l[0][:-2], ad) @@ -72,8 +75,10 @@ def unidiff(a, ad, b, bd, fn, r=None, te # context is the number of context lines # showfunc enables diff -p output # ignorews ignores all whitespace changes in the diff +# ignorewsamount ignores changes in the amount of whitespace +# ignoreblanklines ignores changes whose lines are all blank def bunidiff(t1, t2, l1, l2, header1, header2, context=3, showfunc=False, - ignorews=False): + ignorews=False, ignorewsamount=False, ignoreblanklines=False): def contextend(l, len): ret = l + context if ret > len: @@ -116,6 +121,11 @@ def bunidiff(t1, t2, l1, l2, header1, he if showfunc: funcre = re.compile('\w') + if ignorewsamount: + wsamountre = re.compile('[ \t]+') + wsappendedre = re.compile(' \n') + if ignoreblanklines: + wsblanklinesre = re.compile('\n') if ignorews: wsre = re.compile('[ \t]') @@ -149,6 +159,20 @@ def bunidiff(t1, t2, l1, l2, header1, he if not old and not new: continue + if ignoreblanklines: + wsold = wsblanklinesre.sub('', "".join(old)) + wsnew = wsblanklinesre.sub('', "".join(new)) + if wsold == wsnew: + continue + + if ignorewsamount: + wsold = wsamountre.sub(' ', "".join(old)) + wsold = wsappendedre.sub('\n', wsold) + wsnew = wsamountre.sub(' ', "".join(new)) + wsnew = wsappendedre.sub('\n', wsnew) + if wsold == wsnew: + continue + if ignorews: wsold = wsre.sub('', "".join(old)) wsnew = wsre.sub('', "".join(new)) diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -76,7 +76,7 @@ class ui(object): if root is None: root = os.path.expanduser('~') for name, path in self.configitems("paths"): - if path and path.find("://") == -1 and not os.path.isabs(path): + if path and "://" not in path and not os.path.isabs(path): self.cdata.set("paths", name, os.path.join(root, path)) def setconfig(self, section, name, val): @@ -172,7 +172,8 @@ class ui(object): def diffopts(self): if self.diffcache: return self.diffcache - result = {'showfunc': True, 'ignorews': False} + result = {'showfunc': True, 'ignorews': False, + 'ignorewsamount': False, 'ignoreblanklines': False} for key, value in self.configitems("diff"): if value: result[key.lower()] = (value.lower() == 'true') @@ -208,7 +209,7 @@ class ui(object): def expandpath(self, loc, default=None): """Return repository location relative to cwd or from [paths]""" - if loc.find("://") != -1 or os.path.exists(loc): + if "://" in loc or os.path.exists(loc): return loc path = self.config("paths", loc) @@ -298,7 +299,8 @@ class ui(object): def smtp(): '''send mail using smtp.''' - s = smtplib.SMTP() + local_hostname = self.config('smtp', 'local_hostname') + s = smtplib.SMTP(local_hostname=local_hostname) mailhost = self.config('smtp', 'host') if not mailhost: raise util.Abort(_('no [smtp]host in hgrc - cannot send mail')) diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -620,7 +620,7 @@ else: def parse_patch_output(output_line): """parses the output produced by patch and returns the file name""" pf = output_line[14:] - if pf.startswith("'") and pf.endswith("'") and pf.find(" ") >= 0: + if pf.startswith("'") and pf.endswith("'") and " " in pf: pf = pf[1:-1] # Remove the quotes return pf diff --git a/tests/test-help.out b/tests/test-help.out --- a/tests/test-help.out +++ b/tests/test-help.out @@ -173,12 +173,14 @@ diff repository (or selected files) options: - -r --rev revision - -a --text treat all files as text - -p --show-function show which function each change is in - -w --ignore-all-space ignore white space when comparing lines - -I --include include names matching the given patterns - -X --exclude exclude names matching the given patterns + -r --rev revision + -a --text treat all files as text + -p --show-function show which function each change is in + -w --ignore-all-space ignore white space when comparing lines + -b --ignore-space-change ignore changes in the amount of white space + -B --ignore-blank-lines ignore changes whose lines are all blank + -I --include include names matching the given patterns + -X --exclude exclude names matching the given patterns hg status [OPTION]... [FILE]... show changed files in the working directory