Rev 4077: (James) Support multiple authors for revisions. in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Wed Mar 4 15:25:11 GMT 2009


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 4077
revision-id: pqm at pqm.ubuntu.com-20090304152507-pbmk3iuhsrsu22it
parent: pqm at pqm.ubuntu.com-20090304144239-ttukklew8lkxslgy
parent: jelmer at samba.org-20090304142432-j6c5py19m3xf0v4x
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2009-03-04 15:25:07 +0000
message:
  (James) Support multiple authors for revisions.
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/annotate.py             annotate.py-20050922133147-7c60541d2614f022
  bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
  bzrlib/log.py                  log.py-20050505065812-c40ce11702fe5fb1
  bzrlib/mutabletree.py          mutabletree.py-20060906023413-4wlkalbdpsxi2r4y-2
  bzrlib/revision.py             revision.py-20050309040759-e77802c08f3999d5
  bzrlib/tests/blackbox/test_commit.py test_commit.py-20060212094538-ae88fc861d969db0
  bzrlib/tests/test_annotate.py  test_annotate.py-20061213215015-sttc9agsxomls7q0-1
  bzrlib/tests/test_commit.py    test_commit.py-20050914060732-279f057f8c295434
  bzrlib/tests/test_log.py       testlog.py-20050728115707-1a514809d7d49309
  bzrlib/tests/test_options.py   testoptions.py-20051014093702-96457cfc86319a8f
  bzrlib/tests/test_revision.py  testrevision.py-20050804210559-46f5e1eb67b01289
  doc/developers/revision-properties.txt revisionproperties.t-20070807133526-w57m8zv5o7t5kugm-1
  doc/en/user-guide/recording_changes.txt recording_changes.tx-20071121073725-0corxykv5irjal00-4
    ------------------------------------------------------------
    revno: 4056.2.5
    revision-id: jelmer at samba.org-20090304142432-j6c5py19m3xf0v4x
    parent: jw+debian at jameswestby.net-20090302130213-66oo1wcemlypyyri
    parent: pqm at pqm.ubuntu.com-20090303085413-35seprvnu885xorz
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: authors
    timestamp: Wed 2009-03-04 15:24:32 +0100
    message:
      Merge bzr.dev.
    added:
      bzrlib/tests/test_patches_data/diff-7 diff7-20081201221107-q9f611rir3xx1hdp-1
      bzrlib/tests/test_patches_data/mod-7 mod7-20081201221107-q9f611rir3xx1hdp-2
      bzrlib/tests/test_patches_data/orig-7 orig7-20081201221107-q9f611rir3xx1hdp-3
      doc/developers/ec2-windows.txt ec2windows.txt-20090219062112-2ga0nqpcm7n02njf-1
      doc/en/user-guide/filtered_views.txt filtered_views.txt-20090226100856-a16ba1v97v91ru58-1
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/bzrdir.py               bzrdir.py-20060131065624-156dfea39c4387cb
      bzrlib/fetch.py                fetch.py-20050818234941-26fea6105696365d
      bzrlib/graph.py                graph_walker.py-20070525030359-y852guab65d4wtn0-1
      bzrlib/pack.py                 container.py-20070607160755-tr8zc26q18rn0jnb-1
      bzrlib/patches.py              patches.py-20050727183609-378c1cc5972ce908
      bzrlib/plugins/launchpad/__init__.py __init__.py-20060315182712-2d5feebd2a1032dc
      bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
      bzrlib/plugins/launchpad/test_lp_open.py test_lp_open.py-20090125174355-hxrsxh3sj84225qu-1
      bzrlib/plugins/launchpad/test_lp_service.py test_lp_service.py-20080213034527-drf0ucr2x1js3onb-1
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/repofmt/knitrepo.py     knitrepo.py-20070206081537-pyy4a00xdas0j4pf-1
      bzrlib/repofmt/pack_repo.py    pack_repo.py-20070813041115-gjv5ma7ktfqwsjgn-1
      bzrlib/repofmt/weaverepo.py    presplitout.py-20070125045333-wfav3tsh73oxu3zk-1
      bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
      bzrlib/smart/bzrdir.py         bzrdir.py-20061122024551-ol0l0o0oofsu9b3t-1
      bzrlib/smart/protocol.py       protocol.py-20061108035435-ot0lstk2590yqhzr-1
      bzrlib/smart/repository.py     repository.py-20061128022038-vr5wy5bubyb8xttk-1
      bzrlib/smart/request.py        request.py-20061108095550-gunadhxmzkdjfeek-1
      bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
      bzrlib/tests/blackbox/test_branch.py test_branch.py-20060524161337-noms9gmcwqqrfi8y-1
      bzrlib/tests/blackbox/test_push.py test_push.py-20060329002750-929af230d5d22663
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
      bzrlib/tests/branch_implementations/test_create_clone.py test_create_clone.py-20090225031440-8ybpkzojo7cvourv-1
      bzrlib/tests/branch_implementations/test_push.py test_push.py-20070130153159-fhfap8uoifevg30j-1
      bzrlib/tests/branch_implementations/test_sprout.py test_sprout.py-20070521151739-b8t8p7axw1h966ws-1
      bzrlib/tests/bzrdir_implementations/test_bzrdir.py test_bzrdir.py-20060131065642-0ebeca5e30e30866
      bzrlib/tests/interrepository_implementations/__init__.py __init__.py-20060220054744-baf49a1f88f17b1a
      bzrlib/tests/per_repository/test_fetch.py test_fetch.py-20070814052151-5cxha9slx4c93uog-1
      bzrlib/tests/per_repository/test_repository.py test_repository.py-20060131092128-ad07f494f5c9d26c
      bzrlib/tests/test_fetch.py     testfetch.py-20050825090644-f73e07e7dfb1765a
      bzrlib/tests/test_graph.py     test_graph_walker.py-20070525030405-enq4r60hhi9xrujc-1
      bzrlib/tests/test_http.py      testhttp.py-20051018020158-b2eef6e867c514d9
      bzrlib/tests/test_pack_repository.py test_pack_repository-20080801043947-eaw0e6h2gu75kwmy-1
      bzrlib/tests/test_patches.py   test_patches.py-20051231203844-f4974d20f6aea09c
      bzrlib/tests/test_remote.py    test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
      bzrlib/tests/test_repository.py test_repository.py-20060131075918-65c555b881612f4d
      bzrlib/tests/test_selftest.py  test_selftest.py-20051202044319-c110a115d8c0456a
      bzrlib/tests/test_smart.py     test_smart.py-20061122024551-ol0l0o0oofsu9b3t-2
      bzrlib/tests/test_smart_request.py test_smart_request.p-20090211070731-o38wayv3asm25d6a-1
      bzrlib/tests/test_smart_transport.py test_ssh_transport.py-20060608202016-c25gvf1ob7ypbus6-2
      bzrlib/tests/test_transport_implementations.py test_transport_implementations.py-20051227111451-f97c5c7d5c49fce7
      bzrlib/transport/ftp/__init__.py ftp.py-20051116161804-58dc9506548c2a53
      bzrlib/transport/http/_urllib2_wrappers.py _urllib2_wrappers.py-20060913231729-ha9ugi48ktx481ao-1
      bzrlib/transport/remote.py     ssh.py-20060608202016-c25gvf1ob7ypbus6-1
      bzrlib/tree.py                 tree.py-20050309040759-9d5f2496be663e77
      bzrlib/ui/text.py              text.py-20051130153916-2e438cffc8afc478
      bzrlib/versionedfile.py        versionedfile.py-20060222045106-5039c71ee3b65490
      doc/developers/index.txt       index.txt-20070508041241-qznziunkg0nffhiw-1
      doc/developers/network-protocol.txt networkprotocol.txt-20070903044232-woustorrjbmg5zol-1
      doc/en/user-guide/index.txt    index.txt-20060622101119-tgwtdci8z769bjb9-2
    ------------------------------------------------------------
    revno: 4056.2.4
    revision-id: jw+debian at jameswestby.net-20090302130213-66oo1wcemlypyyri
    parent: jw+debian at jameswestby.net-20090227151434-kk7lqj62fnqmj2ng
    committer: James Westby <jw+debian at jameswestby.net>
    branch nick: bzr.dev
    timestamp: Mon 2009-03-02 13:02:13 +0000
    message:
      Document the --author option to commit.
    modified:
      doc/en/user-guide/recording_changes.txt recording_changes.tx-20071121073725-0corxykv5irjal00-4
    ------------------------------------------------------------
    revno: 4056.2.3
    revision-id: jw+debian at jameswestby.net-20090227151434-kk7lqj62fnqmj2ng
    parent: jw+debian at jameswestby.net-20090226193625-b9idy1369ghwm427
    committer: James Westby <jw+debian at jameswestby.net>
    branch nick: bzr.dev
    timestamp: Fri 2009-02-27 15:14:34 +0000
    message:
      Use a new "authors" revision property to allow multiple authors
      
      The "authors" revision property holds a "\n" separated list of
      authors.
      
      "author" is still read, but will be overriden by "authors" if
      present.
    modified:
      bzrlib/log.py                  log.py-20050505065812-c40ce11702fe5fb1
      bzrlib/mutabletree.py          mutabletree.py-20060906023413-4wlkalbdpsxi2r4y-2
      bzrlib/revision.py             revision.py-20050309040759-e77802c08f3999d5
      bzrlib/tests/blackbox/test_commit.py test_commit.py-20060212094538-ae88fc861d969db0
      bzrlib/tests/test_commit.py    test_commit.py-20050914060732-279f057f8c295434
      bzrlib/tests/test_log.py       testlog.py-20050728115707-1a514809d7d49309
      bzrlib/tests/test_revision.py  testrevision.py-20050804210559-46f5e1eb67b01289
      doc/developers/revision-properties.txt revisionproperties.t-20070807133526-w57m8zv5o7t5kugm-1
    ------------------------------------------------------------
    revno: 4056.2.2
    revision-id: jw+debian at jameswestby.net-20090226193625-b9idy1369ghwm427
    parent: jw+debian at jameswestby.net-20090226192153-wcqvwsjb5cn37lb9
    committer: James Westby <jw+debian at jameswestby.net>
    branch nick: bzr.dev
    timestamp: Thu 2009-02-26 19:36:25 +0000
    message:
      Add the bug number to NEWS.
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
    ------------------------------------------------------------
    revno: 4056.2.1
    revision-id: jw+debian at jameswestby.net-20090226192153-wcqvwsjb5cn37lb9
    parent: pqm at pqm.ubuntu.com-20090226073908-1fjp24b0llwqc0l7
    committer: James Westby <jw+debian at jameswestby.net>
    branch nick: bzr.dev
    timestamp: Thu 2009-02-26 19:21:53 +0000
    message:
      Allow specifying multiple authors for a revision.
      
      The revision property "author" is now a "\n"-separated list of authors.
      
      --author can be specified multiple times to populate that list at commit
      time.
      
      get_apparent_author() is deprecated in favour of get_apparent_authors(),
      which will return a list.
      
      Some things (e.g. annotate) still just use the first item from that list,
      but there is now support for other code to use all of the authors.
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/annotate.py             annotate.py-20050922133147-7c60541d2614f022
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
      bzrlib/log.py                  log.py-20050505065812-c40ce11702fe5fb1
      bzrlib/mutabletree.py          mutabletree.py-20060906023413-4wlkalbdpsxi2r4y-2
      bzrlib/revision.py             revision.py-20050309040759-e77802c08f3999d5
      bzrlib/tests/blackbox/test_commit.py test_commit.py-20060212094538-ae88fc861d969db0
      bzrlib/tests/test_annotate.py  test_annotate.py-20061213215015-sttc9agsxomls7q0-1
      bzrlib/tests/test_commit.py    test_commit.py-20050914060732-279f057f8c295434
      bzrlib/tests/test_options.py   testoptions.py-20051014093702-96457cfc86319a8f
      bzrlib/tests/test_revision.py  testrevision.py-20050804210559-46f5e1eb67b01289
      doc/developers/revision-properties.txt revisionproperties.t-20070807133526-w57m8zv5o7t5kugm-1
