Explorar o código

Make the commit walker use a loop rather than recursion.

With more than a couple of commits the recursion method of commit walking
goes bad. Switch to a loop method to counteract this. Also build the list
in reverse and switch it round at the end. This should be a win, as most
parents are older than their children, and as we deal with the parents after
doing it in reverse means we need to only go a few steps down the list to find
their place. Doing it the other way then more and more steps would be taken.
Hopefully this is a win in all cases, as the reverse might be expensive for
short paths. However they are short, and for the long path case it is going
to be very expensive to keep the list in the correct order.

There may be more scope for optimisation if the place where the child was
inserted is remembered for each parent, and the search starts near there.
However its not guaranteed to be a win in every case, so keep it simple.

The list algorithm chosen actually meant that the merge used in the test
was ordered correctly based on parent ordering, so it wasn't rigorous. The
ooo_merge repo has a merge where the parents were committed in the opposite
order.
James Westby %!s(int64=19) %!d(string=hai) anos
pai
achega
2daa8522c5

+ 20 - 20
git/repository.py

@@ -97,25 +97,25 @@ class Repository(object):
 
     XXX: work out how to handle merges.
     """
-    commit = self.get_commit(head)
-    if commit is None:
-      raise MissingCommitError(head)
-    history = [commit]
-    parents = commit.parents()
-    parent_commits = [[]] * len(parents)
-    i = 0
-    for parent in parents:
-      parent_commits[i] = self.revision_history(parent)
-      i += 1
-    for commit_list in parent_commits:
-      for parent_commit in commit_list:
-        if parent_commit in history:
-          continue
-        j = 0
-        for main_commit in history:
-          if main_commit.commit_time() < parent_commit.commit_time():
-            break
-          j += 1
-        history.insert(j, parent_commit)
+    # We build the list backwards, as parents are more likely to be older
+    # than children
+    pending_commits = [head]
+    history = []
+    while pending_commits != []:
+      head = pending_commits.pop(0)
+      commit = self.get_commit(head)
+      if commit is None:
+        raise MissingCommitError(head)
+      if commit in history:
+        continue
+      i = 0
+      for known_commit in history:
+        if known_commit.commit_time() > commit.commit_time():
+          break
+        i += 1
+      history.insert(i, commit)
+      parents = commit.parents()
+      pending_commits += parents
+    history.reverse()
     return history
 

+ 1 - 0
git/tests/data/repos/ooo_merge/a

@@ -0,0 +1 @@
+test 1

+ 1 - 0
git/tests/data/repos/ooo_merge/b

@@ -0,0 +1 @@
+test 2

+ 1 - 0
git/tests/data/repos/ooo_merge/c

@@ -0,0 +1 @@
+test 3

+ 10 - 0
git/tests/test_repository.py

@@ -111,3 +111,13 @@ class RepositoryTests(unittest.TestCase):
     self.assertRaises(errors.MissingCommitError, r.revision_history,
                       missing_sha)
 
+  def test_out_of_order_merge(self):
+    """Test that revision history is ordered by date, not parent order."""
+    r = self.open_repo('ooo_merge')
+    history = r.revision_history(r.head())
+    shas = [c.sha().hexdigest() for c in history]
+    self.assertEqual(shas, ['7601d7f6231db6a57f7bbb79ee52e4d462fd44d1',
+                            'f507291b64138b875c28e03469025b1ea20bc614',
+                            'fb5b0425c7ce46959bec94d54b9a157645e114f5',
+                            'f9e39b120c68182a4ba35349f832d0e4e61f485c'])
+