diff --git a/contrib/churn.py b/contrib/churn.py --- a/contrib/churn.py +++ b/contrib/churn.py @@ -125,6 +125,7 @@ def gather_stats(ui, repo, amap, revs=No ui.note("rev %d: %d lines by %s\n" % (rev, lines, who)) if progress: + nr_revs = max(nr_revs, 1) if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs): ui.write("%d%%.." % (int(100.0*cur_rev/nr_revs),)) sys.stdout.flush() @@ -144,6 +145,7 @@ def churn(ui, repo, **opts): return s[0:l] def graph(n, maximum, width, char): + maximum = max(1, maximum) n = int(n * width / float(maximum)) return char * (n) @@ -178,6 +180,8 @@ def churn(ui, repo, **opts): ordered = stats.items() ordered.sort(lambda x, y: cmp(y[1], x[1])) + if not ordered: + return maximum = ordered[0][1] width = get_tty_width() diff --git a/doc/hgrc.5.txt b/doc/hgrc.5.txt --- a/doc/hgrc.5.txt +++ b/doc/hgrc.5.txt @@ -17,7 +17,9 @@ FILES Mercurial reads configuration data from several files, if they exist. The names of these files depend on the system on which Mercurial is -installed. +installed. Windows registry keys contain PATH-like strings, every +part must reference a Mercurial.ini file or be a directory where *.rc +files will be read. (Unix) /etc/mercurial/hgrc.d/*.rc:: (Unix) /etc/mercurial/hgrc:: @@ -29,6 +31,8 @@ installed. (Unix) /etc/mercurial/hgrc.d/*.rc:: (Unix) /etc/mercurial/hgrc:: +(Windows) HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial:: + or:: (Windows) C:\Mercurial\Mercurial.ini:: Per-system configuration files, for the system on which Mercurial is running. Options in these files apply to all Mercurial 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 @@ -726,37 +726,21 @@ class hgweb(object): def rewrite_request(req): '''translate new web interface to traditional format''' - def spliturl(req): - def firstitem(query): - return query.split('&', 1)[0].split(';', 1)[0] - - def normurl(url): - inner = '/'.join([x for x in url.split('/') if x]) - tl = len(url) > 1 and url.endswith('/') and '/' or '' - - return '%s%s%s' % (url.startswith('/') and '/' or '', - inner, tl) - - root = normurl(urllib.unquote(req.env.get('REQUEST_URI', '').split('?', 1)[0])) - pi = normurl(req.env.get('PATH_INFO', '')) - if pi: - # strip leading / - pi = pi[1:] - if pi: - root = root[:root.rfind(pi)] - if req.env.has_key('REPO_NAME'): - rn = req.env['REPO_NAME'] + '/' - root += rn - query = pi[len(rn):] - else: - query = pi - else: - root += '?' - query = firstitem(req.env['QUERY_STRING']) - - return (root, query) - - req.url, query = spliturl(req) + req.url = req.env['SCRIPT_NAME'] + if not req.url.endswith('/'): + req.url += '/' + if req.env.has_key('REPO_NAME'): + req.url += req.env['REPO_NAME'] + '/' + + if req.env.get('PATH_INFO'): + parts = req.env.get('PATH_INFO').strip('/').split('/') + repo_parts = req.env.get('REPO_NAME', '').split('/') + if parts[:len(repo_parts)] == repo_parts: + parts = parts[len(repo_parts):] + query = '/'.join(parts) + else: + query = req.env['QUERY_STRING'].split('&', 1)[0] + query = query.split(';', 1)[0] if req.form.has_key('cmd'): # old style diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py +++ b/mercurial/hgweb/hgwebdir_mod.py @@ -17,7 +17,7 @@ from request import wsgirequest class hgwebdir(object): def __init__(self, config, parentui=None): def cleannames(items): - return [(util.pconvert(name.strip(os.sep)), path) + return [(util.pconvert(name).strip('/'), path) for name, path in items] self.parentui = parentui @@ -91,15 +91,11 @@ class hgwebdir(object): def config(section, name, default=None, untrusted=True): return parentui.config(section, name, default, untrusted) - url = req.env['REQUEST_URI'].split('?')[0] + url = req.env.get('SCRIPT_NAME', '') if not url.endswith('/'): url += '/' - pathinfo = req.env.get('PATH_INFO', '').strip('/') + '/' - base = url[:len(url) - len(pathinfo)] - if not base.endswith('/'): - base += '/' - staticurl = config('web', 'staticurl') or base + 'static/' + staticurl = config('web', 'staticurl') or url + 'static/' if not staticurl.endswith('/'): staticurl += '/' @@ -158,8 +154,10 @@ class hgwebdir(object): if u.configbool("web", "hidden", untrusted=True): continue - url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name]) - .replace("//", "/")) + '/' + parts = [req.env['PATH_INFO'], name] + if req.env['SCRIPT_NAME']: + parts.insert(0, req.env['SCRIPT_NAME']) + url = ('/'.join(parts).replace("//", "/")) + '/' # update time with local timezone try: diff --git a/mercurial/hgweb/server.py b/mercurial/hgweb/server.py --- a/mercurial/hgweb/server.py +++ b/mercurial/hgweb/server.py @@ -84,6 +84,7 @@ class _hgwebhandler(object, BaseHTTPServ env['SERVER_NAME'] = self.server.server_name env['SERVER_PORT'] = str(self.server.server_port) env['REQUEST_URI'] = self.path + env['SCRIPT_NAME'] = '' env['PATH_INFO'] = path_info env['REMOTE_HOST'] = self.client_address[0] env['REMOTE_ADDR'] = self.client_address[0] diff --git a/mercurial/hgweb/wsgicgi.py b/mercurial/hgweb/wsgicgi.py --- a/mercurial/hgweb/wsgicgi.py +++ b/mercurial/hgweb/wsgicgi.py @@ -16,6 +16,7 @@ def launch(application): util.set_binary(sys.stdout) environ = dict(os.environ.items()) + environ.setdefault('PATH_INFO', '') environ['wsgi.input'] = sys.stdin environ['wsgi.errors'] = sys.stderr environ['wsgi.version'] = (1, 0) @@ -61,13 +62,4 @@ def launch(application): headers_set[:] = [status, response_headers] return write - result = application(environ, start_response) - try: - for data in result: - if data: # don't send headers until body appears - write(data) - if not headers_sent: - write('') # send headers now if body was empty - finally: - if hasattr(result,'close'): - result.close() + application(environ, start_response) diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -885,6 +885,19 @@ def applydiff(ui, fp, changed, strip=1, dopatch = True gitworkdone = False + def getpatchfile(afile, bfile, hunk): + try: + if sourcefile: + targetfile = patchfile(ui, sourcefile) + else: + targetfile = selectfile(afile, bfile, hunk, + strip, reverse) + targetfile = patchfile(ui, targetfile) + return targetfile + except PatchError, err: + ui.warn(str(err) + '\n') + return None + while True: newfile = False x = lr.readline() @@ -912,22 +925,20 @@ def applydiff(ui, fp, changed, strip=1, continue hunknum += 1 if not current_file: - if sourcefile: - current_file = patchfile(ui, sourcefile) - else: - current_file = selectfile(afile, bfile, current_hunk, - strip, reverse) - current_file = patchfile(ui, current_file) + current_file = getpatchfile(afile, bfile, current_hunk) + if not current_file: + current_file, current_hunk = None, None + rejects += 1 + continue elif state == BFILE and x.startswith('GIT binary patch'): current_hunk = binhunk(changed[bfile[2:]][1]) + hunknum += 1 if not current_file: - if sourcefile: - current_file = patchfile(ui, sourcefile) - else: - current_file = selectfile(afile, bfile, current_hunk, - strip, reverse) - current_file = patchfile(ui, current_file) - hunknum += 1 + current_file = getpatchfile(afile, bfile, current_hunk) + if not current_file: + current_file, current_hunk = None, None + rejects += 1 + continue current_hunk.extract(fp) elif x.startswith('diff --git'): # check for git diff, scanning the whole patch file if needed diff --git a/mercurial/util_win32.py b/mercurial/util_win32.py --- a/mercurial/util_win32.py +++ b/mercurial/util_win32.py @@ -16,6 +16,7 @@ import win32api from i18n import _ import errno, os, pywintypes, win32con, win32file, win32process import cStringIO, winerror +import osutil from win32com.shell import shell,shellcon class WinError: @@ -179,6 +180,20 @@ def testpid(pid): def system_rcpath_win32(): '''return default os-specific hgrc search path''' + try: + value = win32api.RegQueryValue( + win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial') + rcpath = [] + 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 + except pywintypes.error: + pass proc = win32api.GetCurrentProcess() try: # This will fail on windows < NT diff --git a/tests/test-hgweb b/tests/test-hgweb --- a/tests/test-hgweb +++ b/tests/test-hgweb @@ -1,4 +1,5 @@ #!/bin/sh +# Some tests for hgweb. Tests static files, plain files and different 404's. hg init test cd test diff --git a/tests/test-hgweb-no-request-uri b/tests/test-hgweb-no-request-uri new file mode 100755 --- /dev/null +++ b/tests/test-hgweb-no-request-uri @@ -0,0 +1,77 @@ +#!/bin/sh +# This tests if hgweb and hgwebdir still work if the REQUEST_URI variable is +# no longer passed with the request. Instead, SCRIPT_NAME and PATH_INFO +# should be used from d74fc8dec2b4 onward to route the request. + +mkdir repo +cd repo +hg init +echo foo > bar +hg add bar +hg commit -m "test" -d "0 0" -u "Testing" +hg tip + +cat > request.py < + + + http://127.0.0.1/ + + + repo Changelog + 1970-01-01T00:00:00+00:00 + + + test + http://www.selenic.com/mercurial/#changeset-4cbec7e6f8c42eb52b6b52670e1f7560ae9a101e + + + Testing + Testing + + 1970-01-01T00:00:00+00:00 + 1970-01-01T00:00:00+00:00 + +
+
test
+
+
+
+ +
+ +---- ERRORS + +---- HEADERS +200 Script output follows +---- DATA +[('content-type', 'text/plain; charset=ascii')] + +-rw-r--r-- 4 bar + + + +---- ERRORS + +---- HEADERS +200 Script output follows +---- DATA +[('content-type', 'text/plain; charset=ascii')] + +/repo/ + + +---- ERRORS + +---- HEADERS +200 Script output follows +---- DATA +[('content-type', 'text/plain; charset=ascii')] + +-rw-r--r-- 4 bar + + + +---- ERRORS + diff --git a/tests/test-hgwebdir b/tests/test-hgwebdir --- a/tests/test-hgwebdir +++ b/tests/test-hgwebdir @@ -1,4 +1,6 @@ #!/bin/sh +# Tests some basic hgwebdir functionality. Tests setting up paths and +# collection, different forms of 404s and the subdirectory support. mkdir webdir cd webdir @@ -39,17 +41,37 @@ echo % should succeed echo % should give a 404 - repo is not published "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/c/file/tip/c?style=raw' +cat > paths.conf <> $DAEMON_PIDS + +echo % should succeed, slashy names +"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a?style=atom' \ + | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" +"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/?style=atom' \ + | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" +"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/file/tip/a?style=raw' + cat > collections.conf <> $DAEMON_PIDS echo % should succeed -"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw' -"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/a/file/tip/a?style=raw' -"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/b/file/tip/b?style=raw' -"$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/c/file/tip/c?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/a/file/tip/a?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/b/file/tip/b?style=raw' +"$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/c/file/tip/c?style=raw' diff --git a/tests/test-hgwebdir.out b/tests/test-hgwebdir.out --- a/tests/test-hgwebdir.out +++ b/tests/test-hgwebdir.out @@ -24,6 +24,84 @@ 404 Not Found error: repository c not found +% should succeed, slashy names +200 Script output follows + + +/b/ +/t/a/ + +200 Script output follows + + +/t/a/ + +200 Script output follows + + +/t/a/ + +200 Script output follows + + + + + http://127.0.0.1/t/a/ + + + t/a Changelog + 1970-01-01T00:00:01+00:00 + + + a + http://127.0.0.1/mercurial/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab + + + test + test + + 1970-01-01T00:00:01+00:00 + 1970-01-01T00:00:01+00:00 + +
+
a
+
+
+
+ +
+200 Script output follows + + + + + http://127.0.0.1/t/a/ + + + t/a Changelog + 1970-01-01T00:00:01+00:00 + + + a + http://127.0.0.1/mercurial/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab + + + test + test + + 1970-01-01T00:00:01+00:00 + 1970-01-01T00:00:01+00:00 + +
+
a
+
+
+
+ +
+200 Script output follows + +a % should succeed 200 Script output follows diff --git a/tests/test-mq-missingfiles b/tests/test-mq-missingfiles new file mode 100755 --- /dev/null +++ b/tests/test-mq-missingfiles @@ -0,0 +1,69 @@ +#!/bin/sh + +# Test issue835: +# qpush fails immediately when patching a missing file, but +# remaining added files are still created empty which will +# trick a future qrefresh. + +cat > writelines.py <> $HGRCPATH +echo "mq=" >> $HGRCPATH + +hg init normal +cd normal +python ../writelines.py b 10 'a\n' +hg ci -Am addb +echo a > a +python ../writelines.py b 2 'b\n' 10 'a\n' 2 'c\n' +echo c > c +hg add a c +hg qnew -f changeb +hg qpop +hg rm b +hg ci -Am rmb +echo % push patch with missing target +hg qpush +echo % display added files +cat a +cat c +cd .. + + +echo "[diff]" >> $HGRCPATH +echo "git=1" >> $HGRCPATH + +hg init git +cd git +python ../writelines.py b 1 '\x00' +hg ci -Am addb +echo a > a +python ../writelines.py b 1 '\x01' 1 '\x00' +echo c > c +hg add a c +hg qnew -f changeb +hg qpop +hg rm b +hg ci -Am rmb +echo % push git patch with missing target +hg qpush 2>&1 | sed -e 's/b:.*/b: No such file or directory/' +hg st +echo % display added files +cat a +cat c +cd .. + diff --git a/tests/test-mq-missingfiles.out b/tests/test-mq-missingfiles.out new file mode 100644 --- /dev/null +++ b/tests/test-mq-missingfiles.out @@ -0,0 +1,25 @@ +adding b +Patch queue now empty +% push patch with missing target +applying changeb +unable to find b or b for patching +unable to find b or b for patching +patch failed, unable to continue (try -v) +patch failed, rejects left in working dir +Errors during apply, please fix and refresh changeb +% display added files +a +c +adding b +Patch queue now empty +% push git patch with missing target +applying changeb +unable to find b or b for patching +patch failed, unable to continue (try -v) +b: No such file or directory +b not tracked! +patch failed, rejects left in working dir +Errors during apply, please fix and refresh changeb +% display added files +a +c diff --git a/tests/test-newcgi b/tests/test-newcgi new file mode 100755 --- /dev/null +++ b/tests/test-newcgi @@ -0,0 +1,91 @@ +#!/bin/sh +# This tests if CGI files from after d0db3462d568 but +# before d74fc8dec2b4 still work. + +hg init test + +cat >hgweb.cgi <hgweb.config <hgwebdir.cgi <page1 2>&1 ; echo $? +python hgwebdir.cgi >page2 2>&1 ; echo $? +PATH_INFO="/test/" +PATH_TRANSLATED="/var/something/test.cgi" +REQUEST_URI="/test/test/" +SCRIPT_URI="http://hg.omnifarious.org/test/test/" +SCRIPT_URL="/test/test/" +python hgwebdir.cgi >page3 2>&1 ; echo $? +fgrep -i error page1 page2 page3 && exit 1 +exit 0 diff --git a/tests/test-newcgi.out b/tests/test-newcgi.out new file mode 100644 --- /dev/null +++ b/tests/test-newcgi.out @@ -0,0 +1,3 @@ +0 +0 +0 diff --git a/tests/test-newercgi b/tests/test-newercgi new file mode 100755 --- /dev/null +++ b/tests/test-newercgi @@ -0,0 +1,84 @@ +#!/bin/sh +# This is a rudimentary test of the CGI files as of d74fc8dec2b4. + +hg init test + +cat >hgweb.cgi <hgweb.config <hgwebdir.cgi <page1 2>&1 ; echo $? +python hgwebdir.cgi >page2 2>&1 ; echo $? +PATH_INFO="/test/" +PATH_TRANSLATED="/var/something/test.cgi" +REQUEST_URI="/test/test/" +SCRIPT_URI="http://hg.omnifarious.org/test/test/" +SCRIPT_URL="/test/test/" +python hgwebdir.cgi >page3 2>&1 ; echo $? +fgrep -i error page1 page2 page3 && exit 1 +exit 0 diff --git a/tests/test-newercgi.out b/tests/test-newercgi.out new file mode 100644 --- /dev/null +++ b/tests/test-newercgi.out @@ -0,0 +1,3 @@ +0 +0 +0 diff --git a/tests/test-non-interactive-wsgi b/tests/test-non-interactive-wsgi --- a/tests/test-non-interactive-wsgi +++ b/tests/test-non-interactive-wsgi @@ -1,4 +1,6 @@ #!/bin/sh +# Tests if hgweb can run without touching sys.stdin, as is required +# by the WSGI standard and strictly implemented by mod_wsgi. mkdir repo cd repo diff --git a/tests/test-oldcgi b/tests/test-oldcgi --- a/tests/test-oldcgi +++ b/tests/test-oldcgi @@ -1,4 +1,5 @@ #!/bin/sh +# This tests if CGI files from before d0db3462d568 still work. hg init test