##// END OF EJS Templates
Merge with crew.
Thomas Arendsen Hein -
r2531:7a90e0c7 merge default
parent child Browse files
Show More
@@ -0,0 +1,69 b''
1 # hgweb/wsgicgi.py - CGI->WSGI translator
2 #
3 # Copyright 2006 Eric Hopper <hopper@omnifarious.org>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7 #
8 # This was originally copied from the public domain code at
9 # http://www.python.org/dev/peps/pep-0333/#the-server-gateway-side
10
11 import os, sys
12
13 def launch(application):
14
15 environ = dict(os.environ.items())
16 environ['wsgi.input'] = sys.stdin
17 environ['wsgi.errors'] = sys.stderr
18 environ['wsgi.version'] = (1,0)
19 environ['wsgi.multithread'] = False
20 environ['wsgi.multiprocess'] = True
21 environ['wsgi.run_once'] = True
22
23 if environ.get('HTTPS','off') in ('on','1'):
24 environ['wsgi.url_scheme'] = 'https'
25 else:
26 environ['wsgi.url_scheme'] = 'http'
27
28 headers_set = []
29 headers_sent = []
30
31 def write(data):
32 if not headers_set:
33 raise AssertionError("write() before start_response()")
34
35 elif not headers_sent:
36 # Before the first output, send the stored headers
37 status, response_headers = headers_sent[:] = headers_set
38 sys.stdout.write('Status: %s\r\n' % status)
39 for header in response_headers:
40 sys.stdout.write('%s: %s\r\n' % header)
41 sys.stdout.write('\r\n')
42
43 sys.stdout.write(data)
44 sys.stdout.flush()
45
46 def start_response(status,response_headers,exc_info=None):
47 if exc_info:
48 try:
49 if headers_sent:
50 # Re-raise original exception if headers sent
51 raise exc_info[0], exc_info[1], exc_info[2]
52 finally:
53 exc_info = None # avoid dangling circular ref
54 elif headers_set:
55 raise AssertionError("Headers already set!")
56
57 headers_set[:] = [status,response_headers]
58 return write
59
60 result = application(environ, start_response)
61 try:
62 for data in result:
63 if data: # don't send headers until body appears
64 write(data)
65 if not headers_sent:
66 write('') # send headers now if body was empty
67 finally:
68 if hasattr(result,'close'):
69 result.close()
@@ -0,0 +1,81 b''
1 #!/bin/sh
2
3 hg init a
4 echo line 1 > a/a
5 hg --cwd a ci -d '0 0' -Ama
6
7 echo line 2 >> a/a
8 hg --cwd a ci -u someone -d '1 0' -m'second change'
9
10 echo % import exported patch
11 hg clone -r0 a b
12 hg --cwd a export tip > tip.patch
13 hg --cwd b import ../tip.patch
14 echo % message should be same
15 hg --cwd b tip | grep 'second change'
16 echo % committer should be same
17 hg --cwd b tip | grep someone
18 rm -rf b
19
20 echo % import of plain diff should fail without message
21 hg clone -r0 a b
22 hg --cwd a diff -r0:1 > tip.patch
23 hg --cwd b import ../tip.patch
24 rm -rf b
25
26 echo % import of plain diff should be ok with message
27 hg clone -r0 a b
28 hg --cwd a diff -r0:1 > tip.patch
29 hg --cwd b import -mpatch ../tip.patch
30 rm -rf b
31
32 echo % import from stdin
33 hg clone -r0 a b
34 hg --cwd a export tip | hg --cwd b import -
35 rm -rf b
36
37 echo % override commit message
38 hg clone -r0 a b
39 hg --cwd a export tip | hg --cwd b import -m 'override' -
40 hg --cwd b tip | grep override
41 rm -rf b
42
43 cat > mkmsg.py <<EOF
44 import email.Message, sys
45 msg = email.Message.Message()
46 msg.set_payload('email commit message\n' + open('tip.patch').read())
47 msg['Subject'] = 'email patch'
48 msg['From'] = 'email patcher'
49 sys.stdout.write(msg.as_string())
50 EOF
51
52 echo % plain diff in email, subject, message body
53 hg clone -r0 a b
54 hg --cwd a diff -r0:1 > tip.patch
55 python mkmsg.py > msg.patch
56 hg --cwd b import ../msg.patch
57 hg --cwd b tip | grep email
58 rm -rf b
59
60 echo % plain diff in email, no subject, message body
61 hg clone -r0 a b
62 grep -v '^Subject:' msg.patch | hg --cwd b import -
63 rm -rf b
64
65 echo % plain diff in email, subject, no message body
66 hg clone -r0 a b
67 grep -v '^email ' msg.patch | hg --cwd b import -
68 rm -rf b
69
70 echo % plain diff in email, no subject, no message body, should fail
71 hg clone -r0 a b
72 grep -v '^\(Subject\|email\)' msg.patch | hg --cwd b import -
73 rm -rf b
74
75 echo % hg export in email, should use patch header
76 hg clone -r0 a b
77 hg --cwd a export tip > tip.patch
78 python mkmsg.py | hg --cwd b import -
79 hg --cwd b tip | grep second
80 rm -rf b
81
@@ -0,0 +1,103 b''
1 adding a
2 % import exported patch
3 requesting all changes
4 adding changesets
5 adding manifests
6 adding file changes
7 added 1 changesets with 1 changes to 1 files
8 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
9 applying ../tip.patch
10 patching file a
11 % message should be same
12 summary: second change
13 % committer should be same
14 user: someone
15 % import of plain diff should fail without message
16 requesting all changes
17 adding changesets
18 adding manifests
19 adding file changes
20 added 1 changesets with 1 changes to 1 files
21 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 applying ../tip.patch
23 patching file a
24 transaction abort!
25 rollback completed
26 % import of plain diff should be ok with message
27 requesting all changes
28 adding changesets
29 adding manifests
30 adding file changes
31 added 1 changesets with 1 changes to 1 files
32 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 applying ../tip.patch
34 patching file a
35 % import from stdin
36 requesting all changes
37 adding changesets
38 adding manifests
39 adding file changes
40 added 1 changesets with 1 changes to 1 files
41 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 applying patch from stdin
43 patching file a
44 % override commit message
45 requesting all changes
46 adding changesets
47 adding manifests
48 adding file changes
49 added 1 changesets with 1 changes to 1 files
50 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 applying patch from stdin
52 patching file a
53 summary: override
54 % plain diff in email, subject, message body
55 requesting all changes
56 adding changesets
57 adding manifests
58 adding file changes
59 added 1 changesets with 1 changes to 1 files
60 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 applying ../msg.patch
62 patching file a
63 user: email patcher
64 summary: email patch
65 % plain diff in email, no subject, message body
66 requesting all changes
67 adding changesets
68 adding manifests
69 adding file changes
70 added 1 changesets with 1 changes to 1 files
71 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 applying patch from stdin
73 patching file a
74 % plain diff in email, subject, no message body
75 requesting all changes
76 adding changesets
77 adding manifests
78 adding file changes
79 added 1 changesets with 1 changes to 1 files
80 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 applying patch from stdin
82 patching file a
83 % plain diff in email, no subject, no message body, should fail
84 requesting all changes
85 adding changesets
86 adding manifests
87 adding file changes
88 added 1 changesets with 1 changes to 1 files
89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 applying patch from stdin
91 patching file a
92 transaction abort!
93 rollback completed
94 % hg export in email, should use patch header
95 requesting all changes
96 adding changesets
97 adding manifests
98 adding file changes
99 added 1 changesets with 1 changes to 1 files
100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 applying patch from stdin
102 patching file a
103 summary: second change
@@ -0,0 +1,16 b''
1 #!/bin/sh
2
3 hg init
4 echo "test-parse-date" > a
5 hg add a
6 hg ci -d "2006-02-01 13:00:30" -m "rev 0"
7 echo "hi!" >> a
8 hg ci -d "2006-02-01 13:00:30 -0500" -m "rev 1"
9 hg tag -d "2006-04-15 13:30" "Hi"
10 hg backout --merge -d "2006-04-15 13:30 +0200" -m "rev 3" 1
11 hg ci -d "1150000000 14400" -m "rev 4 (merge)"
12 echo "fail" >> a
13 hg ci -d "should fail" -m "fail"
14 hg ci -d "100000000000000000 1400" -m "fail"
15 hg ci -d "100000 1400000" -m "fail"
16 hg log --template '{date|date}\n'
@@ -0,0 +1,19 b''
1 reverting a
2 changeset 3:107ce1ee2b43 backs out changeset 1:25a1420a55f8
3 merging with changeset 2:99a1acecff55
4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
5 (branch merge, don't forget to commit)
6 abort: invalid date: 'should fail'
7 transaction abort!
8 rollback completed
9 abort: date exceeds 32 bits: 100000000000000000
10 transaction abort!
11 rollback completed
12 abort: impossible time zone offset: 1400000
13 transaction abort!
14 rollback completed
15 Sun Jun 11 00:26:40 2006 -0400
16 Sat Apr 15 13:30:00 2006 +0200
17 Sat Apr 15 13:30:00 2006 +0000
18 Wed Feb 01 13:00:30 2006 -0500
19 Wed Feb 01 13:00:30 2006 +0000
@@ -18,13 +18,10 b''
18 <p class="p2"><br></p>
18 <p class="p2"><br></p>
19 <p class="p3">This is <i>not</i> a stand-alone version of Mercurial.</p>
19 <p class="p3">This is <i>not</i> a stand-alone version of Mercurial.</p>
20 <p class="p2"><br></p>
20 <p class="p2"><br></p>
21 <p class="p3">To use it, you must have the β€œofficial unofficial” MacPython 2.4.1 installed.</p>
21 <p class="p3">To use it, you must have the Universal MacPython 2.4.3 from <a href="http://www.python.org">www.python.org</a> installed.</p>
22 <p class="p2"><br></p>
22 <p class="p2"><br></p>
23 <p class="p3">You can download MacPython 2.4.1 from here:</p>
23 <p class="p3">You can download MacPython 2.4.3 from here:</p>
24 <p class="p4"><span class="s1"><a href="http://python.org/ftp/python/2.4.1/MacPython-OSX-2.4.1-1.dmg">http://python.org/ftp/python/2.4.1/MacPython-OSX-2.4.1-1.dmg</a></span></p>
24 <p class="p4"><span class="s1"><a href="http://www.python.org/ftp/python/2.4.3/Universal-MacPython-2.4.3-2006-04-07.dmg">http://www.python.org/ftp/python/2.4.3/Universal-MacPython-2.4.3-2006-04-07.dmg</a></span></p>
25 <p class="p2"><br></p>
26 <p class="p3">For more information on MacPython, go here:</p>
27 <p class="p4"><span class="s1"><a href="http://undefined.org/python/">http://undefined.org/python</a></span></p>
28 <p class="p2"><br></p>
25 <p class="p2"><br></p>
29 <p class="p1"><b>After you install</b></p>
26 <p class="p1"><b>After you install</b></p>
30 <p class="p2"><br></p>
27 <p class="p2"><br></p>
@@ -12,6 +12,6 b''
12 <body>
12 <body>
13 <p class="p1">This is a prepackaged release of <a href="http://www.selenic.com/mercurial">Mercurial</a> for Mac OS X.</p>
13 <p class="p1">This is a prepackaged release of <a href="http://www.selenic.com/mercurial">Mercurial</a> for Mac OS X.</p>
14 <p class="p2"><br></p>
14 <p class="p2"><br></p>
15 <p class="p1">It is based on Mercurial 0.8.</p>
15 <p class="p1">It is based on Mercurial 0.9.</p>
16 </body>
16 </body>
17 </html>
17 </html>
@@ -653,7 +653,7 b' The Mercurial mode user interface is bas'
653 you're already familiar with VC, the same keybindings and functions
653 you're already familiar with VC, the same keybindings and functions
654 will generally work.
654 will generally work.
655
655
656 Below is a list of many common SCM tasks. In the list, `G/L'
656 Below is a list of many common SCM tasks. In the list, `G/L\'
657 indicates whether a key binding is global (G) to a repository or local
657 indicates whether a key binding is global (G) to a repository or local
658 (L) to a file. Many commands take a prefix argument.
658 (L) to a file. Many commands take a prefix argument.
659
659
@@ -682,6 +682,8 b' Pull changes G '
682 Update working directory after pull G C-c h u hg-update
682 Update working directory after pull G C-c h u hg-update
683 See changes that can be pushed G C-c h . hg-outgoing
683 See changes that can be pushed G C-c h . hg-outgoing
684 Push changes G C-c h > hg-push"
684 Push changes G C-c h > hg-push"
685 (unless vc-make-backup-files
686 (set (make-local-variable 'backup-inhibited) t))
685 (run-hooks 'hg-mode-hook))
687 (run-hooks 'hg-mode-hook))
686
688
687 (defun hg-find-file-hook ()
689 (defun hg-find-file-hook ()
@@ -729,6 +731,8 b' With a prefix argument, prompt for the p'
729 (goto-char 0)
731 (goto-char 0)
730 (cd (hg-root path)))
732 (cd (hg-root path)))
731 (when update
733 (when update
734 (unless vc-make-backup-files
735 (set (make-local-variable 'backup-inhibited) t))
732 (with-current-buffer buf
736 (with-current-buffer buf
733 (hg-mode-line)))))
737 (hg-mode-line)))))
734
738
@@ -968,6 +972,7 b' With a prefix argument, prompt for the p'
968 (cd (hg-root path)))
972 (cd (hg-root path)))
969 (when update
973 (when update
970 (with-current-buffer buf
974 (with-current-buffer buf
975 (set (make-local-variable 'backup-inhibited) nil)
971 (hg-mode-line)))))
976 (hg-mode-line)))))
972
977
973 (defun hg-incoming (&optional repo)
978 (defun hg-incoming (&optional repo)
@@ -214,7 +214,6 b' class queue:'
214 return pp[0]
214 return pp[0]
215 if p1 in arevs:
215 if p1 in arevs:
216 return pp[1]
216 return pp[1]
217 return None
218 return pp[0]
217 return pp[0]
219
218
220 def mergepatch(self, repo, mergeq, series, wlock):
219 def mergepatch(self, repo, mergeq, series, wlock):
@@ -386,15 +385,21 b' class queue:'
386 self.ui.write("Local changes found, refresh first\n")
385 self.ui.write("Local changes found, refresh first\n")
387 sys.exit(1)
386 sys.exit(1)
388 def new(self, repo, patch, msg=None, force=None):
387 def new(self, repo, patch, msg=None, force=None):
389 if not force:
388 commitfiles = []
390 self.check_localchanges(repo)
389 (c, a, r, d, u) = repo.changes(None, None)
390 if c or a or d or r:
391 if not force:
392 raise util.Abort(_("Local changes found, refresh first"))
393 else:
394 commitfiles = c + a + r
391 self.check_toppatch(repo)
395 self.check_toppatch(repo)
392 wlock = repo.wlock()
396 wlock = repo.wlock()
393 insert = self.series_end()
397 insert = self.series_end()
394 if msg:
398 if msg:
395 n = repo.commit([], "[mq]: %s" % msg, force=True, wlock=wlock)
399 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
400 wlock=wlock)
396 else:
401 else:
397 n = repo.commit([],
402 n = repo.commit(commitfiles,
398 "New patch: %s" % patch, force=True, wlock=wlock)
403 "New patch: %s" % patch, force=True, wlock=wlock)
399 if n == None:
404 if n == None:
400 self.ui.warn("repo commit failed\n")
405 self.ui.warn("repo commit failed\n")
@@ -412,6 +417,8 b' class queue:'
412 wlock = None
417 wlock = None
413 r = self.qrepo()
418 r = self.qrepo()
414 if r: r.add([patch])
419 if r: r.add([patch])
420 if commitfiles:
421 self.refresh(repo, short=True)
415
422
416 def strip(self, repo, rev, update=True, backup="all", wlock=None):
423 def strip(self, repo, rev, update=True, backup="all", wlock=None):
417 def limitheads(chlog, stop):
424 def limitheads(chlog, stop):
@@ -6,7 +6,11 b' import cgitb, os, sys'
6 cgitb.enable()
6 cgitb.enable()
7
7
8 # sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
8 # sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
9 from mercurial import hgweb
9 from mercurial.hgweb.hgweb_mod import hgweb
10 from mercurial.hgweb.request import wsgiapplication
11 import mercurial.hgweb.wsgicgi as wsgicgi
10
12
11 h = hgweb.hgweb("/path/to/repo", "repository name")
13 def make_web_app():
12 h.run()
14 return hgweb("/path/to/repo", "repository name")
15
16 wsgicgi.launch(wsgiapplication(make_web_app))
@@ -6,7 +6,9 b' import cgitb, sys'
6 cgitb.enable()
6 cgitb.enable()
7
7
8 # sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
8 # sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
9 from mercurial import hgweb
9 from mercurial.hgweb.hgwebdir_mod import hgwebdir
10 from mercurial.hgweb.request import wsgiapplication
11 import mercurial.hgweb.wsgicgi as wsgicgi
10
12
11 # The config file looks like this. You can have paths to individual
13 # The config file looks like this. You can have paths to individual
12 # repos, collections of repos in a directory tree, or both.
14 # repos, collections of repos in a directory tree, or both.
@@ -27,5 +29,7 b' from mercurial import hgweb'
27 # Alternatively you can pass a list of ('virtual/path', '/real/path') tuples
29 # Alternatively you can pass a list of ('virtual/path', '/real/path') tuples
28 # or use a dictionary with entries like 'virtual/path': '/real/path'
30 # or use a dictionary with entries like 'virtual/path': '/real/path'
29
31
30 h = hgweb.hgwebdir("hgweb.config")
32 def make_web_app():
31 h.run()
33 return hgwebdir("hgweb.config")
34
35 wsgicgi.launch(wsgiapplication(make_web_app))
@@ -39,21 +39,10 b' class changelog(revlog):'
39 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
39 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
40 user=None, date=None):
40 user=None, date=None):
41 if date:
41 if date:
42 # validate explicit (probably user-specified) date and
42 parseddate = "%d %d" % util.parsedate(date)
43 # time zone offset. values must fit in signed 32 bits for
44 # current 32-bit linux runtimes. timezones go from UTC-12
45 # to UTC+14
46 try:
47 when, offset = map(int, date.split(' '))
48 except ValueError:
49 raise ValueError(_('invalid date: %r') % date)
50 if abs(when) > 0x7fffffff:
51 raise ValueError(_('date exceeds 32 bits: %d') % when)
52 if offset < -50400 or offset > 43200:
53 raise ValueError(_('impossible time zone offset: %d') % offset)
54 else:
43 else:
55 date = "%d %d" % util.makedate()
44 parseddate = "%d %d" % util.makedate()
56 list.sort()
45 list.sort()
57 l = [hex(manifest), user, date] + list + ["", desc]
46 l = [hex(manifest), user, parseddate] + list + ["", desc]
58 text = "\n".join(l)
47 text = "\n".join(l)
59 return self.addrevision(text, transaction, self.count(), p1, p2)
48 return self.addrevision(text, transaction, self.count(), p1, p2)
@@ -12,7 +12,7 b' demandload(globals(), "os re sys signal '
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 demandload(globals(), "archival changegroup")
15 demandload(globals(), "archival cStringIO changegroup email.Parser")
16 demandload(globals(), "hgweb.server sshserver")
16 demandload(globals(), "hgweb.server sshserver")
17
17
18 class UnknownCommand(Exception):
18 class UnknownCommand(Exception):
@@ -1719,11 +1719,16 b' def import_(ui, repo, patch1, *patches, '
1719 If there are outstanding changes in the working directory, import
1719 If there are outstanding changes in the working directory, import
1720 will abort unless given the -f flag.
1720 will abort unless given the -f flag.
1721
1721
1722 If a patch looks like a mail message (its first line starts with
1722 You can import a patch straight from a mail message. Even patches
1723 "From " or looks like an RFC822 header), it will not be applied
1723 as attachments work (body part must be type text/plain or
1724 unless the -f option is used. The importer neither parses nor
1724 text/x-patch to be used). From and Subject headers of email
1725 discards mail headers, so use -f only to override the "mailness"
1725 message are used as default committer and commit message. All
1726 safety check, not to import a real mail message.
1726 text/plain body parts before first diff are added to commit
1727 message.
1728
1729 If imported patch was generated by hg export, user and description
1730 from patch override values from message headers and body. Values
1731 given on command line with -m and -u override these.
1727
1732
1728 To read a patch from standard input, use patch name "-".
1733 To read a patch from standard input, use patch name "-".
1729 """
1734 """
@@ -1739,79 +1744,98 b' def import_(ui, repo, patch1, *patches, '
1739
1744
1740 # attempt to detect the start of a patch
1745 # attempt to detect the start of a patch
1741 # (this heuristic is borrowed from quilt)
1746 # (this heuristic is borrowed from quilt)
1742 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1747 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1743 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1748 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1744 '(---|\*\*\*)[ \t])')
1749 '(---|\*\*\*)[ \t])', re.MULTILINE)
1745
1750
1746 for patch in patches:
1751 for patch in patches:
1747 pf = os.path.join(d, patch)
1752 pf = os.path.join(d, patch)
1748
1753
1749 message = []
1754 message = None
1750 user = None
1755 user = None
1751 date = None
1756 date = None
1752 hgpatch = False
1757 hgpatch = False
1758
1759 p = email.Parser.Parser()
1753 if pf == '-':
1760 if pf == '-':
1754 f = sys.stdin
1761 msg = p.parse(sys.stdin)
1755 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1756 pf = tmpname
1757 tmpfp = os.fdopen(fd, 'w')
1758 ui.status(_("applying patch from stdin\n"))
1762 ui.status(_("applying patch from stdin\n"))
1759 else:
1763 else:
1760 f = open(pf)
1764 msg = p.parse(file(pf))
1761 tmpfp, tmpname = None, None
1762 ui.status(_("applying %s\n") % patch)
1765 ui.status(_("applying %s\n") % patch)
1766
1767 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1768 tmpfp = os.fdopen(fd, 'w')
1763 try:
1769 try:
1764 while True:
1770 message = msg['Subject']
1765 line = f.readline()
1771 if message:
1766 if not line: break
1772 message = message.replace('\n\t', ' ')
1767 if tmpfp: tmpfp.write(line)
1773 ui.debug('Subject: %s\n' % message)
1768 line = line.rstrip()
1774 user = msg['From']
1769 if (not message and not hgpatch and
1775 if user:
1770 mailre.match(line) and not opts['force']):
1776 ui.debug('From: %s\n' % user)
1771 if len(line) > 35:
1777 diffs_seen = 0
1772 line = line[:32] + '...'
1778 ok_types = ('text/plain', 'text/x-patch')
1773 raise util.Abort(_('first line looks like a '
1779 for part in msg.walk():
1774 'mail header: ') + line)
1780 content_type = part.get_content_type()
1775 if diffre.match(line):
1781 ui.debug('Content-Type: %s\n' % content_type)
1782 if content_type not in ok_types:
1783 continue
1784 payload = part.get_payload(decode=True)
1785 m = diffre.search(payload)
1786 if m:
1787 ui.debug(_('found patch at byte %d\n') % m.start(0))
1788 diffs_seen += 1
1789 hgpatch = False
1790 fp = cStringIO.StringIO()
1791 if message:
1792 fp.write(message)
1793 fp.write('\n')
1794 for line in payload[:m.start(0)].splitlines():
1795 if line.startswith('# HG changeset patch'):
1796 ui.debug(_('patch generated by hg export\n'))
1797 hgpatch = True
1798 # drop earlier commit message content
1799 fp.seek(0)
1800 fp.truncate()
1801 elif hgpatch:
1802 if line.startswith('# User '):
1803 user = line[7:]
1804 ui.debug('From: %s\n' % user)
1805 elif line.startswith("# Date "):
1806 date = line[7:]
1807 if not line.startswith('# '):
1808 fp.write(line)
1809 fp.write('\n')
1810 message = fp.getvalue()
1776 if tmpfp:
1811 if tmpfp:
1777 for chunk in util.filechunkiter(f):
1812 tmpfp.write(payload)
1778 tmpfp.write(chunk)
1813 if not payload.endswith('\n'):
1779 break
1814 tmpfp.write('\n')
1780 elif hgpatch:
1815 elif not diffs_seen and message and content_type == 'text/plain':
1781 # parse values when importing the result of an hg export
1816 message += '\n' + payload
1782 if line.startswith("# User "):
1783 user = line[7:]
1784 ui.debug(_('User: %s\n') % user)
1785 elif line.startswith("# Date "):
1786 date = line[7:]
1787 elif not line.startswith("# ") and line:
1788 message.append(line)
1789 hgpatch = False
1790 elif line == '# HG changeset patch':
1791 hgpatch = True
1792 message = [] # We may have collected garbage
1793 elif message or line:
1794 message.append(line)
1795
1817
1796 if opts['message']:
1818 if opts['message']:
1797 # pickup the cmdline msg
1819 # pickup the cmdline msg
1798 message = opts['message']
1820 message = opts['message']
1799 elif message:
1821 elif message:
1800 # pickup the patch msg
1822 # pickup the patch msg
1801 message = '\n'.join(message).rstrip()
1823 message = message.strip()
1802 else:
1824 else:
1803 # launch the editor
1825 # launch the editor
1804 message = None
1826 message = None
1805 ui.debug(_('message:\n%s\n') % message)
1827 ui.debug(_('message:\n%s\n') % message)
1806
1828
1807 if tmpfp: tmpfp.close()
1829 tmpfp.close()
1808 files = util.patch(strip, pf, ui)
1830 if not diffs_seen:
1809
1831 raise util.Abort(_('no diffs found'))
1832
1833 files = util.patch(strip, tmpname, ui)
1810 if len(files) > 0:
1834 if len(files) > 0:
1811 addremove_lock(ui, repo, files, {})
1835 addremove_lock(ui, repo, files, {})
1812 repo.commit(files, message, user, date)
1836 repo.commit(files, message, user, date)
1813 finally:
1837 finally:
1814 if tmpname: os.unlink(tmpname)
1838 os.unlink(tmpname)
1815
1839
1816 def incoming(ui, repo, source="default", **opts):
1840 def incoming(ui, repo, source="default", **opts):
1817 """show new changesets found in source
1841 """show new changesets found in source
@@ -1851,7 +1875,10 b' def incoming(ui, repo, source="default",'
1851 # use the created uncompressed bundlerepo
1875 # use the created uncompressed bundlerepo
1852 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1876 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1853
1877
1854 o = other.changelog.nodesbetween(incoming)[0]
1878 revs = None
1879 if opts['rev']:
1880 revs = [other.lookup(rev) for rev in opts['rev']]
1881 o = other.changelog.nodesbetween(incoming, revs)[0]
1855 if opts['newest_first']:
1882 if opts['newest_first']:
1856 o.reverse()
1883 o.reverse()
1857 displayer = show_changeset(ui, other, opts)
1884 displayer = show_changeset(ui, other, opts)
@@ -2061,13 +2088,16 b' def outgoing(ui, repo, dest=None, **opts'
2061 ui.setconfig("ui", "ssh", opts['ssh'])
2088 ui.setconfig("ui", "ssh", opts['ssh'])
2062 if opts['remotecmd']:
2089 if opts['remotecmd']:
2063 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2090 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2091 revs = None
2092 if opts['rev']:
2093 revs = [repo.lookup(rev) for rev in opts['rev']]
2064
2094
2065 other = hg.repository(ui, dest)
2095 other = hg.repository(ui, dest)
2066 o = repo.findoutgoing(other, force=opts['force'])
2096 o = repo.findoutgoing(other, force=opts['force'])
2067 if not o:
2097 if not o:
2068 ui.status(_("no changes found\n"))
2098 ui.status(_("no changes found\n"))
2069 return
2099 return
2070 o = repo.changelog.nodesbetween(o)[0]
2100 o = repo.changelog.nodesbetween(o, revs)[0]
2071 if opts['newest_first']:
2101 if opts['newest_first']:
2072 o.reverse()
2102 o.reverse()
2073 displayer = show_changeset(ui, repo, opts)
2103 displayer = show_changeset(ui, repo, opts)
@@ -2998,11 +3028,13 b' table = {'
2998 ('n', 'newest-first', None, _('show newest record first')),
3028 ('n', 'newest-first', None, _('show newest record first')),
2999 ('', 'bundle', '', _('file to store the bundles into')),
3029 ('', 'bundle', '', _('file to store the bundles into')),
3000 ('p', 'patch', None, _('show patch')),
3030 ('p', 'patch', None, _('show patch')),
3031 ('r', 'rev', [], _('a specific revision you would like to pull')),
3001 ('', 'template', '', _('display with template')),
3032 ('', 'template', '', _('display with template')),
3002 ('e', 'ssh', '', _('specify ssh command to use')),
3033 ('e', 'ssh', '', _('specify ssh command to use')),
3003 ('', 'remotecmd', '',
3034 ('', 'remotecmd', '',
3004 _('specify hg command to run on the remote side'))],
3035 _('specify hg command to run on the remote side'))],
3005 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
3036 _('hg incoming [-p] [-n] [-M] [-r REV]...'
3037 '[--bundle FILENAME] [SOURCE]')),
3006 "^init": (init, [], _('hg init [DEST]')),
3038 "^init": (init, [], _('hg init [DEST]')),
3007 "locate":
3039 "locate":
3008 (locate,
3040 (locate,
@@ -3040,12 +3072,13 b' table = {'
3040 _('run even when remote repository is unrelated')),
3072 _('run even when remote repository is unrelated')),
3041 ('p', 'patch', None, _('show patch')),
3073 ('p', 'patch', None, _('show patch')),
3042 ('', 'style', '', _('display using template map file')),
3074 ('', 'style', '', _('display using template map file')),
3075 ('r', 'rev', [], _('a specific revision you would like to push')),
3043 ('n', 'newest-first', None, _('show newest record first')),
3076 ('n', 'newest-first', None, _('show newest record first')),
3044 ('', 'template', '', _('display with template')),
3077 ('', 'template', '', _('display with template')),
3045 ('e', 'ssh', '', _('specify ssh command to use')),
3078 ('e', 'ssh', '', _('specify ssh command to use')),
3046 ('', 'remotecmd', '',
3079 ('', 'remotecmd', '',
3047 _('specify hg command to run on the remote side'))],
3080 _('specify hg command to run on the remote side'))],
3048 _('hg outgoing [-M] [-p] [-n] [DEST]')),
3081 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3049 "^parents":
3082 "^parents":
3050 (parents,
3083 (parents,
3051 [('b', 'branches', None, _('show branches')),
3084 [('b', 'branches', None, _('show branches')),
@@ -17,7 +17,7 b' def get_mtime(repo_path):'
17 else:
17 else:
18 return os.stat(hg_path).st_mtime
18 return os.stat(hg_path).st_mtime
19
19
20 def staticfile(directory, fname):
20 def staticfile(directory, fname, req):
21 """return a file inside directory with guessed content-type header
21 """return a file inside directory with guessed content-type header
22
22
23 fname always uses '/' as directory separator and isn't allowed to
23 fname always uses '/' as directory separator and isn't allowed to
@@ -36,7 +36,9 b' def staticfile(directory, fname):'
36 try:
36 try:
37 os.stat(path)
37 os.stat(path)
38 ct = mimetypes.guess_type(path)[0] or "text/plain"
38 ct = mimetypes.guess_type(path)[0] or "text/plain"
39 return "Content-type: %s\n\n%s" % (ct, file(path).read())
39 req.header([('Content-type', ct),
40 ('Content-length', os.path.getsize(path))])
41 return file(path).read()
40 except (TypeError, OSError):
42 except (TypeError, OSError):
41 # illegal fname or unreadable file
43 # illegal fname or unreadable file
42 return ""
44 return ""
@@ -10,9 +10,8 b' import os'
10 import os.path
10 import os.path
11 import mimetypes
11 import mimetypes
12 from mercurial.demandload import demandload
12 from mercurial.demandload import demandload
13 demandload(globals(), "re zlib ConfigParser cStringIO sys tempfile")
13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
15 demandload(globals(), "mercurial.hgweb.request:hgrequest")
16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
15 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
17 from mercurial.node import *
16 from mercurial.node import *
18 from mercurial.i18n import gettext as _
17 from mercurial.i18n import gettext as _
@@ -651,9 +650,12 b' class hgweb(object):'
651 raise Exception("suspicious path")
650 raise Exception("suspicious path")
652 return p
651 return p
653
652
654 def run(self, req=hgrequest()):
653 def run(self, req):
655 def header(**map):
654 def header(**map):
656 yield self.t("header", **map)
655 header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
656 msg = mimetools.Message(header_file, 0)
657 req.header(msg.items())
658 yield header_file.read()
657
659
658 def footer(**map):
660 def footer(**map):
659 yield self.t("footer",
661 yield self.t("footer",
@@ -724,7 +726,6 b' class hgweb(object):'
724 method(req)
726 method(req)
725 else:
727 else:
726 req.write(self.t("error"))
728 req.write(self.t("error"))
727 req.done()
728
729
729 def do_changelog(self, req):
730 def do_changelog(self, req):
730 hi = self.repo.changelog.count() - 1
731 hi = self.repo.changelog.count() - 1
@@ -830,7 +831,7 b' class hgweb(object):'
830 static = self.repo.ui.config("web", "static",
831 static = self.repo.ui.config("web", "static",
831 os.path.join(self.templatepath,
832 os.path.join(self.templatepath,
832 "static"))
833 "static"))
833 req.write(staticfile(static, fname)
834 req.write(staticfile(static, fname, req)
834 or self.t("error", error="%r not found" % fname))
835 or self.t("error", error="%r not found" % fname))
835
836
836 def do_capabilities(self, req):
837 def do_capabilities(self, req):
@@ -8,10 +8,9 b''
8
8
9 import os
9 import os
10 from mercurial.demandload import demandload
10 from mercurial.demandload import demandload
11 demandload(globals(), "ConfigParser")
11 demandload(globals(), "ConfigParser mimetools cStringIO")
12 demandload(globals(), "mercurial:ui,hg,util,templater")
12 demandload(globals(), "mercurial:ui,hg,util,templater")
13 demandload(globals(), "mercurial.hgweb.hgweb_mod:hgweb")
13 demandload(globals(), "mercurial.hgweb.hgweb_mod:hgweb")
14 demandload(globals(), "mercurial.hgweb.request:hgrequest")
15 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
14 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
16 from mercurial.i18n import gettext as _
15 from mercurial.i18n import gettext as _
17
16
@@ -47,9 +46,12 b' class hgwebdir(object):'
47 self.repos.append((name.lstrip(os.sep), repo))
46 self.repos.append((name.lstrip(os.sep), repo))
48 self.repos.sort()
47 self.repos.sort()
49
48
50 def run(self, req=hgrequest()):
49 def run(self, req):
51 def header(**map):
50 def header(**map):
52 yield tmpl("header", **map)
51 header_file = cStringIO.StringIO(''.join(tmpl("header", **map)))
52 msg = mimetools.Message(header_file, 0)
53 req.header(msg.items())
54 yield header_file.read()
53
55
54 def footer(**map):
56 def footer(**map):
55 yield tmpl("footer", motd=self.motd, **map)
57 yield tmpl("footer", motd=self.motd, **map)
@@ -133,7 +135,7 b' class hgwebdir(object):'
133 if req.form.has_key('static'):
135 if req.form.has_key('static'):
134 static = os.path.join(templater.templatepath(), "static")
136 static = os.path.join(templater.templatepath(), "static")
135 fname = req.form['static'][0]
137 fname = req.form['static'][0]
136 req.write(staticfile(static, fname)
138 req.write(staticfile(static, fname, req)
137 or tmpl("error", error="%r not found" % fname))
139 or tmpl("error", error="%r not found" % fname))
138 else:
140 else:
139 sortable = ["name", "description", "contact", "lastchange"]
141 sortable = ["name", "description", "contact", "lastchange"]
@@ -10,13 +10,48 b' from mercurial.demandload import demandl'
10 demandload(globals(), "socket sys cgi os errno")
10 demandload(globals(), "socket sys cgi os errno")
11 from mercurial.i18n import gettext as _
11 from mercurial.i18n import gettext as _
12
12
13 class hgrequest(object):
13 class wsgiapplication(object):
14 def __init__(self, inp=None, out=None, env=None):
14 def __init__(self, destmaker):
15 self.inp = inp or sys.stdin
15 self.destmaker = destmaker
16 self.out = out or sys.stdout
16
17 self.env = env or os.environ
17 def __call__(self, wsgienv, start_response):
18 return _wsgirequest(self.destmaker(), wsgienv, start_response)
19
20 class _wsgioutputfile(object):
21 def __init__(self, request):
22 self.request = request
23
24 def write(self, data):
25 self.request.write(data)
26 def writelines(self, lines):
27 for line in lines:
28 self.write(line)
29 def flush(self):
30 return None
31 def close(self):
32 return None
33
34 class _wsgirequest(object):
35 def __init__(self, destination, wsgienv, start_response):
36 version = wsgienv['wsgi.version']
37 if (version < (1,0)) or (version >= (2, 0)):
38 raise RuntimeError("Unknown and unsupported WSGI version %d.%d" \
39 % version)
40 self.inp = wsgienv['wsgi.input']
41 self.out = _wsgioutputfile(self)
42 self.server_write = None
43 self.err = wsgienv['wsgi.errors']
44 self.threaded = wsgienv['wsgi.multithread']
45 self.multiprocess = wsgienv['wsgi.multiprocess']
46 self.run_once = wsgienv['wsgi.run_once']
47 self.env = wsgienv
18 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
48 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
19 self.will_close = True
49 self.start_response = start_response
50 self.headers = []
51 destination.run(self)
52
53 def __iter__(self):
54 return iter([])
20
55
21 def read(self, count=-1):
56 def read(self, count=-1):
22 return self.inp.read(count)
57 return self.inp.read(count)
@@ -27,23 +62,22 b' class hgrequest(object):'
27 for part in thing:
62 for part in thing:
28 self.write(part)
63 self.write(part)
29 else:
64 else:
65 thing = str(thing)
66 if self.server_write is None:
67 if not self.headers:
68 raise RuntimeError("request.write called before headers sent (%s)." % thing)
69 self.server_write = self.start_response('200 Script output follows',
70 self.headers)
71 self.start_response = None
72 self.headers = None
30 try:
73 try:
31 self.out.write(str(thing))
74 self.server_write(thing)
32 except socket.error, inst:
75 except socket.error, inst:
33 if inst[0] != errno.ECONNRESET:
76 if inst[0] != errno.ECONNRESET:
34 raise
77 raise
35
78
36 def done(self):
37 if self.will_close:
38 self.inp.close()
39 self.out.close()
40 else:
41 self.out.flush()
42
43 def header(self, headers=[('Content-type','text/html')]):
79 def header(self, headers=[('Content-type','text/html')]):
44 for header in headers:
80 self.headers.extend(headers)
45 self.out.write("%s: %s\r\n" % header)
46 self.out.write("\r\n")
47
81
48 def httphdr(self, type, filename=None, length=0, headers={}):
82 def httphdr(self, type, filename=None, length=0, headers={}):
49 headers = headers.items()
83 headers = headers.items()
@@ -51,12 +85,6 b' class hgrequest(object):'
51 if filename:
85 if filename:
52 headers.append(('Content-disposition', 'attachment; filename=%s' %
86 headers.append(('Content-disposition', 'attachment; filename=%s' %
53 filename))
87 filename))
54 # we do not yet support http 1.1 chunked transfer, so we have
55 # to force connection to close if content-length not known
56 if length:
88 if length:
57 headers.append(('Content-length', str(length)))
89 headers.append(('Content-length', str(length)))
58 self.will_close = False
59 else:
60 headers.append(('Connection', 'close'))
61 self.will_close = True
62 self.header(headers)
90 self.header(headers)
@@ -10,7 +10,7 b' from mercurial.demandload import demandl'
10 import os, sys, errno
10 import os, sys, errno
11 demandload(globals(), "urllib BaseHTTPServer socket SocketServer")
11 demandload(globals(), "urllib BaseHTTPServer socket SocketServer")
12 demandload(globals(), "mercurial:ui,hg,util,templater")
12 demandload(globals(), "mercurial:ui,hg,util,templater")
13 demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:hgrequest")
13 demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:wsgiapplication")
14 from mercurial.i18n import gettext as _
14 from mercurial.i18n import gettext as _
15
15
16 def _splitURI(uri):
16 def _splitURI(uri):
@@ -25,6 +25,17 b' def _splitURI(uri):'
25 path, query = uri, ''
25 path, query = uri, ''
26 return urllib.unquote(path), query
26 return urllib.unquote(path), query
27
27
28 class _error_logger(object):
29 def __init__(self, handler):
30 self.handler = handler
31 def flush(self):
32 pass
33 def write(str):
34 self.writelines(str.split('\n'))
35 def writelines(seq):
36 for msg in seq:
37 self.handler.log_error("HG error: %s", msg)
38
28 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
39 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
29 def __init__(self, *args, **kargs):
40 def __init__(self, *args, **kargs):
30 self.protocol_version = 'HTTP/1.1'
41 self.protocol_version = 'HTTP/1.1'
@@ -76,17 +87,72 b' class _hgwebhandler(object, BaseHTTPServ'
76 length = self.headers.getheader('content-length')
87 length = self.headers.getheader('content-length')
77 if length:
88 if length:
78 env['CONTENT_LENGTH'] = length
89 env['CONTENT_LENGTH'] = length
79 accept = []
90 for header in [h for h in self.headers.keys() \
80 for line in self.headers.getallmatchingheaders('accept'):
91 if h not in ('content-type', 'content-length')]:
81 if line[:1] in "\t\n\r ":
92 hkey = 'HTTP_' + header.replace('-', '_').upper()
82 accept.append(line.strip())
93 hval = self.headers.getheader(header)
83 else:
94 hval = hval.replace('\n', '').strip()
84 accept = accept + line[7:].split(',')
95 if hval:
85 env['HTTP_ACCEPT'] = ','.join(accept)
96 env[hkey] = hval
97 env['SERVER_PROTOCOL'] = self.request_version
98 env['wsgi.version'] = (1, 0)
99 env['wsgi.url_scheme'] = 'http'
100 env['wsgi.input'] = self.rfile
101 env['wsgi.errors'] = _error_logger(self)
102 env['wsgi.multithread'] = isinstance(self.server,
103 SocketServer.ThreadingMixIn)
104 env['wsgi.multiprocess'] = isinstance(self.server,
105 SocketServer.ForkingMixIn)
106 env['wsgi.run_once'] = 0
107
108 self.close_connection = True
109 self.saved_status = None
110 self.saved_headers = []
111 self.sent_headers = False
112 self.length = None
113 req = self.server.reqmaker(env, self._start_response)
114 for data in req:
115 if data:
116 self._write(data)
86
117
87 req = hgrequest(self.rfile, self.wfile, env)
118 def send_headers(self):
88 self.send_response(200, "Script output follows")
119 if not self.saved_status:
89 self.close_connection = self.server.make_and_run_handler(req)
120 raise AssertionError("Sending headers before start_response() called")
121 saved_status = self.saved_status.split(None, 1)
122 saved_status[0] = int(saved_status[0])
123 self.send_response(*saved_status)
124 should_close = True
125 for h in self.saved_headers:
126 self.send_header(*h)
127 if h[0].lower() == 'content-length':
128 should_close = False
129 self.length = int(h[1])
130 if should_close:
131 self.send_header('Connection', 'close')
132 self.close_connection = should_close
133 self.end_headers()
134 self.sent_headers = True
135
136 def _start_response(self, http_status, headers, exc_info=None):
137 code, msg = http_status.split(None, 1)
138 code = int(code)
139 self.saved_status = http_status
140 bad_headers = ('connection', 'transfer-encoding')
141 self.saved_headers = [ h for h in headers \
142 if h[0].lower() not in bad_headers ]
143 return self._write
144
145 def _write(self, data):
146 if not self.saved_status:
147 raise AssertionError("data written before start_response() called")
148 elif not self.sent_headers:
149 self.send_headers()
150 if self.length is not None:
151 if len(data) > self.length:
152 raise AssertionError("Content-length header sent, but more bytes than specified are being written.")
153 self.length = self.length - len(data)
154 self.wfile.write(data)
155 self.wfile.flush()
90
156
91 def create_server(ui, repo):
157 def create_server(ui, repo):
92 use_threads = True
158 use_threads = True
@@ -126,8 +192,9 b' def create_server(ui, repo):'
126 self.webdir_conf = webdir_conf
192 self.webdir_conf = webdir_conf
127 self.webdirmaker = hgwebdir
193 self.webdirmaker = hgwebdir
128 self.repoviewmaker = hgweb
194 self.repoviewmaker = hgweb
195 self.reqmaker = wsgiapplication(self.make_handler)
129
196
130 def make_and_run_handler(self, req):
197 def make_handler(self):
131 if self.webdir_conf:
198 if self.webdir_conf:
132 hgwebobj = self.webdirmaker(self.webdir_conf)
199 hgwebobj = self.webdirmaker(self.webdir_conf)
133 elif self.repo is not None:
200 elif self.repo is not None:
@@ -135,8 +202,7 b' def create_server(ui, repo):'
135 repo.origroot))
202 repo.origroot))
136 else:
203 else:
137 raise hg.RepoError(_('no repo found'))
204 raise hg.RepoError(_('no repo found'))
138 hgwebobj.run(req)
205 return hgwebobj
139 return req.will_close
140
206
141 class IPv6HTTPServer(MercurialHTTPServer):
207 class IPv6HTTPServer(MercurialHTTPServer):
142 address_family = getattr(socket, 'AF_INET6', None)
208 address_family = getattr(socket, 'AF_INET6', None)
@@ -144,7 +210,7 b' def create_server(ui, repo):'
144 def __init__(self, *args, **kwargs):
210 def __init__(self, *args, **kwargs):
145 if self.address_family is None:
211 if self.address_family is None:
146 raise hg.RepoError(_('IPv6 not available on this system'))
212 raise hg.RepoError(_('IPv6 not available on this system'))
147 super(IPv6HTTPServer, self).__init__(*args, **kargs)
213 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
148
214
149 if use_ipv6:
215 if use_ipv6:
150 return IPv6HTTPServer((address, port), _hgwebhandler)
216 return IPv6HTTPServer((address, port), _hgwebhandler)
@@ -225,6 +225,10 b' def isodate(date):'
225 '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
225 '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
226 return util.datestr(date, format='%Y-%m-%d %H:%M')
226 return util.datestr(date, format='%Y-%m-%d %H:%M')
227
227
228 def hgdate(date):
229 '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.'''
230 return "%d %d" % date
231
228 def nl2br(text):
232 def nl2br(text):
229 '''replace raw newlines with xhtml line breaks.'''
233 '''replace raw newlines with xhtml line breaks.'''
230 return text.replace('\n', '<br/>\n')
234 return text.replace('\n', '<br/>\n')
@@ -282,6 +286,7 b' common_filters = {'
282 "fill76": lambda x: fill(x, width=76),
286 "fill76": lambda x: fill(x, width=76),
283 "firstline": lambda x: x.splitlines(1)[0].rstrip('\r\n'),
287 "firstline": lambda x: x.splitlines(1)[0].rstrip('\r\n'),
284 "tabindent": lambda x: indent(x, '\t'),
288 "tabindent": lambda x: indent(x, '\t'),
289 "hgdate": hgdate,
285 "isodate": isodate,
290 "isodate": isodate,
286 "obfuscate": obfuscate,
291 "obfuscate": obfuscate,
287 "permissions": lambda x: x and "-rwxr-xr-x" or "-rw-r--r--",
292 "permissions": lambda x: x and "-rwxr-xr-x" or "-rw-r--r--",
@@ -859,6 +859,49 b" def datestr(date=None, format='%a %b %d "
859 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
859 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
860 return s
860 return s
861
861
862 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
863 """parse a localized time string and return a (unixtime, offset) tuple.
864 if the string cannot be parsed, ValueError is raised."""
865 def hastimezone(string):
866 return (string[-4:].isdigit() and
867 (string[-5] == '+' or string[-5] == '-') and
868 string[-6].isspace())
869
870 if hastimezone(string):
871 date, tz = string.rsplit(None, 1)
872 tz = int(tz)
873 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
874 else:
875 date, offset = string, 0
876 when = int(time.mktime(time.strptime(date, format))) + offset
877 return when, offset
878
879 def parsedate(string, formats=('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M')):
880 """parse a localized time string and return a (unixtime, offset) tuple.
881 The date may be a "unixtime offset" string or in one of the specified
882 formats."""
883 try:
884 when, offset = map(int, string.split(' '))
885 except ValueError:
886 for format in formats:
887 try:
888 when, offset = strdate(string, format)
889 except ValueError:
890 pass
891 else:
892 break
893 else:
894 raise ValueError(_('invalid date: %r') % string)
895 # validate explicit (probably user-specified) date and
896 # time zone offset. values must fit in signed 32 bits for
897 # current 32-bit linux runtimes. timezones go from UTC-12
898 # to UTC+14
899 if abs(when) > 0x7fffffff:
900 raise ValueError(_('date exceeds 32 bits: %d') % when)
901 if offset < -50400 or offset > 43200:
902 raise ValueError(_('impossible time zone offset: %d') % offset)
903 return when, offset
904
862 def shortuser(user):
905 def shortuser(user):
863 """Return a short representation of a user name or email address."""
906 """Return a short representation of a user name or email address."""
864 f = user.find('@')
907 f = user.find('@')
@@ -1,7 +1,7 b''
1 #header#
1 #header#
2 # HG changeset patch
2 # HG changeset patch
3 # User #author#
3 # User #author#
4 # Date #date|date#
4 # Date #date|hgdate#
5 # Node ID #node#
5 # Node ID #node#
6 #parent%changesetparent#
6 #parent%changesetparent#
7 #desc#
7 #desc#
@@ -5,8 +5,8 b" difflineplus = '#line#'"
5 difflineminus = '#line#'
5 difflineminus = '#line#'
6 difflineat = '#line#'
6 difflineat = '#line#'
7 diffline = '#line#'
7 diffline = '#line#'
8 changesetparent = '# parent: #node#'
8 changesetparent = '# Parent #node#'
9 changesetchild = '# child: #node#'
9 changesetchild = '# Child #node#'
10 filenodelink = ''
10 filenodelink = ''
11 filerevision = 'Content-Type: #mimetype#\nContent-Disposition: filename=#file#\n\n#raw#'
11 filerevision = 'Content-Type: #mimetype#\nContent-Disposition: filename=#file#\n\n#raw#'
12 fileline = '#line#'
12 fileline = '#line#'
@@ -14,8 +14,10 b' cd ..'
14 hg init new
14 hg init new
15 # http incoming
15 # http incoming
16 http_proxy= hg -R new incoming http://localhost:20059/
16 http_proxy= hg -R new incoming http://localhost:20059/
17 http_proxy= hg -R new incoming -r 4 http://localhost:20059/
17 # local incoming
18 # local incoming
18 hg -R new incoming test
19 hg -R new incoming test
20 hg -R new incoming -r 4 test
19
21
20 # test with --bundle
22 # test with --bundle
21 http_proxy= hg -R new incoming --bundle test.hg http://localhost:20059/
23 http_proxy= hg -R new incoming --bundle test.hg http://localhost:20059/
@@ -42,5 +44,6 b' hg verify'
42 cd ..
44 cd ..
43 hg -R test-dev outgoing test
45 hg -R test-dev outgoing test
44 http_proxy= hg -R test-dev outgoing http://localhost:20059/
46 http_proxy= hg -R test-dev outgoing http://localhost:20059/
47 http_proxy= hg -R test-dev outgoing -r 11 http://localhost:20059/
45
48
46 kill `cat test/hg.pid`
49 kill `cat test/hg.pid`
@@ -75,6 +75,31 b' user: test'
75 date: Mon Jan 12 13:46:40 1970 +0000
75 date: Mon Jan 12 13:46:40 1970 +0000
76 summary: 4
76 summary: 4
77
77
78 changeset: 0:9cb21d99fe27
79 user: test
80 date: Mon Jan 12 13:46:40 1970 +0000
81 summary: 0
82
83 changeset: 1:d717f5dfad6a
84 user: test
85 date: Mon Jan 12 13:46:40 1970 +0000
86 summary: 1
87
88 changeset: 2:c0d6b86da426
89 user: test
90 date: Mon Jan 12 13:46:40 1970 +0000
91 summary: 2
92
93 changeset: 3:dfacbd43b3fe
94 user: test
95 date: Mon Jan 12 13:46:40 1970 +0000
96 summary: 3
97
98 changeset: 4:1f3a964b6022
99 user: test
100 date: Mon Jan 12 13:46:40 1970 +0000
101 summary: 4
102
78 changeset: 5:c028bcc7a28a
103 changeset: 5:c028bcc7a28a
79 user: test
104 user: test
80 date: Mon Jan 12 13:46:40 1970 +0000
105 date: Mon Jan 12 13:46:40 1970 +0000
@@ -121,6 +146,31 b' user: test'
121 date: Mon Jan 12 13:46:40 1970 +0000
146 date: Mon Jan 12 13:46:40 1970 +0000
122 summary: 4
147 summary: 4
123
148
149 changeset: 0:9cb21d99fe27
150 user: test
151 date: Mon Jan 12 13:46:40 1970 +0000
152 summary: 0
153
154 changeset: 1:d717f5dfad6a
155 user: test
156 date: Mon Jan 12 13:46:40 1970 +0000
157 summary: 1
158
159 changeset: 2:c0d6b86da426
160 user: test
161 date: Mon Jan 12 13:46:40 1970 +0000
162 summary: 2
163
164 changeset: 3:dfacbd43b3fe
165 user: test
166 date: Mon Jan 12 13:46:40 1970 +0000
167 summary: 3
168
169 changeset: 4:1f3a964b6022
170 user: test
171 date: Mon Jan 12 13:46:40 1970 +0000
172 summary: 4
173
124 changeset: 5:c028bcc7a28a
174 changeset: 5:c028bcc7a28a
125 user: test
175 user: test
126 date: Mon Jan 12 13:46:40 1970 +0000
176 date: Mon Jan 12 13:46:40 1970 +0000
@@ -270,3 +320,19 b' user: test'
270 date: Mon Jan 12 13:46:40 1970 +0000
320 date: Mon Jan 12 13:46:40 1970 +0000
271 summary: 13
321 summary: 13
272
322
323 searching for changes
324 changeset: 9:3741c3ad1096
325 user: test
326 date: Mon Jan 12 13:46:40 1970 +0000
327 summary: 9
328
329 changeset: 10:de4143c8d9a5
330 user: test
331 date: Mon Jan 12 13:46:40 1970 +0000
332 summary: 10
333
334 changeset: 11:0e1c188b9a7a
335 user: test
336 date: Mon Jan 12 13:46:40 1970 +0000
337 summary: 11
338
General Comments 0
You need to be logged in to leave comments. Login now