Rev 3188: (jameinel) Switch dotted revnos to always be 3 digits long. in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Wed Jan 16 21:12:08 GMT 2008


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

------------------------------------------------------------
revno: 3188
revision-id:pqm at pqm.ubuntu.com-20080116211201-fy431qtj4d3vre5x
parent: pqm at pqm.ubuntu.com-20080116053913-ny7sihxkms5v6d7j
parent: john at arbash-meinel.com-20080115214200-27y5bnbuf3dbtqnm
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2008-01-16 21:12:01 +0000
message:
  (jameinel) Switch dotted revnos to always be 3 digits long.
added:
  doc/en/user-guide/revnos.txt   revnos.txt-20080111231928-pbntxea0ynh9ww1t-1
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/tests/blackbox/test_log.py test_log.py-20060112090212-78f6ea560c868e24
  bzrlib/tests/test_annotate.py  test_annotate.py-20061213215015-sttc9agsxomls7q0-1
  bzrlib/tests/test_log.py       testlog.py-20050728115707-1a514809d7d49309
  bzrlib/tests/test_tsort.py     testtsort.py-20051025073946-27da871c394d5be4
  bzrlib/tsort.py                tsort.py-20051025073946-7808f6aaf7d07208
  doc/en/user-guide/core_concepts.txt core_concepts.txt-20071114035000-q36a9h57ps06uvnl-2
  doc/en/user-guide/index.txt    index.txt-20060622101119-tgwtdci8z769bjb9-2
    ------------------------------------------------------------
    revno: 3170.3.8
    revision-id:john at arbash-meinel.com-20080115214200-27y5bnbuf3dbtqnm
    parent: john at arbash-meinel.com-20080115210031-7hhwowgkmo2monk8
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: dotted_revnos
    timestamp: Tue 2008-01-15 15:42:00 -0600
    message:
      Finish the rest of the review feedback.
    modified:
      bzrlib/tests/test_tsort.py     testtsort.py-20051025073946-27da871c394d5be4
      doc/en/user-guide/revnos.txt   revnos.txt-20080111231928-pbntxea0ynh9ww1t-1
    ------------------------------------------------------------
    revno: 3170.3.7
    revision-id:john at arbash-meinel.com-20080115210031-7hhwowgkmo2monk8
    parent: john at arbash-meinel.com-20080115205515-a2eyt2w1imuxgv1v
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: dotted_revnos
    timestamp: Tue 2008-01-15 15:00:31 -0600
    message:
      update MergeSorter.__init__() docstring
    modified:
      bzrlib/tsort.py                tsort.py-20051025073946-7808f6aaf7d07208
    ------------------------------------------------------------
    revno: 3170.3.6
    revision-id:john at arbash-meinel.com-20080115205515-a2eyt2w1imuxgv1v
    parent: meinel at lululaptop-20080111231953-044hfb9u9gyuffgy
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: dotted_revnos
    timestamp: Tue 2008-01-15 14:55:15 -0600
    message:
      Documentation feedback.
      Link the new doc from the index, and cross reference it from core_concepts.
    modified:
      doc/en/user-guide/core_concepts.txt core_concepts.txt-20071114035000-q36a9h57ps06uvnl-2
      doc/en/user-guide/index.txt    index.txt-20060622101119-tgwtdci8z769bjb9-2
      doc/en/user-guide/revnos.txt   revnos.txt-20080111231928-pbntxea0ynh9ww1t-1
    ------------------------------------------------------------
    revno: 3170.3.5
    revision-id:meinel at lululaptop-20080111231953-044hfb9u9gyuffgy
    parent: meinel at lululaptop-20080110223409-2d2w3l1wkc0f2kap
    committer: John Arbash Meinel <John Arbash Meinel at LULULAPTOP>
    branch nick: dotted_revnos
    timestamp: Fri 2008-01-11 17:19:53 -0600
    message:
      Add a NEWS entry and a revision number document.
    added:
      doc/en/user-guide/revnos.txt   revnos.txt-20080111231928-pbntxea0ynh9ww1t-1
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
    ------------------------------------------------------------
    revno: 3170.3.4
    revision-id:meinel at lululaptop-20080110223409-2d2w3l1wkc0f2kap
    parent: meinel at lululaptop-20080110165447-pwcd7orl015rtsrs
    committer: John Arbash Meinel <John Arbash Meinel at LULULAPTOP>
    branch nick: dotted_revnos
    timestamp: Thu 2008-01-10 16:34:09 -0600
    message:
      Update the tests for the new revision numbering.
    modified:
      bzrlib/tests/blackbox/test_log.py test_log.py-20060112090212-78f6ea560c868e24
      bzrlib/tests/test_annotate.py  test_annotate.py-20061213215015-sttc9agsxomls7q0-1
      bzrlib/tests/test_log.py       testlog.py-20050728115707-1a514809d7d49309
      bzrlib/tests/test_tsort.py     testtsort.py-20051025073946-27da871c394d5be4
    ------------------------------------------------------------
    revno: 3170.3.3
    revision-id:meinel at lululaptop-20080110165447-pwcd7orl015rtsrs
    parent: meinel at lululaptop-20080109211018-oyh76zn4fb21qda1
    parent: pqm at pqm.ubuntu.com-20080110025628-6tl4b9cmdn335suw
    committer: John Arbash Meinel <John Arbash Meinel at LULULAPTOP>
    branch nick: dotted_revnos
    timestamp: Thu 2008-01-10 10:54:47 -0600
    message:
      Merge bzr.dev 3172
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/bzrdir.py               bzrdir.py-20060131065624-156dfea39c4387cb
      bzrlib/debug.py                debug.py-20061102062349-vdhrw9qdpck8cl35-1
      bzrlib/fetch.py                fetch.py-20050818234941-26fea6105696365d
      bzrlib/help_topics/__init__.py help_topics.py-20060920210027-rnim90q9e0bwxvy4-1
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
      bzrlib/tests/interrepository_implementations/test_interrepository.py test_interrepository.py-20060220061411-1ec13fa99e5e3eee
      bzrlib/tests/repository_implementations/test_repository.py test_repository.py-20060131092128-ad07f494f5c9d26c
      bzrlib/tests/test_osutils.py   test_osutils.py-20051201224856-e48ee24c12182989
      bzrlib/tests/test_repository.py test_repository.py-20060131075918-65c555b881612f4d
      bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
      bzrlib/xml_serializer.py       xml.py-20050309040759-57d51586fdec365d
    ------------------------------------------------------------
    revno: 3170.3.2
    revision-id:meinel at lululaptop-20080109211018-oyh76zn4fb21qda1
    parent: meinel at lululaptop-20080109194739-q5nyw06zus444na3
    committer: John Arbash Meinel <John Arbash Meinel at LULULAPTOP>
    branch nick: dotted_revnos
    timestamp: Wed 2008-01-09 15:10:18 -0600
    message:
      Implement the new dotted revision numbering.
    modified:
      bzrlib/tests/test_tsort.py     testtsort.py-20051025073946-27da871c394d5be4
      bzrlib/tsort.py                tsort.py-20051025073946-7808f6aaf7d07208
    ------------------------------------------------------------
    revno: 3170.3.1
    revision-id:meinel at lululaptop-20080109194739-q5nyw06zus444na3
    parent: pqm at pqm.ubuntu.com-20080107174938-hvwo399dzshh3cod
    committer: John Arbash Meinel <John Arbash Meinel at LULULAPTOP>
    branch nick: dotted_revnos
    timestamp: Wed 2008-01-09 13:47:39 -0600
    message:
      Write up a simple test that is getting ready to fix up deeply nested dotted revnos.
    modified:
      bzrlib/tests/test_tsort.py     testtsort.py-20051025073946-27da871c394d5be4