=== modified file 'NEWS'
--- a/NEWS	2009-03-03 08:54:13 +0000
+++ b/NEWS	2009-03-04 14:24:32 +0000
@@ -29,6 +29,9 @@
     * The ``-Dmemory`` flag now gives memory information on Windows.
       (John Arbash Meinel)
 
+    * Multiple authors for a commit can now be recorded by using the "--author"
+      option multiple times. (James Westby, #185772)
+
     * New command ``bzr launchpad-open`` opens a Launchpad web page for that
       branch in your web browser, as long as the branch is on Launchpad at all.
       (Jonathan Lange)
@@ -122,8 +125,15 @@
       UI at all - indeed it only reflects the repository status not
       changes to the branch itself. (Robert Collins)
 
+    * MutableTree.commit now favours the "authors" argument, with the old
+      "author" argument being deprecated.
+
     * Remove deprecated EmptyTree.  (Martin Pool)
 
+    * Revision.get_apparent_author() is now deprecated, replaced by
+      Revision.get_apparent_authors(), which returns a list. The former
+      now returns the first item that would be returned from the second.
+
     * The ``_fetch_*`` attributes on ``Repository`` are now on
       ``RepositoryFormat``, more accurately reflecting their intent (they
       describe a disk format capability, not state of a particular

=== modified file 'bzrlib/annotate.py'
--- a/bzrlib/annotate.py	2009-02-23 15:29:35 +0000
+++ b/bzrlib/annotate.py	2009-02-26 19:21:53 +0000
@@ -231,7 +231,7 @@
                                      time.gmtime(rev.timestamp + tz))
             # a lazy way to get something like the email address
             # TODO: Get real email address
-            author = rev.get_apparent_author()
+            author = rev.get_apparent_authors()[0]
             try:
                 author = extract_email_address(author)
             except errors.NoEmailInUsername:

=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py	2009-02-25 23:12:24 +0000
+++ b/bzrlib/builtins.py	2009-02-26 19:21:53 +0000
@@ -2633,6 +2633,8 @@
     If author of the change is not the same person as the committer, you can
     specify the author's name using the --author option. The name should be
     in the same format as a committer-id, e.g. "John Doe <jdoe at example.com>".
+    If there is more than one author of the change you can specify the option
+    multiple times, once for each author.
 
     A selected-file commit may fail in some cases where the committed
     tree would be invalid. Consider::
@@ -2682,7 +2684,7 @@
                     "files in the working tree."),
              ListOption('fixes', type=str,
                     help="Mark a bug as being fixed by this revision."),
