Rev 6093: (jr) Document gpgme usage in comments. (Jonathan Riddell) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Tue Aug 23 13:14:57 UTC 2011
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 6093 [merge]
revision-id: pqm at pqm.ubuntu.com-20110823131455-25rwamcvm0il00ps
parent: pqm at pqm.ubuntu.com-20110822103857-e7kb6tge3sfw4dt7
parent: jriddell at canonical.com-20110823103453-0b99yoab8ymva8sb
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2011-08-23 13:14:55 +0000
message:
(jr) Document gpgme usage in comments. (Jonathan Riddell)
modified:
bzrlib/commit_signature_commands.py sign_my_commits.py-20060215152201-5a6363365180e671
bzrlib/gpg.py gpg.py-20051017065112-8654536d415dacc6
bzrlib/tests/blackbox/test_sign_my_commits.py test_sign_my_commits.py-20060215152957-270238a1ffacc841
bzrlib/tests/test_gpg.py testgpg.py-20051017042228-9276cd40a784c93c
doc/en/release-notes/bzr-2.5.txt bzr2.5.txt-20110708125756-587p0hpw7oke4h05-1
=== modified file 'bzrlib/commit_signature_commands.py'
--- a/bzrlib/commit_signature_commands.py 2011-06-24 13:57:59 +0000
+++ b/bzrlib/commit_signature_commands.py 2011-08-04 10:28:02 +0000
@@ -172,6 +172,11 @@
if verbose:
for message in gpg_strategy.verbose_valid_message(result):
write_verbose(message)
+ write(gpg_strategy.expired_commit_message(count))
+ if verbose:
+ for message in gpg_strategy.verbose_expired_key_message(result,
+ repo):
+ write_verbose(message)
write(gpg_strategy.unknown_key_message(count))
if verbose:
for message in gpg_strategy.verbose_missing_key_message(result):
=== modified file 'bzrlib/gpg.py'
--- a/bzrlib/gpg.py 2011-07-11 10:53:46 +0000
+++ b/bzrlib/gpg.py 2011-08-23 10:34:53 +0000
@@ -52,6 +52,7 @@
SIGNATURE_KEY_MISSING = 1
SIGNATURE_NOT_VALID = 2
SIGNATURE_NOT_SIGNED = 3
+SIGNATURE_EXPIRED = 4
class DisabledGPGStrategy(object):
@@ -108,7 +109,8 @@
count = {SIGNATURE_VALID: 0,
SIGNATURE_KEY_MISSING: 0,
SIGNATURE_NOT_VALID: 0,
- SIGNATURE_NOT_SIGNED: 0}
+ SIGNATURE_NOT_SIGNED: 0,
+ SIGNATURE_EXPIRED: 0}
result = []
all_verifiable = True
for rev_id in revisions:
@@ -142,6 +144,12 @@
count[SIGNATURE_NOT_SIGNED]).format(
count[SIGNATURE_NOT_SIGNED])
+ def expired_commit_message(self, count):
+ return i18n.ngettext(u"{0} commit with key now expired",
+ u"{0} commits with key now expired",
+ count[SIGNATURE_EXPIRED]).format(
+ count[SIGNATURE_EXPIRED])
+
def _set_gpg_tty():
tty = os.environ.get('TTY')
@@ -244,29 +252,61 @@
except gpgme.GpgmeError,error:
raise errors.SignatureVerificationFailed(error[2])
+ # No result if input is invalid.
+ # test_verify_invalid()
if len(result) == 0:
return SIGNATURE_NOT_VALID, None
+ # User has specified a list of acceptable keys, check our result is in it.
+ # test_verify_unacceptable_key()
fingerprint = result[0].fpr
if self.acceptable_keys is not None:
- if not fingerprint in self.acceptable_keys:
+ if not fingerprint in self.acceptable_keys:
return SIGNATURE_KEY_MISSING, fingerprint[-8:]
+ # Check the signature actually matches the testament.
+ # test_verify_bad_testament()
if testament != plain_output.getvalue():
- return SIGNATURE_NOT_VALID, None
+ return SIGNATURE_NOT_VALID, None
+ # Yay gpgme set the valid bit.
+ # Can't write a test for this one as you can't set a key to be
+ # trusted using gpgme.
if result[0].summary & gpgme.SIGSUM_VALID:
key = self.context.get_key(fingerprint)
name = key.uids[0].name
email = key.uids[0].email
return SIGNATURE_VALID, name + " <" + email + ">"
+ # Sigsum_red indicates a problem, unfortunatly I have not been able
+ # to write any tests which actually set this.
if result[0].summary & gpgme.SIGSUM_RED:
return SIGNATURE_NOT_VALID, None
+ # GPG does not know this key.
+ # test_verify_unknown_key()
if result[0].summary & gpgme.SIGSUM_KEY_MISSING:
return SIGNATURE_KEY_MISSING, fingerprint[-8:]
- #summary isn't set if sig is valid but key is untrusted
+ # Summary isn't set if sig is valid but key is untrusted
+ # but if user has explicity set the key as acceptable we can validate it.
if result[0].summary == 0 and self.acceptable_keys is not None:
if fingerprint in self.acceptable_keys:
- return SIGNATURE_VALID, None
- else:
- return SIGNATURE_KEY_MISSING, None
+ # test_verify_untrusted_but_accepted()
+ return SIGNATURE_VALID, None
+ # test_verify_valid_but_untrusted()
+ if result[0].summary == 0 and self.acceptable_keys is None:
+ return SIGNATURE_NOT_VALID, None
+ if result[0].summary & gpgme.SIGSUM_KEY_EXPIRED:
+ expires = self.context.get_key(result[0].fpr).subkeys[0].expires
+ if expires > result[0].timestamp:
+ # The expired key was not expired at time of signing.
+ # test_verify_expired_but_valid()
+ return SIGNATURE_EXPIRED, fingerprint[-8:]
+ else:
+ # I can't work out how to create a test where the signature
+ # was expired at the time of signing.
+ return SIGNATURE_NOT_VALID, None
+ # A signature from a revoked key gets this.
+ # test_verify_revoked_signature()
+ if result[0].summary & gpgme.SIGSUM_SYS_ERROR:
+ return SIGNATURE_NOT_VALID, None
+ # Other error types such as revoked keys should (I think) be caught by
+ # SIGSUM_RED so anything else means something is buggy.
raise errors.SignatureVerificationFailed("Unknown GnuPG key "\
"verification result")
@@ -322,7 +362,8 @@
count = {SIGNATURE_VALID: 0,
SIGNATURE_KEY_MISSING: 0,
SIGNATURE_NOT_VALID: 0,
- SIGNATURE_NOT_SIGNED: 0}
+ SIGNATURE_NOT_SIGNED: 0,
+ SIGNATURE_EXPIRED: 0}
result = []
all_verifiable = True
for rev_id in revisions:
@@ -397,6 +438,27 @@
number).format(fingerprint, number) )
return result
+ def verbose_expired_key_message(self, result, repo):
+ """takes a verify result and returns list of expired key info"""
+ signers = {}
+ fingerprint_to_authors = {}
+ for rev_id, validity, fingerprint in result:
+ if validity == SIGNATURE_EXPIRED:
+ revision = repo.get_revision(rev_id)
+ authors = ', '.join(revision.get_apparent_authors())
+ signers.setdefault(fingerprint, 0)
+ signers[fingerprint] += 1
+ fingerprint_to_authors[fingerprint] = authors
+ result = []
+ for fingerprint, number in signers.items():
+ result.append( i18n.ngettext(u"{0} commit by author {1} with "\
+ "key {2} now expired",
+ u"{0} commits by author {1} with key {2} now "\
+ "expired",
+ number).format(number,
+ fingerprint_to_authors[fingerprint], fingerprint) )
+ return result
+
def valid_commits_message(self, count):
"""returns message for number of commits"""
return i18n.gettext(u"{0} commits with valid signatures").format(
@@ -422,3 +484,10 @@
u"{0} commits not signed",
count[SIGNATURE_NOT_SIGNED]).format(
count[SIGNATURE_NOT_SIGNED])
+
+ def expired_commit_message(self, count):
+ """returns message for number of commits"""
+ return i18n.ngettext(u"{0} commit with key now expired",
+ u"{0} commits with key now expired",
+ count[SIGNATURE_EXPIRED]).format(
+ count[SIGNATURE_EXPIRED])
=== modified file 'bzrlib/tests/blackbox/test_sign_my_commits.py'
--- a/bzrlib/tests/blackbox/test_sign_my_commits.py 2011-06-23 11:13:43 +0000
+++ b/bzrlib/tests/blackbox/test_sign_my_commits.py 2011-08-16 11:05:38 +0000
@@ -121,6 +121,7 @@
self.run_bzr('sign-my-commits')
out = self.run_bzr('verify-signatures', retcode=1)
self.assertEquals(('4 commits with valid signatures\n'
+ '0 commits with key now expired\n'
'0 commits with unknown keys\n'
'0 commits not valid\n'
'1 commit not signed\n', ''), out)
@@ -132,6 +133,7 @@
out = self.run_bzr(['verify-signatures', '--acceptable-keys=foo,bar'],
retcode=1)
self.assertEquals(('4 commits with valid signatures\n'
+ '0 commits with key now expired\n'
'0 commits with unknown keys\n'
'0 commits not valid\n'
'1 commit not signed\n', ''), out)
=== modified file 'bzrlib/tests/test_gpg.py'
--- a/bzrlib/tests/test_gpg.py 2011-07-12 13:20:12 +0000
+++ b/bzrlib/tests/test_gpg.py 2011-08-04 14:39:42 +0000
@@ -91,6 +91,8 @@
self.assertRaises(errors.BzrBadParameterUnicode,
self.assertProduces, u'foo')
+class TestVerify(TestCase):
+
def import_keys(self):
from StringIO import StringIO
import gpgme
@@ -187,10 +189,59 @@
-----END PGP PRIVATE KEY BLOCK-----
""")
+ revoked_key = StringIO("""-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+mI0ETjlW5gEEAOb/6P+TVM59E897wRtatxys2BhsHCXM4T7xjIiANfDwejDdifqh
+tluTfSJLLxPembtrrEjux1C0AJgc+f0MIfsc3Pr3eFJzKB2ot/1IVG1/1KnA0zt3
+W2xPT3lRib27WJ9Fag+dMtQaIzgJ7/n2DFxsFZ33FD2kxrEXB2exGg6FABEBAAGI
+pgQgAQIAEAUCTjlXkAkdAHJldm9rZWQACgkQjs6dvEpb0cQPHAP/Wi9rbx0e+1Sf
+ziGgyVdr3m3A6uvze5oXKVgFRbGRUYSH4/I8GW0W9x4TcRg9h+YaQ8NUdADr9kNE
+tKAljLqYA5qdqSfYuaij1M++Xj+KUZ359R74sHuQqwnRy1XXQNfRs/QpXA7vLdds
+rjg+pbWuXO92TZJUdnqtWW+VEyZBsPy0G3Rlc3Qga2V5IDx0ZXN0QGV4YW1wbGUu
+Y29tPoi4BBMBAgAiBQJOOVbmAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAK
+CRCOzp28SlvRxNWzA/42WVmI0b+6mF/imEOlY1TiyvrcpK250rkSDsCtL4lOwy7G
+antZhpgNfnXRd/ySfsS3EB6dpOWgOSxGRvWQhA+vxBT9BYNk49qd3JIrSaSWpR12
+rET8qO1rEQQFWsw03CxTGujxGlmEO+a1yguRXp2UWaY7FngcQmD+8q7BUIVm7riN
+BE45VuYBBADTEH2jHTjNCc5CMOhea6EJTrkx3upcEqB2oyhWeSWJiBGOxlcddsjo
+3J3/EmBB8kK1hM9TidD3SG64x1N287lg8ELJBlKv+pQVyxohGJ1u/THgpTDMMQcL
+luG5rAHQGSfyzKTiOnaTyBYg3M/nzgUOU9dKEFB0EA3tjUXFOT+r3wARAQABiJ8E
+GAECAAkFAk45VuYCGwwACgkQjs6dvEpb0cRSLQP/fzCWX2lXwlwWiVF8BOPF7o9z
+icHErc7/X17RGb4qj1kVf+UkRdUWJrbEVh4h6MncBIuA70WsYogiw+Kz/0LCtQAR
+YUJsPy/EL++OKPH1aFasOdTxwkTka85+RdYqhP1+z/aYLFMWq6mRFI+o6x2k5mGi
+7dMv2kKTJPoXUpiXJbg=
+=hLYO
+-----END PGP PUBLIC KEY BLOCK-----
+""")
+
+ expired_key = StringIO("""-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+mI0ETjZ6PAEEALkR4GcFQidCCxV7pgQwQd5MZua0YO2l92fVqHX+PhnZ6egCLKdD
+2bWlMUd6MLPF3FlRL7BBAxvW/DazkBOp7ljsnpMpptEzY49Uem1irYLYiVb9zK96
+0sQZzFxFkfEYetQEXC68mIck8tbySOX5NAOw++3jFm3J7dsU1R3XtYzRABEBAAG0
+G3Rlc3Qga2V5IDx0ZXN0QGV4YW1wbGUuY29tPoi+BBMBAgAoBQJONno8AhsDBQkA
+AVGABgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRAc4m97T40VEz+DA/9PBphG
+Yp9cHVaHSfTUKGTGgIbvRe60sFNpDCYZeAGDrygOMuI8MNzbVpwefRBFHVPx7jWd
+rrYMsLkcsNUS9D0baU+0D/qp7JVg7ZSQtG0O6IG4eTZhibteY1fu0+unlXmg9NHx
+5VvhwzBiJDYji00M2p/CZEMiYFUuy76CsxUpN7iNBE42ejwBBACkv2/mX7IPQg0C
+A3KSrJsJv+sdvKm4b4xuI4OwagwTIVz4KlTqV4IBrVjSBfwyMXucXz0bTW85qjgA
++n67td8vyjYYZUEz1uY9lSquQQDnAN0txL3cLHZXWiWOkmzZVddQtlflK2a/J9o0
+QkHPVUm+hc4l64dIzStrNl2S66fAvQARAQABiKUEGAECAA8FAk42ejwCGwwFCQAB
+UYAACgkQHOJve0+NFROEYQP/epg+o8iBs31hkSERyZjrRR66LpywezWj30Rn/3mX
+Fzi9HkF4xLemWOzdNt9C5PYrOep85PQg8haEjknxVjZFS0ikT1h3OWk/TF1ZrLVm
+WzyX8DaHQEjKpLJJjXcAbTiZBNMk0QaVC9RvIeHpCf3n3DC49DdjsPJRMKOn8KDi
+kRk=
+=p0gt
+-----END PGP PUBLIC KEY BLOCK-----
+""")
context.import_(key)
context.import_(secret_key)
+ context.import_(revoked_key)
+ context.import_(expired_key)
- def test_verify_valid(self):
+ def test_verify_untrusted_but_accepted(self):
+ #untrusted by gpg but listed as acceptable_keys by user
self.requireFeature(features.gpgme)
self.import_keys()
@@ -221,6 +272,67 @@
self.assertEqual((gpg.SIGNATURE_VALID, None), my_gpg.verify(content,
plain))
+ def test_verify_unacceptable_key(self):
+ self.requireFeature(features.gpgme)
+ self.import_keys()
+
+ content = """-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+bazaar-ng testament short form 1
+revision-id: amy at example.com-20110527185938-hluafawphszb8dl1
+sha1: 6411f9bdf6571200357140c9ce7c0f50106ac9a4
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iQEcBAEBAgAGBQJN+ekFAAoJEIdoGx7jCA5FGtEH/i+XxJRvqU6wdBtLVrGBMAGk
+FZ5VP+KyXYtymSbgSstj/vM12NeMIeFs3xGnNnYuX1MIcY6We5TKtCH0epY6ym5+
+6g2Q2QpQ5/sT2d0mWzR0K4uVngmxVQaXTdk5PdZ40O7ULeDLW6CxzxMHyUL1rsIx
+7UBUTBh1O/1n3ZfD99hUkm3hVcnsN90uTKH59zV9NWwArU0cug60+5eDKJhSJDbG
+rIwlqbFAjDZ7L/48e+IaYIJwBZFzMBpJKdCxzALLtauMf+KK8hGiL2hrRbWm7ty6
+NgxfkMYOB4rDPdSstT35N+5uBG3n/UzjxHssi0svMfVETYYX40y57dm2eZQXFp8=
+=iwsn
+-----END PGP SIGNATURE-----
+"""
+ plain = """bazaar-ng testament short form 1
+revision-id: amy at example.com-20110527185938-hluafawphszb8dl1
+sha1: 6411f9bdf6571200357140c9ce7c0f50106ac9a4
+"""
+ my_gpg = gpg.GPGStrategy(FakeConfig())
+ my_gpg.set_acceptable_keys("foo at example.com")
+ self.assertEqual((gpg.SIGNATURE_KEY_MISSING, u'E3080E45'),
+ my_gpg.verify(content, plain))
+
+ def test_verify_valid_but_untrusted(self):
+ self.requireFeature(features.gpgme)
+ self.import_keys()
+
+ content = """-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+bazaar-ng testament short form 1
+revision-id: amy at example.com-20110527185938-hluafawphszb8dl1
+sha1: 6411f9bdf6571200357140c9ce7c0f50106ac9a4
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iQEcBAEBAgAGBQJN+ekFAAoJEIdoGx7jCA5FGtEH/i+XxJRvqU6wdBtLVrGBMAGk
+FZ5VP+KyXYtymSbgSstj/vM12NeMIeFs3xGnNnYuX1MIcY6We5TKtCH0epY6ym5+
+6g2Q2QpQ5/sT2d0mWzR0K4uVngmxVQaXTdk5PdZ40O7ULeDLW6CxzxMHyUL1rsIx
+7UBUTBh1O/1n3ZfD99hUkm3hVcnsN90uTKH59zV9NWwArU0cug60+5eDKJhSJDbG
+rIwlqbFAjDZ7L/48e+IaYIJwBZFzMBpJKdCxzALLtauMf+KK8hGiL2hrRbWm7ty6
+NgxfkMYOB4rDPdSstT35N+5uBG3n/UzjxHssi0svMfVETYYX40y57dm2eZQXFp8=
+=iwsn
+-----END PGP SIGNATURE-----
+"""
+ plain = """bazaar-ng testament short form 1
+revision-id: amy at example.com-20110527185938-hluafawphszb8dl1
+sha1: 6411f9bdf6571200357140c9ce7c0f50106ac9a4
+"""
+ my_gpg = gpg.GPGStrategy(FakeConfig())
+ self.assertEqual((gpg.SIGNATURE_NOT_VALID, None), my_gpg.verify(content,
+ plain))
+
def test_verify_bad_testament(self):
self.requireFeature(features.gpgme)
self.import_keys()
@@ -252,6 +364,31 @@
self.assertEqual((gpg.SIGNATURE_NOT_VALID, None), my_gpg.verify(content,
plain))
+
+ def test_verify_revoked_signature(self):
+ self.requireFeature(features.gpgme)
+ self.import_keys()
+
+ content = """-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+asdf
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iJwEAQECAAYFAk45V18ACgkQjs6dvEpb0cSIZQP/eOGTXGPlrNwvDkcX2d8O///I
+ecB4sUIUEpv1XAk1MkNu58lsjjK72lRaLusEGqd7HwrFmpxVeVs0oWLg23PNPCFs
+yJBID9ma+VxFVPtkEFnrc1R72sBJLfBcTxMkwVTC8eeznjdtn+cg+aLkxbPdrGnr
+JFA6kUIJU2w9LU/b88Y=
+=UuRX
+-----END PGP SIGNATURE-----
+"""
+ plain = """asdf\n"""
+ my_gpg = gpg.GPGStrategy(FakeConfig())
+ my_gpg.set_acceptable_keys("test at example.com")
+ self.assertEqual((gpg.SIGNATURE_NOT_VALID, None), my_gpg.verify(content,
+ plain))
+
def test_verify_invalid(self):
self.requireFeature(features.gpgme)
content = """-----BEGIN PGP SIGNED MESSAGE-----
@@ -276,6 +413,55 @@
self.assertEqual((gpg.SIGNATURE_NOT_VALID, None),
my_gpg.verify(content, plain))
+ def test_verify_expired_but_valid(self):
+ self.requireFeature(features.gpgme)
+ content = """-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+bazaar-ng testament short form 1
+revision-id: test at example.com-20110801100657-f1dr1nompeex723z
+sha1: 59ab434be4c2d5d646dee84f514aa09e1b72feeb
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.10 (GNU/Linux)
+
+iJwEAQECAAYFAk42esUACgkQHOJve0+NFRPc5wP7BoZkzBU8JaHMLv/LmqLr0sUz
+zuE51ofZZ19L7KVtQWsOi4jFy0fi4A5TFwO8u9SOfoREGvkw292Uty9subSouK5/
+mFmDOYPQ+O83zWgYZsBmMJWYDZ+X9I6XXZSbPtV/7XyTjaxtl5uRnDVJjg+AzKvD
+dTp8VatVVrwuvzOPDVc=
+=uHen
+-----END PGP SIGNATURE-----
+"""
+ plain = """bazaar-ng testament short form 1
+revision-id: test at example.com-20110801100657-f1dr1nompeex723z
+sha1: 59ab434be4c2d5d646dee84f514aa09e1b72feeb
+"""
+ my_gpg = gpg.GPGStrategy(FakeConfig())
+ self.assertEqual((gpg.SIGNATURE_EXPIRED, u'4F8D1513'),
+ my_gpg.verify(content, plain))
+
+ def test_verify_unknown_key(self):
+ self.requireFeature(features.gpgme)
+ content = """-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+asdf
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iQEcBAEBAgAGBQJOORKwAAoJENf6AkFdUeVvJDYH/1Cz+AJn1Jvy5n64o+0fZ5Ow
+Y7UQb4QQTIOV7jI7n4hv/yBzuHrtImFzYvQl/o2Ezzi8B8L5gZtQy+xCUF+Q8iWs
+gytZ5JUtSze7hDZo1NUl4etjoRGYqRfrUcvE2LkVH2dFbDGyyQfVmoeSHa5akuuP
+QZmyg2F983rACVIpGvsqTH6RcBdvE9vx68lugeKQA8ArDn39/74FBFipFzrXSPij
+eKFpl+yZmIb3g6HkPIC8o4j/tMvc37xF1OG5sBu8FT0+FC+VgY7vAblneDftAbyP
+sIODx4WcfJtjLG/qkRYqJ4gDHo0eMpTJSk2CWebajdm4b+JBrM1F9mgKuZFLruE=
+=RNR5
+-----END PGP SIGNATURE-----
+"""
+ plain = "asdf\n"
+ my_gpg = gpg.GPGStrategy(FakeConfig())
+ self.assertEqual((gpg.SIGNATURE_KEY_MISSING, u'5D51E56F'),
+ my_gpg.verify(content, plain))
+
def test_set_acceptable_keys(self):
self.requireFeature(features.gpgme)
self.import_keys()
=== modified file 'doc/en/release-notes/bzr-2.5.txt'
--- a/doc/en/release-notes/bzr-2.5.txt 2011-08-20 14:41:12 +0000
+++ b/doc/en/release-notes/bzr-2.5.txt 2011-08-23 10:27:54 +0000
@@ -81,11 +81,15 @@
* Relative local paths can now be specified in URL syntax by using the
"file:" prefix. (Jelmer Vernooij)
+* Report commits signed with expired keys in "verify-signatures".
+ (Jonathan Riddell, #804254)
+
* bzr add now skips large files in recursive mode. The default "large"
size is 20MB, and is configurable via the add.maximum_file_size
option. A value of 0 disables skipping. Named items passed to add are
never skipped. (Shannon Weyrick, #54624)
+
Improvements
************
More information about the bazaar-commits
mailing list