=== added file 'doc/en/user-guide/revnos.txt'
--- a/doc/en/user-guide/revnos.txt	1970-01-01 00:00:00 +0000
+++ b/doc/en/user-guide/revnos.txt	2008-01-15 21:42:00 +0000
@@ -0,0 +1,33 @@
+Understanding Revision Numbers
+==============================
+
+All revisions in the mainline of a branch will have a simple increasing
+integer. (First commit gets 1, 10th commit gets 10, etc.) This makes them
+fairly natural to use when you want to say "grab the 10th revision from my
+branch", or "fixed in revision 3050".
+
+For revisions which have been merged into a branch, a dotted notation is
+used (eg, 3112.1.5). Dotted revision numbers have three numbers. The first
+number indicates what mainline revision change is derived from. The second
+number is the branch counter. There can be many branches derived from the
+same revision, so they all get a unique number. The third number is the
+number of revisions since the branch started. For example, 3112.1.5 is the
+first branch from revision 3112, the fifth revision on that branch.
+
+Revisions are numbered in a stable way, such that if two branches have the
+same revision in their mainline, all revisions in the ancestry of that
+revision will have the same revision numbers. (So if Alice and Bob's
+branches agree on revision 10, they will agree on all revisions before
+that.) Future merges will not change revision numbers. However doing
+``bzr pull`` can change revision numbers, because it changes the
+mainline revisions.
+
+
+bzr versions < 1.2
+------------------
+Versions prior to bzr 1.2 used a slightly different algorithm. Some nested
+branches would get extra numbers (such as 1.1.1.1.1) rather than the
+simpler 3-number system.
+
+..
+   vim: ft=rst tw=74 ai