-             Option('author', type=unicode,
+             ListOption('author', type=unicode,
                     help="Set the author's name, if it's different "
                          "from the committer."),
              Option('local',
@@ -2785,7 +2787,7 @@
                         specific_files=selected_list,
                         allow_pointless=unchanged, strict=strict, local=local,
                         reporter=None, verbose=verbose, revprops=properties,
-                        author=author,
+                        authors=author,
                         exclude=safe_relpath_files(tree, exclude))
         except PointlessCommit:
             # FIXME: This should really happen before the file is read in;

=== modified file 'bzrlib/log.py'
--- a/bzrlib/log.py	2009-02-23 15:29:35 +0000
+++ b/bzrlib/log.py	2009-02-27 15:14:34 +0000
@@ -1174,7 +1174,7 @@
         return address
 
     def short_author(self, rev):
-        name, address = config.parse_username(rev.get_apparent_author())
+        name, address = config.parse_username(rev.get_apparent_authors()[0])
         if name:
             return name
         return address
@@ -1216,10 +1216,11 @@
                 to_file.write(indent + 'parent: %s\n' % (parent_id,))
         self.show_properties(revision.rev, indent)
 
-        author = revision.rev.properties.get('author', None)
-        if author is not None:
-            to_file.write(indent + 'author: %s\n' % (author,))
-        to_file.write(indent + 'committer: %s\n' % (revision.rev.committer,))
+        committer = revision.rev.committer
+        authors = revision.rev.get_apparent_authors()
+        if authors != [committer]:
+            to_file.write(indent + 'author: %s\n' % (", ".join(authors),))
+        to_file.write(indent + 'committer: %s\n' % (committer,))
 
         branch_nick = revision.rev.properties.get('branch-nick', None)
         if branch_nick is not None:

=== modified file 'bzrlib/mutabletree.py'
--- a/bzrlib/mutabletree.py	2009-01-17 01:30:58 +0000
+++ b/bzrlib/mutabletree.py	2009-02-27 15:14:34 +0000
@@ -28,6 +28,7 @@
     add,
     bzrdir,
     hooks,
+    symbol_versioning,
     )
 from bzrlib.osutils import dirname
 from bzrlib.revisiontree import RevisionTree
