--- a/doc/hgrc.5.txt Thu Sep 30 14:07:57 2010 -0300
+++ b/doc/hgrc.5.txt Thu Sep 30 19:09:58 2010 -0500
@@ -951,8 +951,9 @@
third-party tools like email notification hooks can construct
URLs. Example: ``http://hgserver/repos/``.
``cacerts``
- Path to file containing a list of PEM encoded certificate authorities
- that may be used to verify an SSL server's identity. The form must be
+ Path to file containing a list of PEM encoded certificate authority
+ certificates. If specified on the client, then it will verify the identity
+ of remote HTTPS servers with these certificates. The form must be
as follows::
-----BEGIN CERTIFICATE-----
@@ -962,8 +963,8 @@
... (certificate in base64 PEM encoding) ...
-----END CERTIFICATE-----
- This feature is only supported when using Python 2.6. If you wish to
- use it with earlier versions of Python, install the backported
+ This feature is only supported when using Python 2.6 or later. If you wish
+ to use it with earlier versions of Python, install the backported
version of the ssl library that is available from
``http://pypi.python.org``.
--- a/mercurial/help/urls.txt Thu Sep 30 14:07:57 2010 -0300
+++ b/mercurial/help/urls.txt Thu Sep 30 19:09:58 2010 -0500
@@ -18,6 +18,9 @@
possible if the feature is explicitly enabled on the remote Mercurial
server.
+Note that the security of HTTPS URLs depends on proper configuration of
+web.cacerts.
+
Some notes about using SSH with Mercurial:
- SSH requires an accessible shell account on the destination machine
--- a/mercurial/patch.py Thu Sep 30 14:07:57 2010 -0300
+++ b/mercurial/patch.py Thu Sep 30 19:09:58 2010 -0500
@@ -1184,7 +1184,9 @@
gp.path = pathstrip(gp.path, strip - 1)[1]
if gp.oldpath:
gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
- if gp.op in ('COPY', 'RENAME'):
+ # Binary patches really overwrite target files, copying them
+ # will just make it fails with "target file exists"
+ if gp.op in ('COPY', 'RENAME') and not gp.binary:
copyfn(gp.oldpath, gp.path, cwd)
changed[gp.path] = gp
else:
@@ -1567,6 +1569,9 @@
header.append('new file mode %s\n' % mode)
elif ctx2.flags(f):
losedatafn(f)
+ # In theory, if tn was copied or renamed we should check
+ # if the source is binary too but the copy record already
+ # forces git mode.
if util.binary(tn):
if opts.git:
dodiff = 'binary'
@@ -1586,7 +1591,7 @@
else:
header.append('deleted file mode %s\n' %
gitmode[man1.flags(f)])
- elif not to:
+ elif not to or util.binary(to):
# regular diffs cannot represent empty file deletion
losedatafn(f)
else:
--- a/mercurial/url.py Thu Sep 30 14:07:57 2010 -0300
+++ b/mercurial/url.py Thu Sep 30 19:09:58 2010 -0500
@@ -7,7 +7,7 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
-import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO
+import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO, time
from i18n import _
import keepalive, util
@@ -469,6 +469,31 @@
_generic_start_transaction(self, h, req)
return keepalive.HTTPHandler._start_transaction(self, h, req)
+def _verifycert(cert, hostname):
+ '''Verify that cert (in socket.getpeercert() format) matches hostname and is
+ valid at this time. CRLs and subjectAltName are not handled.
+
+ Returns error message if any problems are found and None on success.
+ '''
+ if not cert:
+ return _('no certificate received')
+ notafter = cert.get('notAfter')
+ if notafter and time.time() > ssl.cert_time_to_seconds(notafter):
+ return _('certificate expired %s') % notafter
+ notbefore = cert.get('notBefore')
+ if notbefore and time.time() < ssl.cert_time_to_seconds(notbefore):
+ return _('certificate not valid before %s') % notbefore
+ dnsname = hostname.lower()
+ for s in cert.get('subject', []):
+ key, value = s[0]
+ if key == 'commonName':
+ certname = value.lower()
+ if (certname == dnsname or
+ '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1]):
+ return None
+ return _('certificate is for %s') % certname
+ return _('no commonName found in certificate')
+
if has_https:
class BetterHTTPS(httplib.HTTPSConnection):
send = keepalive.safesend
@@ -484,7 +509,11 @@
self.sock = _ssl_wrap_socket(sock, self.key_file,
self.cert_file, cert_reqs=CERT_REQUIRED,
ca_certs=cacerts)
- self.ui.debug(_('server identity verification succeeded\n'))
+ msg = _verifycert(self.sock.getpeercert(), self.host)
+ if msg:
+ raise util.Abort('%s certificate error: %s' % (self.host, msg))
+ self.ui.debug(_('%s certificate successfully verified\n') %
+ self.host)
else:
httplib.HTTPSConnection.connect(self)
--- a/tests/test-diff-upgrade Thu Sep 30 14:07:57 2010 -0300
+++ b/tests/test-diff-upgrade Thu Sep 30 19:09:58 2010 -0500
@@ -20,6 +20,7 @@
chmod +x unsetexec
echo binary > binary
python -c "file('rmbinary', 'wb').write('\0')"
+python -c "file('bintoregular', 'wb').write('\0')"
hg ci -Am addfiles
echo regular >> regular
echo newregular >> newregular
@@ -28,6 +29,7 @@
rm rmregular
echo exec >> exec
echo newexec > newexec
+echo bintoregular > bintoregular
chmod +x newexec
rm rmexec
chmod +x setexec
@@ -43,10 +45,11 @@
echo '% git=no: git diff for single regular file'
hg autodiff --git=yes regular
-echo '% git=auto: regular diff for regular files and removals'
-hg autodiff --git=auto regular newregular rmregular rmbinary rmexec
+echo '% git=auto: regular diff for regular files and non-binary removals'
+hg autodiff --git=auto regular newregular rmregular rmexec
-for f in exec newexec setexec unsetexec binary newbinary newempty rmempty; do
+for f in exec newexec setexec unsetexec binary newbinary newempty rmempty \
+ rmbinary bintoregular; do
echo '% git=auto: git diff for' $f
hg autodiff --git=auto $f
done
--- a/tests/test-diff-upgrade.out Thu Sep 30 14:07:57 2010 -0300
+++ b/tests/test-diff-upgrade.out Thu Sep 30 19:09:58 2010 -0500
@@ -1,5 +1,6 @@
% make a combination of new, changed and deleted file
adding binary
+adding bintoregular
adding exec
adding regular
adding rmbinary
@@ -17,40 +18,42 @@
removing rmexec
removing rmregular
% git=no: regular diff for all files
-diff -r b3f053cd7c7f binary
+diff -r a66d19b9302d binary
Binary file binary has changed
-diff -r b3f053cd7c7f exec
+diff -r a66d19b9302d bintoregular
+Binary file bintoregular has changed
+diff -r a66d19b9302d exec
--- a/exec
+++ b/exec
@@ -1,1 +1,2 @@
exec
+exec
-diff -r b3f053cd7c7f newbinary
+diff -r a66d19b9302d newbinary
Binary file newbinary has changed
-diff -r b3f053cd7c7f newexec
+diff -r a66d19b9302d newexec
--- /dev/null
+++ b/newexec
@@ -0,0 +1,1 @@
+newexec
-diff -r b3f053cd7c7f newregular
+diff -r a66d19b9302d newregular
--- /dev/null
+++ b/newregular
@@ -0,0 +1,1 @@
+newregular
-diff -r b3f053cd7c7f regular
+diff -r a66d19b9302d regular
--- a/regular
+++ b/regular
@@ -1,1 +1,2 @@
regular
+regular
-diff -r b3f053cd7c7f rmbinary
+diff -r a66d19b9302d rmbinary
Binary file rmbinary has changed
-diff -r b3f053cd7c7f rmexec
+diff -r a66d19b9302d rmexec
--- a/rmexec
+++ /dev/null
@@ -1,1 +0,0 @@
-rmexec
-diff -r b3f053cd7c7f rmregular
+diff -r a66d19b9302d rmregular
--- a/rmregular
+++ /dev/null
@@ -1,1 +0,0 @@
@@ -62,32 +65,30 @@
@@ -1,1 +1,2 @@
regular
+regular
-% git=auto: regular diff for regular files and removals
-diff -r b3f053cd7c7f newregular
+% git=auto: regular diff for regular files and non-binary removals
+diff -r a66d19b9302d newregular
--- /dev/null
+++ b/newregular
@@ -0,0 +1,1 @@
+newregular
-diff -r b3f053cd7c7f regular
+diff -r a66d19b9302d regular
--- a/regular
+++ b/regular
@@ -1,1 +1,2 @@
regular
+regular
-diff -r b3f053cd7c7f rmbinary
-Binary file rmbinary has changed
-diff -r b3f053cd7c7f rmexec
+diff -r a66d19b9302d rmexec
--- a/rmexec
+++ /dev/null
@@ -1,1 +0,0 @@
-rmexec
-diff -r b3f053cd7c7f rmregular
+diff -r a66d19b9302d rmregular
--- a/rmregular
+++ /dev/null
@@ -1,1 +0,0 @@
-rmregular
% git=auto: git diff for exec
-diff -r b3f053cd7c7f exec
+diff -r a66d19b9302d exec
--- a/exec
+++ b/exec
@@ -1,1 +1,2 @@
@@ -129,56 +130,71 @@
% git=auto: git diff for rmempty
diff --git a/rmempty b/rmempty
deleted file mode 100644
+% git=auto: git diff for rmbinary
+diff --git a/rmbinary b/rmbinary
+deleted file mode 100644
+Binary file rmbinary has changed
+% git=auto: git diff for bintoregular
+diff --git a/bintoregular b/bintoregular
+index f76dd238ade08917e6712764a16a22005a50573d..9c42f2b6427d8bf034b7bc23986152dc01bfd3ab
+GIT binary patch
+literal 13
+Uc$`bh%qz(+N=+}#Ni5<5043uE82|tP
+
% git=warn: regular diff with data loss warnings
-diff -r b3f053cd7c7f binary
+diff -r a66d19b9302d binary
Binary file binary has changed
-diff -r b3f053cd7c7f exec
+diff -r a66d19b9302d bintoregular
+Binary file bintoregular has changed
+diff -r a66d19b9302d exec
--- a/exec
+++ b/exec
@@ -1,1 +1,2 @@
exec
+exec
-diff -r b3f053cd7c7f newbinary
+diff -r a66d19b9302d newbinary
Binary file newbinary has changed
-diff -r b3f053cd7c7f newexec
+diff -r a66d19b9302d newexec
--- /dev/null
+++ b/newexec
@@ -0,0 +1,1 @@
+newexec
-diff -r b3f053cd7c7f newregular
+diff -r a66d19b9302d newregular
--- /dev/null
+++ b/newregular
@@ -0,0 +1,1 @@
+newregular
-diff -r b3f053cd7c7f regular
+diff -r a66d19b9302d regular
--- a/regular
+++ b/regular
@@ -1,1 +1,2 @@
regular
+regular
-diff -r b3f053cd7c7f rmbinary
+diff -r a66d19b9302d rmbinary
Binary file rmbinary has changed
-diff -r b3f053cd7c7f rmexec
+diff -r a66d19b9302d rmexec
--- a/rmexec
+++ /dev/null
@@ -1,1 +0,0 @@
-rmexec
-diff -r b3f053cd7c7f rmregular
+diff -r a66d19b9302d rmregular
--- a/rmregular
+++ /dev/null
@@ -1,1 +0,0 @@
-rmregular
data lost for: binary
+data lost for: bintoregular
data lost for: newbinary
data lost for: newempty
data lost for: newexec
+data lost for: rmbinary
data lost for: rmempty
data lost for: setexec
data lost for: unsetexec
% git=abort: fail on execute bit change
abort: losing data for setexec
% git=abort: succeed on regular file
-diff -r b3f053cd7c7f regular
+diff -r a66d19b9302d regular
--- a/regular
+++ b/regular
@@ -1,1 +1,2 @@
--- a/tests/test-doctest.py Thu Sep 30 14:07:57 2010 -0300
+++ b/tests/test-doctest.py Thu Sep 30 19:09:58 2010 -0500
@@ -5,18 +5,13 @@
import doctest
import mercurial.changelog
-# test doctest from changelog
-
doctest.testmod(mercurial.changelog)
-import mercurial.httprepo
-doctest.testmod(mercurial.httprepo)
-
-import mercurial.util
-doctest.testmod(mercurial.util)
-
import mercurial.dagparser
doctest.testmod(mercurial.dagparser, optionflags=doctest.NORMALIZE_WHITESPACE)
+import mercurial.url
+doctest.testmod(mercurial.url)
+
import hgext.convert.cvsps
doctest.testmod(hgext.convert.cvsps)
--- a/tests/test-git-import Thu Sep 30 14:07:57 2010 -0300
+++ b/tests/test-git-import Thu Sep 30 19:09:58 2010 -0500
@@ -1,5 +1,7 @@
#!/bin/sh
+. $TESTDIR/helpers.sh
+
hg init a
cd a
@@ -226,5 +228,20 @@
copy to foo3
EOF
hg tip -q
+cat foo3
-cat foo3
+echo % move text file and patch as binary
+echo a > text2
+hg ci -Am0
+hg import -d "1000000 0" -m rename-as-binary - <<"EOF"
+diff --git a/text2 b/binary2
+rename from text2
+rename to binary2
+index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
+GIT binary patch
+literal 5
+Mc$`b*O5$Pw00T?_*Z=?k
+
+EOF
+cat binary2 | repr
+hg st --copies --change .
\ No newline at end of file
--- a/tests/test-git-import.out Thu Sep 30 14:07:57 2010 -0300
+++ b/tests/test-git-import.out Thu Sep 30 19:09:58 2010 -0500
@@ -80,3 +80,10 @@
applying patch from stdin
14:c4cd9cdeaa74
foo
+% move text file and patch as binary
+adding text2
+applying patch from stdin
+'a\nb\n\x00'
+A binary2
+ text2
+R text2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-url.py Thu Sep 30 19:09:58 2010 -0500
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+
+def check(a, b):
+ if a != b:
+ print (a, b)
+
+from mercurial.url import _verifycert
+
+# Test non-wildcard certificates
+check(_verifycert({'subject': ((('commonName', 'example.com'),),)}, 'example.com'),
+ None)
+check(_verifycert({'subject': ((('commonName', 'example.com'),),)}, 'www.example.com'),
+ 'certificate is for example.com')
+check(_verifycert({'subject': ((('commonName', 'www.example.com'),),)}, 'example.com'),
+ 'certificate is for www.example.com')
+
+# Test wildcard certificates
+check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'www.example.com'),
+ None)
+check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'example.com'),
+ 'certificate is for *.example.com')
+check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'w.w.example.com'),
+ 'certificate is for *.example.com')
+
+# Avoid some pitfalls
+check(_verifycert({'subject': ((('commonName', '*.foo'),),)}, 'foo'),
+ 'certificate is for *.foo')
+check(_verifycert({'subject': ((('commonName', '*o'),),)}, 'foo'),
+ 'certificate is for *o')
+
+import time
+lastyear = time.gmtime().tm_year - 1
+nextyear = time.gmtime().tm_year + 1
+check(_verifycert({'notAfter': 'May 9 00:00:00 %s GMT' % lastyear}, 'example.com'),
+ 'certificate expired May 9 00:00:00 %s GMT' % lastyear)
+check(_verifycert({'notBefore': 'May 9 00:00:00 %s GMT' % nextyear}, 'example.com'),
+ 'certificate not valid before May 9 00:00:00 %s GMT' % nextyear)
+check(_verifycert({'notAfter': 'Sep 29 15:29:48 %s GMT' % nextyear, 'subject': ()}, 'example.com'),
+ 'no commonName found in certificate')
+check(_verifycert(None, 'example.com'),
+ 'no certificate received')