=== modified file 'NEWS'
--- a/NEWS	2008-01-16 04:10:03 +0000
+++ b/NEWS	2008-01-16 21:12:01 +0000
@@ -81,6 +81,11 @@
 
   CHANGES:
    
+   * Dotted revision numbers have been revised. Instead of growing longer with
+     nested branches the branch number just increases. (eg instead of 1.1.1.1.1
+     we now report 1.2.1.) This helps scale long lived branches which have many
+     feature branches merged between them. (John Arbash Meinel)
+
    * The syntax ``bzr diff branch1 branch2`` is no longer supported.
      Use ``bzr diff branch1 --new branch2`` instead. This change has
      been made to remove the ambiguity where ``branch2`` is in fact a

=== modified file 'bzrlib/tests/blackbox/test_log.py'
--- a/bzrlib/tests/blackbox/test_log.py	2007-12-27 08:25:04 +0000
+++ b/bzrlib/tests/blackbox/test_log.py	2008-01-10 22:34:09 +0000
@@ -222,7 +222,7 @@
     message:
       merge branch 2
         ------------------------------------------------------------
-        revno: 1.1.1.1.1
+        revno: 1.2.1
         committer: Lorem Ipsum <test at example.com>
         branch nick: smallerchild
         timestamp: Just now
@@ -258,7 +258,7 @@
 message:
   merge branch 2
     ------------------------------------------------------------
-    revno: 1.1.1.1.1
+    revno: 1.2.1
     committer: Lorem Ipsum <test at example.com>
     branch nick: smallerchild
     timestamp: Just now
@@ -280,7 +280,7 @@
 message:
   merge branch 2
     ------------------------------------------------------------
-    revno: 1.1.1.1.1
+    revno: 1.2.1
     committer: Lorem Ipsum <test at example.com>
     branch nick: smallerchild
     timestamp: Just now

=== modified file 'bzrlib/tests/test_annotate.py'
--- a/bzrlib/tests/test_annotate.py	2007-11-21 21:57:11 +0000
+++ b/bzrlib/tests/test_annotate.py	2008-01-10 22:34:09 +0000
@@ -162,11 +162,11 @@
          |      |          |
          +------+          |
          |      |          |
-        rev-3  rev-1_1_2  rev-1_1_1_1_1 --+
+        rev-3  rev-1_1_2  rev-1_2_1 ------+
          |      |          |              |
          +------+          |              |
          |                 |              |
-        rev-4             rev-1_1_1_1_2  rev-1_1_1_1_1_1_1
+        rev-4             rev-1_2_2  rev-1_3_1
          |                 |              |
          +-----------------+              |
          |                                |
@@ -186,13 +186,13 @@
         tree1.commit('noop merge', rev_id='rev-4')
 
         self.build_tree_contents([('tree3/a', 'first\nthird\nfourth\n')])