@@ -41,7 +42,6 @@
     )
 from bzrlib.decorators import needs_read_lock, needs_write_lock
 from bzrlib.osutils import splitpath
-from bzrlib.symbol_versioning import DEPRECATED_PARAMETER
 
 
 def needs_tree_write_lock(unbound):
@@ -192,12 +192,32 @@
             revprops['branch-nick'] = self.branch._get_nick(
                 kwargs.get('local', False),
                 possible_master_transports)
+        authors = kwargs.pop('authors', None)
         author = kwargs.pop('author', None)
+        if authors is not None:
+            if author is not None:
+                raise AssertionError('Specifying both author and authors '
+                        'is not allowed. Specify just authors instead')
+            if 'author' in revprops or 'authors' in revprops:
+                # XXX: maybe we should just accept one of them?
+                raise AssertionError('author property given twice')
+            if authors:
+                for individual in authors:
+                    if '\n' in individual:
+                        raise AssertionError('\\n is not a valid character '
+                                'in an author identity')
+                revprops['authors'] = '\n'.join(authors)
         if author is not None:
-            if 'author' in revprops:
+            symbol_versioning.warn('The parameter author was deprecated'
+                   ' in version 1.13. Use authors instead',
+                   DeprecationWarning)
+            if 'author' in revprops or 'authors' in revprops:
                 # XXX: maybe we should just accept one of them?
                 raise AssertionError('author property given twice')