-        tree3.commit('four', rev_id='rev-1_1_1_1_1',
+        tree3.commit('four', rev_id='rev-1_2_1',
                      committer='jerry at foo.com',
                      timestamp=1166046003.00, timezone=0)
 
         tree4 = tree3.bzrdir.clone('tree4').open_workingtree()
 
-        tree3.commit('noop', rev_id='rev-1_1_1_1_2',
+        tree3.commit('noop', rev_id='rev-1_2_2',
                      committer='jerry at foo.com',
                      timestamp=1166046004.00, timezone=0)
         self.assertEqual(0, tree1.merge_from_branch(tree3.branch))
@@ -200,7 +200,7 @@
 
         self.build_tree_contents([('tree4/a',
                                    'first\nthird\nfourth\nfifth\nsixth\n')])
-        tree4.commit('five and six', rev_id='rev-1_1_1_1_1_1_1',
+        tree4.commit('five and six', rev_id='rev-1_3_1',
                      committer='george at foo.com',
                      timestamp=1166046005.00, timezone=0)
         self.assertEqual(0, tree1.merge_from_branch(tree4.branch))
@@ -226,46 +226,46 @@
         sio = StringIO()
         annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
                                to_file=sio, verbose=False, full=False)
-        self.assertEqualDiff('1            joe at foo | first\n'
-                             '2            joe at foo | second\n'
-                             '1.1.1        barry at f | third\n'
-                             '1.1.1.1.1    jerry at f | fourth\n'
-                             '1.1.1.1.1.1> george@ | fifth\n'
-                             '                     | sixth\n',
+        self.assertEqualDiff('1     joe at foo | first\n'
+                             '2     joe at foo | second\n'
+                             '1.1.1 barry at f | third\n'
+                             '1.2.1 jerry at f | fourth\n'
+                             '1.3.1 george@ | fifth\n'
+                             '              | sixth\n',
                              sio.getvalue())
 
         sio = StringIO()
         annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
                                to_file=sio, verbose=False, full=True)
-        self.assertEqualDiff('1            joe at foo | first\n'
-                             '2            joe at foo | second\n'
-                             '1.1.1        barry at f | third\n'
-                             '1.1.1.1.1    jerry at f | fourth\n'
-                             '1.1.1.1.1.1> george@ | fifth\n'
-                             '1.1.1.1.1.1> george@ | sixth\n',
+        self.assertEqualDiff('1     joe at foo | first\n'
+                             '2     joe at foo | second\n'
+                             '1.1.1 barry at f | third\n'
+                             '1.2.1 jerry at f | fourth\n'
+                             '1.3.1 george@ | fifth\n'
+                             '1.3.1 george@ | sixth\n',
                              sio.getvalue())
 
         # verbose=True shows everything, the full revno, user id, and date
         sio = StringIO()
         annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
                                to_file=sio, verbose=True, full=False)
-        self.assertEqualDiff('1             joe at foo.com    20061213 | first\n'
-                             '2             joe at foo.com    20061213 | second\n'
-                             '1.1.1         barry at foo.com  20061213 | third\n'
-                             '1.1.1.1.1     jerry at foo.com  20061213 | fourth\n'
-                             '1.1.1.1.1.1.1 george at foo.com 20061213 | fifth\n'
-                             '                                      | sixth\n',
+        self.assertEqualDiff('1     joe at foo.com    20061213 | first\n'
+                             '2     joe at foo.com    20061213 | second\n'
+                             '1.1.1 barry at foo.com  20061213 | third\n'
+                             '1.2.1 jerry at foo.com  20061213 | fourth\n'
+                             '1.3.1 george at foo.com 20061213 | fifth\n'
+                             '                              | sixth\n',
                              sio.getvalue())
 
         sio = StringIO()
         annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
                                to_file=sio, verbose=True, full=True)
-        self.assertEqualDiff('1             joe at foo.com    20061213 | first\n'
-                             '2             joe at foo.com    20061213 | second\n'
-                             '1.1.1         barry at foo.com  20061213 | third\n'
-                             '1.1.1.1.1     jerry at foo.com  20061213 | fourth\n'
-                             '1.1.1.1.1.1.1 george at foo.com 20061213 | fifth\n'
-                             '1.1.1.1.1.1.1 george at foo.com 20061213 | sixth\n',
+        self.assertEqualDiff('1     joe at foo.com    20061213 | first\n'
+                             '2     joe at foo.com    20061213 | second\n'
+                             '1.1.1 barry at foo.com  20061213 | third\n'
+                             '1.2.1 jerry at foo.com  20061213 | fourth\n'
+                             '1.3.1 george at foo.com 20061213 | fifth\n'
+                             '1.3.1 george at foo.com 20061213 | sixth\n',
                              sio.getvalue())
 
     def test_annotate_uses_branch_context(self):
@@ -277,13 +277,13 @@
         tree1 = self.create_deeply_merged_trees()
 
         sio = StringIO()
-        annotate.annotate_file(tree1.branch, 'rev-1_1_1_1_1_1_1', 'a-id',
+        annotate.annotate_file(tree1.branch, 'rev-1_3_1', 'a-id',
                                to_file=sio, verbose=False, full=False)
-        self.assertEqualDiff('1            joe at foo | first\n'
-                             '1.1.1        barry at f | third\n'
-                             '1.1.1.1.1    jerry at f | fourth\n'
-                             '1.1.1.1.1.1> george@ | fifth\n'
-                             '                     | sixth\n',
+        self.assertEqualDiff('1     joe at foo | first\n'
+                             '1.1.1 barry at f | third\n'
+                             '1.2.1 jerry at f | fourth\n'
+                             '1.3.1 george@ | fifth\n'
+                             '              | sixth\n',
                              sio.getvalue())
 
     def test_annotate_show_ids(self):
@@ -294,24 +294,24 @@
                                to_file=sio, show_ids=True, full=False)
 
         # It looks better with real revision ids :)
-        self.assertEqualDiff('            rev-1 | first\n'
-                             '            rev-2 | second\n'
-                             '        rev-1_1_1 | third\n'
-                             '    rev-1_1_1_1_1 | fourth\n'
-                             'rev-1_1_1_1_1_1_1 | fifth\n'
-                             '                  | sixth\n',
+        self.assertEqualDiff('    rev-1 | first\n'
+                             '    rev-2 | second\n'
+                             'rev-1_1_1 | third\n'
+                             'rev-1_2_1 | fourth\n'
+                             'rev-1_3_1 | fifth\n'
+                             '          | sixth\n',
                              sio.getvalue())
 
         sio = StringIO()
         annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
                                to_file=sio, show_ids=True, full=True)
 
-        self.assertEqualDiff('            rev-1 | first\n'
-                             '            rev-2 | second\n'
-                             '        rev-1_1_1 | third\n'
-                             '    rev-1_1_1_1_1 | fourth\n'
-                             'rev-1_1_1_1_1_1_1 | fifth\n'
-                             'rev-1_1_1_1_1_1_1 | sixth\n',
+        self.assertEqualDiff('    rev-1 | first\n'
+                             '    rev-2 | second\n'
+                             'rev-1_1_1 | third\n'
+                             'rev-1_2_1 | fourth\n'
+                             'rev-1_3_1 | fifth\n'
+                             'rev-1_3_1 | sixth\n',
                              sio.getvalue())
 
     def test_annotate_unicode_author(self):

=== modified file 'bzrlib/tests/test_log.py'
--- a/bzrlib/tests/test_log.py	2007-12-05 18:47:51 +0000
+++ b/bzrlib/tests/test_log.py	2008-01-10 22:34:09 +0000
@@ -425,7 +425,7 @@
     message:
       merge branch 2
         ------------------------------------------------------------
-        revno: 1.1.1.1.1
+        revno: 1.2.1
         committer: Lorem Ipsum <test at example.com>
         branch nick: smallerchild
         timestamp: Just now
@@ -674,10 +674,10 @@
         full_rev_nos_for_reference = {
             '1': '1',
             '2': '2',
-            '3a': '2.2.1', #first commit tree 3
-            '3b': '2.1.1', # first commit tree 2
+            '3a': '2.1.1', #first commit tree 3
+            '3b': '2.2.1', # first commit tree 2
             '3c': '3', #merges 3b to main
-            '4a': '2.1.2', # second commit tree 2
+            '4a': '2.2.2', # second commit tree 2
             '4b': '4', # merges 4a to main
             }
         return mainline_revs, rev_nos, wt
@@ -738,8 +738,8 @@
         revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
                                             'forward'))
         expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
-            ('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
-            ('4a', '2.1.2', 1)]
+            ('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
+            ('4a', '2.2.2', 1)]
         self.assertEqual(expected, revisions)
         revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
                                              'forward', include_merges=False))

=== modified file 'bzrlib/tests/test_tsort.py'
--- a/bzrlib/tests/test_tsort.py	2007-06-21 01:58:29 +0000
+++ b/bzrlib/tests/test_tsort.py	2008-01-15 21:42:00 +0000
@@ -181,7 +181,55 @@
              ],
             True
             )
-        
+
+    def test_merge_sort_race(self):
+        # A
+        # |
+        # B-.
+        # |\ \
+        # | | C
+        # | |/
+        # | D
+        # |/
+        # F
+        graph = {'A': [],
+                 'B': ['A'],
+                 'C': ['B'],
+                 'D': ['B', 'C'],
+                 'F': ['B', 'D'],
+                 }
+        self.assertSortAndIterate(graph, 'F',
+            [(0, 'F', 0, (3,), False),
+             (1, 'D', 1, (2,2,1), False),
+             (2, 'C', 1, (2,1,1), True), # XXX: Shouldn't it be merge_depth=2?
+             (3, 'B', 0, (2,), False),
+             (4, 'A', 0, (1,), True),
+             ], True)
+        # A
+        # |
+        # B-.
+        # |\ \
+        # | X C
+        # | |/
+        # | D
+        # |/
+        # F
+        graph = {'A': [],
+                 'B': ['A'],
+                 'C': ['B'],
+                 'X': ['B'],
+                 'D': ['X', 'C'],
+                 'F': ['B', 'D'],
+                 }
+        self.assertSortAndIterate(graph, 'F',
+            [(0, 'F', 0, (3,), False),
+             (1, 'D', 1, (2,1,2), False),
+             (2, 'C', 2, (2,2,1), True),
+             (3, 'X', 1, (2,1,1), True),
+             (4, 'B', 0, (2,), False),
+             (5, 'A', 0, (1,), True),
+             ], True)
+
     def test_merge_depth_with_nested_merges(self):
         # the merge depth marker should reflect the depth of the revision
         # in terms of merges out from the mainline
@@ -228,17 +276,96 @@
              }.items(),
             'A',
             [(0, 'A', 0, (3,),  False),
-             (1, 'B', 1, (1,2,2), False),
-             (2, 'C', 1, (1,2,1), True),
+             (1, 'B', 1, (1,3,2), False),
+             (2, 'C', 1, (1,3,1), True),
              (3, 'D', 0, (2,), False),
              (4, 'E', 1, (1,1,2), False),
-             (5, 'F', 2, (1,1,1,1,1), True),
+             (5, 'F', 2, (1,2,1), True),
              (6, 'G', 1, (1,1,1), True),
              (7, 'H', 0, (1,), True),
              ],
             True
             )