-            revprops['author'] = author
+            if '\n' in author:
+                raise AssertionError('\\n is not a valid character '
+                        'in an author identity')
+            revprops['authors'] = author
         # args for wt.commit start at message from the Commit.commit method,
         args = (message, ) + args
         for hook in MutableTree.hooks['start_commit']:

=== modified file 'bzrlib/revision.py'
--- a/bzrlib/revision.py	2009-01-17 01:30:58 +0000
+++ b/bzrlib/revision.py	2009-02-27 15:14:34 +0000
@@ -112,13 +112,33 @@
         """
         return self.message.lstrip().split('\n', 1)[0]
 
+    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((1, 13, 0)))
     def get_apparent_author(self):
         """Return the apparent author of this revision.
 
-        If the revision properties contain the author name,
-        return it. Otherwise return the committer name.
-        """
-        return self.properties.get('author', self.committer)
+        This method is deprecated in favour of get_apparent_authors.
+
+        If the revision properties contain any author names,
+        return the first. Otherwise return the committer name.
+        """
+        return self.get_apparent_authors()[0]
+
+    def get_apparent_authors(self):
+        """Return the apparent authors of this revision.
+
+        If the revision properties contain the names of the authors,
+        return them. Otherwise return the committer name.
+
+        The return value will be a list containing at least one element.
+        """
+        authors = self.properties.get('authors', None)
+        if authors is None:
+            author = self.properties.get('author', None)
+            if author is None:
+                return [self.committer]
+            return [author]
+        else:
+            return authors.split("\n")
 
 
 def iter_ancestors(revision_id, revision_source, only_present=False):

=== modified file 'bzrlib/tests/blackbox/test_commit.py'
--- a/bzrlib/tests/blackbox/test_commit.py	2009-01-17 01:30:58 +0000
+++ b/bzrlib/tests/blackbox/test_commit.py	2009-02-27 15:14:34 +0000
@@ -554,7 +554,7 @@
                      "tree/hello.txt"])
         last_rev = tree.branch.repository.get_revision(tree.last_revision())
         properties = last_rev.properties
-        self.assertEqual(u'John D\xf6 <jdoe at example.com>', properties['author'])
+        self.assertEqual(u'John D\xf6 <jdoe at example.com>', properties['authors'])
 
     def test_author_no_email(self):
         """Author's name without an email address is allowed, too."""
@@ -565,7 +565,18 @@
                                 "tree/hello.txt")
         last_rev = tree.branch.repository.get_revision(tree.last_revision())
         properties = last_rev.properties
-        self.assertEqual('John Doe', properties['author'])
+        self.assertEqual('John Doe', properties['authors'])
+
+    def test_multiple_authors(self):
+        """Multiple authors can be specyfied, and all are stored."""
+        tree = self.make_branch_and_tree('tree')
+        self.build_tree(['tree/hello.txt'])
+        tree.add('hello.txt')
+        out, err = self.run_bzr("commit -m hello --author='John Doe' "
+                                "--author='Jane Rey' tree/hello.txt")
+        last_rev = tree.branch.repository.get_revision(tree.last_revision())
+        properties = last_rev.properties
+        self.assertEqual('John Doe\nJane Rey', properties['authors'])
 
     def test_partial_commit_with_renames_in_tree(self):
         # this test illustrates bug #140419