- 
+
+    def test_dotted_revnos_with_simple_merges(self):
+        # A         1
+        # |\
+        # B C       2, 1.1.1
+        # | |\
+        # D E F     3, 1.1.2, 1.2.1
+        # |/ /|
+        # G H I     4, 1.2.2, 1.3.1
+        # |/ /
+        # J K       5, 1.3.2
+        # |/
+        # L         6
+        self.assertSortAndIterate(
+            {'A': [],
+             'B': ['A'],
+             'C': ['A'],
+             'D': ['B'],
+             'E': ['C'],
+             'F': ['C'],
+             'G': ['D', 'E'],
+             'H': ['F'],
+             'I': ['F'],
+             'J': ['G', 'H'],
+             'K': ['I'],
+             'L': ['J', 'K'],
+            }.items(),
+            'L',
+            [(0, 'L', 0, (6,), False),
+             (1, 'K', 1, (1,3,2), False),
+             (2, 'I', 1, (1,3,1), True),
+             (3, 'J', 0, (5,), False),
+             (4, 'H', 1, (1,2,2), False),
+             (5, 'F', 1, (1,2,1), True),
+             (6, 'G', 0, (4,), False),
+             (7, 'E', 1, (1,1,2), False),
+             (8, 'C', 1, (1,1,1), True),
+             (9, 'D', 0, (3,), False),
+             (10, 'B', 0, (2,), False),
+             (11, 'A', 0, (1,),  True),
+             ],
+            True
+            )
+        # Adding a shortcut from the first revision should not change any of
+        # the existing numbers
+        self.assertSortAndIterate(
+            {'A': [],
+             'B': ['A'],
+             'C': ['A'],
+             'D': ['B'],
+             'E': ['C'],
+             'F': ['C'],
+             'G': ['D', 'E'],
+             'H': ['F'],
+             'I': ['F'],
+             'J': ['G', 'H'],
+             'K': ['I'],
+             'L': ['J', 'K'],
+             'M': ['A'],
+             'N': ['L', 'M'],
+            }.items(),
+            'N',
+            [(0, 'N', 0, (7,), False),
+             (1, 'M', 1, (1,4,1), True),
+             (2, 'L', 0, (6,), False),
+             (3, 'K', 1, (1,3,2), False),
+             (4, 'I', 1, (1,3,1), True),
+             (5, 'J', 0, (5,), False),
+             (6, 'H', 1, (1,2,2), False),
+             (7, 'F', 1, (1,2,1), True),
+             (8, 'G', 0, (4,), False),
+             (9, 'E', 1, (1,1,2), False),
+             (10, 'C', 1, (1,1,1), True),
+             (11, 'D', 0, (3,), False),
+             (12, 'B', 0, (2,), False),
+             (13, 'A', 0, (1,),  True),
+             ],
+            True
+            )
+
     def test_end_of_merge_not_last_revision_in_branch(self):
         # within a branch only the last revision gets an
         # end of merge marker.
@@ -311,11 +438,11 @@
              },
             'A',
             [(0, 'A', 0, (2,), False),
-             (1, 'B', 1, (1,2,2), False),
-             (2, 'C', 2, (1,2,1,1,1), True),
-             (3, 'D', 1, (1,2,1), True),
+             (1, 'B', 1, (1,3,2), False),
+             (2, 'C', 2, (1,4,1), True),
+             (3, 'D', 1, (1,3,1), True),
              (4, 'E', 1, (1,1,2), False),
-             (5, 'F', 2, (1,1,1,1,1), True),
+             (5, 'F', 2, (1,2,1), True),
              (6, 'G', 1, (1,1,1), True),
              (7, 'H', 0, (1,), True),
              ],

=== modified file 'bzrlib/tsort.py'
--- a/bzrlib/tsort.py	2007-06-21 01:58:29 +0000
+++ b/bzrlib/tsort.py	2008-01-15 21:00:31 +0000
@@ -192,7 +192,7 @@
     __slots__ = ['_node_name_stack',
                  '_node_merge_depth_stack',
                  '_pending_parents_stack',
-                 '_assigned_sequence_stack',
+                 '_first_child_stack',
                  '_left_subtree_pushed_stack',
                  '_generate_revno',
                  '_graph',
@@ -200,7 +200,7 @@
                  '_stop_revision',
                  '_original_graph',
                  '_revnos',
-                 '_root_sequence',
+                 '_revno_to_branch_count',
                  '_completed_node_names',
                  '_scheduled_nodes',
                 ]
@@ -239,7 +239,7 @@
            found.
          * revno_sequence: When requested this field provides a sequence of
              revision numbers for all revisions. The format is:
-             REVNO[[.BRANCHREVNO.REVNO] ...]. BRANCHREVNO is the number of the
+             (REVNO, BRANCHNUM, BRANCHREVNO). BRANCHNUM is the number of the
              branch that the revno is on. From left to right the REVNO numbers
              are the sequence numbers within that branch of the revision.
              For instance, the graph {A:[], B:['A'], C:['A', 'B']} will get