=== modified file 'bzrlib/tests/test_annotate.py'
--- a/bzrlib/tests/test_annotate.py	2009-01-17 01:30:58 +0000
+++ b/bzrlib/tests/test_annotate.py	2009-02-26 19:21:53 +0000
@@ -475,7 +475,7 @@
         tree1.add(['b'], ['b-id'])
         tree1.commit('b', rev_id='rev-2',
                      committer='Committer <committer at example.com>',
-                     author='Author <author at example.com>',
+                     authors=['Author <author at example.com>'],
                      timestamp=1166046000.00, timezone=0)
 
         tree1.lock_read()

=== modified file 'bzrlib/tests/test_commit.py'
--- a/bzrlib/tests/test_commit.py	2009-01-17 01:30:58 +0000
+++ b/bzrlib/tests/test_commit.py	2009-02-27 15:14:34 +0000
@@ -747,16 +747,50 @@
         rev_id = tree.commit('commit 1')
         rev = tree.branch.repository.get_revision(rev_id)
         self.assertFalse('author' in rev.properties)
+        self.assertFalse('authors' in rev.properties)
 
     def test_commit_author(self):
         """Passing a non-empty author kwarg to MutableTree.commit should add
         the 'author' revision property.
         """
         tree = self.make_branch_and_tree('foo')
-        rev_id = tree.commit('commit 1', author='John Doe <jdoe at example.com>')
+        rev_id = self.callDeprecated(['The parameter author was '
+                'deprecated in version 1.13. Use authors instead'],
+                tree.commit, 'commit 1', author='John Doe <jdoe at example.com>')
         rev = tree.branch.repository.get_revision(rev_id)
         self.assertEqual('John Doe <jdoe at example.com>',
-                         rev.properties['author'])
+                         rev.properties['authors'])
+        self.assertFalse('author' in rev.properties)
+
+    def test_commit_empty_authors_list(self):
+        """Passing an empty list to authors shouldn't add the property."""
+        tree = self.make_branch_and_tree('foo')
+        rev_id = tree.commit('commit 1', authors=[])
+        rev = tree.branch.repository.get_revision(rev_id)
+        self.assertFalse('author' in rev.properties)
+        self.assertFalse('authors' in rev.properties)
+
+    def test_multiple_authors(self):
+        tree = self.make_branch_and_tree('foo')
+        rev_id = tree.commit('commit 1',
+                authors=['John Doe <jdoe at example.com>',
+                         'Jane Rey <jrey at example.com>'])
+        rev = tree.branch.repository.get_revision(rev_id)
+        self.assertEqual('John Doe <jdoe at example.com>\n'
+                'Jane Rey <jrey at example.com>', rev.properties['authors'])
+        self.assertFalse('author' in rev.properties)
+
+    def test_author_and_authors_incompatible(self):
+        tree = self.make_branch_and_tree('foo')
+        self.assertRaises(AssertionError, tree.commit, 'commit 1',
+                authors=['John Doe <jdoe at example.com>',
+                         'Jane Rey <jrey at example.com>'],
+                author="Jack Me <jme at example.com>")
+
+    def test_author_with_newline_rejected(self):
+        tree = self.make_branch_and_tree('foo')
+        self.assertRaises(AssertionError, tree.commit, 'commit 1',
+                authors=['John\nDoe <jdoe at example.com>'])
 
     def test_commit_with_checkout_and_branch_sharing_repo(self):
         repo = self.make_repository('repo', shared=True)

=== modified file 'bzrlib/tests/test_log.py'
--- a/bzrlib/tests/test_log.py	2009-02-23 15:42:47 +0000
+++ b/bzrlib/tests/test_log.py	2009-02-27 15:14:34 +0000
@@ -240,7 +240,7 @@
     wt.commit('multiline\nlog\nmessage\n', rev_id='a2',
               timestamp=1132586842.411175966, timezone=-6*3600,
               committer='Joe Foo <joe at foo.com>',
-              author='Joe Bar <joe at bar.com>')
+              authors=['Joe Bar <joe at bar.com>'])
 
     open('c', 'wb').write('just another manic monday\n')
     wt.add('c')