@@ -362,15 +362,15 @@
         self._original_graph = dict(self._graph.items())
         # we need to know the revision numbers of revisions to determine
         # the revision numbers of their descendants
-        # this is a graph from node to [revno_tuple, sequence_number]
-        # where sequence is the number of branches made from the node,
+        # this is a graph from node to [revno_tuple, first_child]
+        # where first_child is True if no other children have seen this node
         # and revno_tuple is the tuple that was assigned to the node.
         # we dont know revnos to start with, so we start it seeded with
-        # [None, 0]
-        self._revnos = dict((revision, [None, 0]) for revision in self._graph)
-        # the global implicit root node has revno 0, but we need to know
-        # the sequence number for it too:
-        self._root_sequence = 0
+        # [None, True]
+        self._revnos = dict((revision, [None, True])
+                            for revision in self._graph)
+        # Each mainline revision counts how many child branches have spawned from it.
+        self._revno_to_branch_count = {}
         
         # this is a stack storing the depth first search into the graph.
         self._node_name_stack = []
@@ -382,7 +382,7 @@
         self._pending_parents_stack = []
         # When we first look at a node we assign it a seqence number from its
         # leftmost parent.
-        self._assigned_sequence_stack = []
+        self._first_child_stack = []
         # this is a set of the nodes who have been completely analysed for fast
         # membership checking
         self._completed_node_names = set()
@@ -439,8 +439,7 @@
                       node_merge_depth_stack_append=node_merge_depth_stack.append,
                       left_subtree_pushed_stack_append=left_subtree_pushed_stack.append,
                       pending_parents_stack_append=pending_parents_stack.append,
-                      assigned_sequence_stack_append=self._assigned_sequence_stack.append,
-                      original_graph=self._original_graph,
+                      first_child_stack_append=self._first_child_stack.append,
                       revnos=self._revnos,
                       ):
             """Add node_name to the pending node stack.
@@ -455,28 +454,28 @@
             node_merge_depth_stack_append(merge_depth)
             left_subtree_pushed_stack_append(False)
             pending_parents_stack_append(list(parents))
-            # as we push it, assign it a sequence number against its parent:
-            parents = original_graph[node_name]
+            # as we push it, check if it is the first child
             if parents:
                 # node has parents, assign from the left most parent.
-                parent_revno = revnos[parents[0]]
-                sequence = parent_revno[1]
-                parent_revno[1] += 1
+                parent_info = revnos[parents[0]]
+                first_child = parent_info[1]
+                parent_info[1] = False
             else:
-                # no parents, use the root sequence
-                sequence = self._root_sequence
-                self._root_sequence +=1
-            assigned_sequence_stack_append(sequence)
+                # We don't use the same algorithm here, but we need to keep the
+                # stack in line
+                first_child = None
+            first_child_stack_append(first_child)
 
         def pop_node(node_name_stack_pop=node_name_stack.pop,
                      node_merge_depth_stack_pop=node_merge_depth_stack.pop,
-                     assigned_sequence_stack_pop=self._assigned_sequence_stack.pop,
+                     first_child_stack_pop=self._first_child_stack.pop,
                      left_subtree_pushed_stack_pop=left_subtree_pushed_stack.pop,
                      pending_parents_stack_pop=pending_parents_stack.pop,
                      original_graph=self._original_graph,
                      revnos=self._revnos,
                      completed_node_names_add=self._completed_node_names.add,
                      scheduled_nodes_append=scheduled_nodes.append,
+                     revno_to_branch_count=self._revno_to_branch_count,
                     ):
             """Pop the top node off the stack
 
@@ -486,7 +485,7 @@
             # pop off the local variables
             node_name = node_name_stack_pop()
             merge_depth = node_merge_depth_stack_pop()
-            sequence = assigned_sequence_stack_pop()
+            first_child = first_child_stack_pop()
             # remove this node from the pending lists:
             left_subtree_pushed_stack_pop()
             pending_parents_stack_pop()
@@ -494,20 +493,28 @@
             parents = original_graph[node_name]
             if parents:
                 # node has parents, assign from the left most parent.
-                parent_revno = revnos[parents[0]]
-                if sequence:
+                parent_revno = revnos[parents[0]][0]
+                if not first_child:
                     # not the first child, make a new branch
-                    revno = parent_revno[0] + (sequence, 1)
+                    base_revno = parent_revno[0]
+                    branch_count = revno_to_branch_count.get(base_revno, 0)
+                    branch_count += 1
+                    revno_to_branch_count[base_revno] = branch_count
+                    revno = (parent_revno[0], branch_count, 1)
+                    # revno = (parent_revno[0], branch_count, parent_revno[-1]+1)
                 else:
-                    # increment the sequence number within the branch
-                    revno = parent_revno[0][:-1] + (parent_revno[0][-1] + 1,)
+                    # as the first child, we just increase the final revision
+                    # number
+                    revno = parent_revno[:-1] + (parent_revno[-1] + 1,)
             else:
                 # no parents, use the root sequence
-                if sequence:
-                    # make a parallel import revision number
-                    revno = (0, sequence, 1)
+                root_count = revno_to_branch_count.get(0, 0)
+                if root_count:
+                    revno = (0, root_count, 1)
                 else:
                     revno = (1,)
+                root_count += 1
+                revno_to_branch_count[0] = root_count
 
             # store the revno for this node for future reference
             revnos[node_name][0] = revno
@@ -610,18 +617,18 @@
         self._node_merge_depth_stack.append(merge_depth)
         self._left_subtree_pushed_stack.append(False)
         self._pending_parents_stack.append(list(parents))
-        # as we push it, assign it a sequence number against its parent:
+        # as we push it, figure out if this is the first child
         parents = self._original_graph[node_name]
         if parents:
             # node has parents, assign from the left most parent.
-            parent_revno = self._revnos[parents[0]]
-            sequence = parent_revno[1]
-            parent_revno[1] += 1
+            parent_info = self._revnos[parents[0]]
+            first_child = parent_info[1]
+            parent_info[1] = False
         else:
-            # no parents, use the root sequence
-            sequence = self._root_sequence
-            self._root_sequence +=1
-        self._assigned_sequence_stack.append(sequence)
+            # We don't use the same algorithm here, but we need to keep the
+            # stack in line
+            first_child = None
+        self._first_child_stack.append(first_child)
 
     def _pop_node(self):
         """Pop the top node off the stack 
@@ -632,7 +639,7 @@
         # pop off the local variables
         node_name = self._node_name_stack.pop()
         merge_depth = self._node_merge_depth_stack.pop()
-        sequence = self._assigned_sequence_stack.pop()
+        first_child = self._first_child_stack.pop()
         # remove this node from the pending lists:
         self._left_subtree_pushed_stack.pop()
         self._pending_parents_stack.pop()
@@ -640,20 +647,28 @@
         parents = self._original_graph[node_name]
         if parents:
             # node has parents, assign from the left most parent.
-            parent_revno = self._revnos[parents[0]]
-            if sequence:
+            parent_revno = self._revnos[parents[0]][0]
+            if not first_child:
                 # not the first child, make a new branch
-                revno = parent_revno[0] + (sequence, 1)
+                base_revno = parent_revno[0]
+                branch_count = self._revno_to_branch_count.get(base_revno, 0)
+                branch_count += 1
+                self._revno_to_branch_count[base_revno] = branch_count
+                revno = (parent_revno[0], branch_count, 1)
+                # revno = (parent_revno[0], branch_count, parent_revno[-1]+1)
             else:
-                # increment the sequence number within the branch
-                revno = parent_revno[0][:-1] + (parent_revno[0][-1] + 1,)
+                # as the first child, we just increase the final revision
+                # number
+                revno = parent_revno[:-1] + (parent_revno[-1] + 1,)
         else:
             # no parents, use the root sequence
-            if sequence:
-                # make a parallel import revision number
-                revno = (0, sequence, 1)
+            root_count = self._revno_to_branch_count.get(0, 0)
+            if root_count:
+                revno = (0, root_count, 1)
             else:
                 revno = (1,)
+            root_count += 1
+            self._revno_to_branch_count[0] = root_count
 
         # store the revno for this node for future reference
         self._revnos[node_name][0] = revno

=== modified file 'doc/en/user-guide/core_concepts.txt'
--- a/doc/en/user-guide/core_concepts.txt	2007-12-14 07:35:49 +0000
+++ b/doc/en/user-guide/core_concepts.txt	2008-01-15 20:55:15 +0000
@@ -40,7 +40,8 @@
 interface for humans. Typical revision numbers are 1, 42 and 2977.1.59.
 See `Specifying revisions`_ in the appendices for a closer look at
 the numerous ways that revisions and ranges of revisions can be specified
-in Bazaar.
+in Bazaar, and `Understanding Revision Numbers` for a more details about
+numbering.
 
 .. *TODO: add diagram*
 

=== modified file 'doc/en/user-guide/index.txt'
--- a/doc/en/user-guide/index.txt	2007-12-17 01:45:32 +0000
+++ b/doc/en/user-guide/index.txt	2008-01-15 20:55:15 +0000
@@ -27,6 +27,7 @@
 
 .. include:: installing_bazaar.txt
 .. include:: entering_commands.txt
+.. include:: revnos.txt
 .. include:: getting_help.txt
 .. include:: configuring_bazaar.txt
 .. include:: using_aliases.txt




More information about the bazaar-commits mailing list