@@ -679,14 +679,15 @@
                   timestamp=1132711707,
                   timezone=36000,
                   committer='Lorem Ipsum <test at example.com>',
-                  author='John Doe <jdoe at example.com>')
+                  authors=['John Doe <jdoe at example.com>',
+                           'Jane Rey <jrey at example.com>'])
         sio = StringIO()
         formatter = log.LongLogFormatter(to_file=sio)
         log.show_log(b, formatter)
         self.assertEqualDiff('''\
 ------------------------------------------------------------
 revno: 1
-author: John Doe <jdoe at example.com>
+author: John Doe <jdoe at example.com>, Jane Rey <jrey at example.com>
 committer: Lorem Ipsum <test at example.com>
 branch nick: test_author_log
 timestamp: Wed 2005-11-23 12:08:27 +1000
@@ -708,7 +709,7 @@
                   timestamp=1132711707,
                   timezone=36000,
                   committer='Lorem Ipsum <test at example.com>',
-                  author='John Doe <jdoe at example.com>')
+                  authors=['John Doe <jdoe at example.com>'])
         sio = StringIO()
         formatter = log.LongLogFormatter(to_file=sio)
         try:
@@ -748,7 +749,7 @@
                   timestamp=1132711707,
                   timezone=36000,
                   committer='Lorem Ipsum <test at example.com>',
-                  author='John Doe <jdoe at example.com>')
+                  authors=['John Doe <jdoe at example.com>'])
         sio = StringIO()
         formatter = log.ShortLogFormatter(to_file=sio)
         try:
@@ -783,7 +784,7 @@
                   timestamp=1132711707,
                   timezone=36000,
                   committer='Lorem Ipsum <test at example.com>',
-                  author='John Doe <jdoe at example.com>',
+                  authors=['John Doe <jdoe at example.com>'],
                   revprops={'first_prop':'first_value'})
         sio = StringIO()
         formatter = log.LongLogFormatter(to_file=sio)
@@ -809,7 +810,7 @@
                   timestamp=1132711707,
                   timezone=36000,
                   committer='Lorem Ipsum <test at example.com>',
-                  author='John Doe <jdoe at example.com>',
+                  authors=['John Doe <jdoe at example.com>'],
                   revprops={'a_prop':'test_value'})
         sio = StringIO()
         formatter = log.LongLogFormatter(to_file=sio)
@@ -959,7 +960,7 @@
                   timestamp=1132711707,
                   timezone=36000,
                   committer='Lorem Ipsum <test at example.com>',
-                  author='John Doe <jdoe at example.com>')
+                  authors=['John Doe <jdoe at example.com>'])
         sio = StringIO()
         formatter = log.LongLogFormatter(to_file=sio, levels=1)
         log.show_log(b, formatter)
@@ -988,7 +989,7 @@
                   timestamp=1132711707,
                   timezone=36000,
                   committer='Lorem Ipsum <test at example.com>',
-                  author='John Doe <jdoe at example.com>')
+                  authors=['John Doe <jdoe at example.com>'])
         sio = StringIO()
         formatter = log.LongLogFormatter(to_file=sio, levels=1)
         try:
@@ -1545,6 +1546,10 @@
         self.assertEqual('jsmith at example.com', lf.short_author(rev))
         rev.properties['author'] = 'John Smith jsmith at example.com'
         self.assertEqual('John Smith', lf.short_author(rev))
+        del rev.properties['author']
+        rev.properties['authors'] = ('John Smith <jsmith at example.com>\n'
+                'Jane Rey <jrey at example.com>')
+        self.assertEqual('John Smith', lf.short_author(rev))
 
 
 class TestReverseByDepth(tests.TestCase):

=== modified file 'bzrlib/tests/test_options.py'
--- a/bzrlib/tests/test_options.py	2008-10-02 06:18:42 +0000
+++ b/bzrlib/tests/test_options.py	2009-02-26 19:21:53 +0000
@@ -42,14 +42,14 @@
         # to cmd_commit, when they are meant to be about option parsing in
         # general.
         self.assertEqual(parse_args(cmd_commit(), ['--help']),
-           ([], {'exclude': [], 'fixes': [], 'help': True}))
+           ([], {'author': [], 'exclude': [], 'fixes': [], 'help': True}))
         self.assertEqual(parse_args(cmd_commit(), ['--message=biter']),
-           ([], {'exclude': [], 'fixes': [], 'message': 'biter'}))
+           ([], {'author': [], 'exclude': [], 'fixes': [], 'message': 'biter'}))
 
     def test_no_more_opts(self):
         """Terminated options"""
         self.assertEqual(parse_args(cmd_commit(), ['--', '-file-with-dashes']),
-                          (['-file-with-dashes'], {'exclude': [], 'fixes': []}))
+                          (['-file-with-dashes'], {'author': [], 'exclude': [], 'fixes': []}))
 
     def test_option_help(self):
         """Options have help strings."""

=== modified file 'bzrlib/tests/test_revision.py'
--- a/bzrlib/tests/test_revision.py	2009-01-17 01:30:58 +0000
+++ b/bzrlib/tests/test_revision.py	2009-02-27 15:14:34 +0000
@@ -208,6 +208,26 @@
     def test_get_apparent_author(self):
         r = revision.Revision('1')
         r.committer = 'A'
-        self.assertEqual('A', r.get_apparent_author())
-        r.properties['author'] = 'B'
-        self.assertEqual('B', r.get_apparent_author())
+        author = self.applyDeprecated(
+                symbol_versioning.deprecated_in((1, 13, 0)),
+                r.get_apparent_author)
+        self.assertEqual('A', author)
+        r.properties['author'] = 'B'
+        author = self.applyDeprecated(
+                symbol_versioning.deprecated_in((1, 13, 0)),
+                r.get_apparent_author)
+        self.assertEqual('B', author)
+        r.properties['authors'] = 'C\nD'
+        author = self.applyDeprecated(
+                symbol_versioning.deprecated_in((1, 13, 0)),
+                r.get_apparent_author)
+        self.assertEqual('C', author)
+
+    def test_get_apparent_authors(self):
+        r = revision.Revision('1')
+        r.committer = 'A'
+        self.assertEqual(['A'], r.get_apparent_authors())
+        r.properties['author'] = 'B'
+        self.assertEqual(['B'], r.get_apparent_authors())
+        r.properties['authors'] = 'C\nD'
+        self.assertEqual(['C', 'D'], r.get_apparent_authors())

=== modified file 'doc/developers/revision-properties.txt'
--- a/doc/developers/revision-properties.txt	2007-08-07 20:45:21 +0000
+++ b/doc/developers/revision-properties.txt	2009-02-27 15:14:34 +0000
@@ -28,9 +28,13 @@
 At the moment, three standardized revision properties are recognized and used
 by bzrlib:
 
- * ``author`` - Author of the change. This value is in the same format as
-   the committer-id. This property can be set by passing keyword argument
-   ``author`` to function ``MutableTree.commit``.
+ * ``authors`` - Authors of the change. This value is a "\n" separated set
+   of values in the same format as the committer-id. This property can be
+   set by passing a list to the keyword argument ``authors`` of the function
+   ``MutableTree.commit``.
+ * ``author`` - Single author of the change. This property is deprecated in
+   favour of ``authors``. It should no longer be set by any code, but will
+   still be read. It is ignored if ``authors`` is set in the same revision.
  * ``branch-nick`` - Nickname of the branch. It's either the directory name
    or manually set by ``bzr nick``. The value is set automatically in
    ``MutableTree.commit``.

=== modified file 'doc/en/user-guide/recording_changes.txt'
--- a/doc/en/user-guide/recording_changes.txt	2007-11-21 07:39:16 +0000
+++ b/doc/en/user-guide/recording_changes.txt	2009-03-02 13:02:13 +0000
@@ -60,3 +60,22 @@
 
     % bzr commit .
 
+Giving credit for a change
+--------------------------
+
+If you didn't actually write the changes that you are about to commit, for instance
+if you are applying a patch from someone else, you can use the ``--author`` commit
+option to give them credit for the change::
+
+    % bzr commit --author "Jane Rey <jrey at example.com>"
+
+The person that you specify there will be recorded as the "author" of the revision,
+and you will be recorded as the "committer" of the revision.
+
+If more than one person works on the changes for a revision, for instance if you
+are pair-programming, then you can record this by specifying ``--author`` multiple
+times::
+
+    % bzr commit --author "Jane Rey <jrey at example.com>" \
+        --author "John Doe <jdoe at example.com>"
+




More information about the bazaar-commits mailing list