|
@@ -8,7 +8,6 @@ Implementation of merge-base following the approach of git
|
|
|
#
|
|
|
# Available under the MIT License
|
|
|
|
|
|
-import sys
|
|
|
from collections import deque
|
|
|
|
|
|
|
|
@@ -68,17 +67,17 @@ def _find_lcas(lookup_parents, c1, c2s):
|
|
|
return results
|
|
|
|
|
|
|
|
|
-def find_merge_base(r, commit_ids):
|
|
|
- """ find lowest common ancestors of commit_ids[0] and *any* of commits_ids[1:]
|
|
|
- ARGS:
|
|
|
- r: Repo object
|
|
|
- commit_ids: list of commit ids
|
|
|
- Returns
|
|
|
- list of lowest common ancestor commit_ids
|
|
|
- """
|
|
|
+def find_merge_base(object_store, commit_ids):
|
|
|
+ """Find lowest common ancestors of commit_ids[0] and *any* of commits_ids[1:]
|
|
|
|
|
|
+ Args:
|
|
|
+ object_store: object store
|
|
|
+ commit_ids: list of commit ids
|
|
|
+ Returns:
|
|
|
+ list of lowest common ancestor commit_ids
|
|
|
+ """
|
|
|
def lookup_parents(commit_id):
|
|
|
- return r.object_store[commit_id].parents
|
|
|
+ return object_store[commit_id].parents
|
|
|
|
|
|
if not commit_ids:
|
|
|
return []
|
|
@@ -91,22 +90,23 @@ def find_merge_base(r, commit_ids):
|
|
|
return _find_lcas(lookup_parents, c1, c2s)
|
|
|
|
|
|
|
|
|
-def find_octopus_base(r, commit_ids):
|
|
|
- """ find lowest common ancestors of *all* provided commit_ids
|
|
|
- ARGS:
|
|
|
- r: Repo object
|
|
|
- commit_ids: list of commit ids
|
|
|
- Returns
|
|
|
- list of lowest common ancestor commit_ids
|
|
|
+def find_octopus_base(object_store, commit_ids):
|
|
|
+ """Find lowest common ancestors of *all* provided commit_ids
|
|
|
+
|
|
|
+ Args:
|
|
|
+ object_store: Object store
|
|
|
+ commit_ids: list of commit ids
|
|
|
+ Returns:
|
|
|
+ list of lowest common ancestor commit_ids
|
|
|
"""
|
|
|
|
|
|
def lookup_parents(commit_id):
|
|
|
- return r.object_store[commit_id].parents
|
|
|
+ return object_store[commit_id].parents
|
|
|
|
|
|
if not commit_ids:
|
|
|
return []
|
|
|
if len(commit_ids) <= 2:
|
|
|
- return find_merge_base(r, commit_ids)
|
|
|
+ return find_merge_base(object_store, commit_ids)
|
|
|
lcas = [commit_ids[0]]
|
|
|
others = commit_ids[1:]
|
|
|
for cmt in others:
|
|
@@ -116,153 +116,3 @@ def find_octopus_base(r, commit_ids):
|
|
|
next_lcas.extend(res)
|
|
|
lcas = next_lcas[:]
|
|
|
return lcas
|
|
|
-
|
|
|
-
|
|
|
-def test():
|
|
|
-
|
|
|
- all_tests_passed = True
|
|
|
-
|
|
|
- parents = {}
|
|
|
-
|
|
|
- def lookup_parents(commit_id):
|
|
|
- return parents.get(commit_id, [])
|
|
|
-
|
|
|
- def run_test(dag, inputs, expected):
|
|
|
- nonlocal parents
|
|
|
- parents = dag
|
|
|
- c1 = inputs[0]
|
|
|
- c2s = inputs[1:]
|
|
|
- res = _find_lcas(lookup_parents, c1, c2s)
|
|
|
- return set(res) == expected
|
|
|
-
|
|
|
- # two lowest common ancestors
|
|
|
- test1 = {
|
|
|
- '5': ['1', '2'],
|
|
|
- '4': ['3', '1'],
|
|
|
- '3': ['2'],
|
|
|
- '2': ['0'],
|
|
|
- '1': [],
|
|
|
- '0': []
|
|
|
- }
|
|
|
- test_passed = run_test(test1, ['4', '5'], set(['1', '2']))
|
|
|
- print('Test 1: Multiple LCA ', test_passed)
|
|
|
- all_tests_passed = all_tests_passed and test_passed
|
|
|
-
|
|
|
- # no common ancestor
|
|
|
- test2 = {
|
|
|
- '4': ['2'],
|
|
|
- '3': ['1'],
|
|
|
- '2': [],
|
|
|
- '1': ['0'],
|
|
|
- '0': [],
|
|
|
- }
|
|
|
- test_passed = run_test(test2, ['4', '3'], set([]))
|
|
|
- print('Test 2: No Common Ancestor ', test_passed)
|
|
|
- all_tests_passed = all_tests_passed and test_passed
|
|
|
-
|
|
|
- # ancestor
|
|
|
- test3 = {
|
|
|
- 'G': ['D', 'F'],
|
|
|
- 'F': ['E'],
|
|
|
- 'D': ['C'],
|
|
|
- 'C': ['B'],
|
|
|
- 'E': ['B'],
|
|
|
- 'B': ['A'],
|
|
|
- 'A': []
|
|
|
- }
|
|
|
- test_passed = run_test(test3, ['D', 'C'], set(['C']))
|
|
|
- print('Test 3: Ancestor ', test_passed)
|
|
|
- all_tests_passed = all_tests_passed and test_passed
|
|
|
-
|
|
|
- # parent
|
|
|
- test4 = {
|
|
|
- 'G': ['D', 'F'],
|
|
|
- 'F': ['E'],
|
|
|
- 'D': ['C'],
|
|
|
- 'C': ['B'],
|
|
|
- 'E': ['B'],
|
|
|
- 'B': ['A'],
|
|
|
- 'A': []
|
|
|
- }
|
|
|
- test_passed = run_test(test4, ['G', 'D'], set(['D']))
|
|
|
- print('Test 4: Direct Parent ', test_passed)
|
|
|
- all_tests_passed = all_tests_passed and test_passed
|
|
|
-
|
|
|
- # Another cross over
|
|
|
- test5 = {
|
|
|
- 'G': ['D', 'F'],
|
|
|
- 'F': ['E', 'C'],
|
|
|
- 'D': ['C', 'E'],
|
|
|
- 'C': ['B'],
|
|
|
- 'E': ['B'],
|
|
|
- 'B': ['A'],
|
|
|
- 'A': []
|
|
|
- }
|
|
|
- test_passed = run_test(test5, ['D', 'F'], set(['E', 'C']))
|
|
|
- print('Test 5: Cross Over ', test_passed)
|
|
|
- all_tests_passed = all_tests_passed and test_passed
|
|
|
-
|
|
|
- # three way merge commit straight from git docs
|
|
|
- test6 = {
|
|
|
- 'C': ['C1'],
|
|
|
- 'C1': ['C2'],
|
|
|
- 'C2': ['C3'],
|
|
|
- 'C3': ['C4'],
|
|
|
- 'C4': ['2'],
|
|
|
- 'B': ['B1'],
|
|
|
- 'B1': ['B2'],
|
|
|
- 'B2': ['B3'],
|
|
|
- 'B3': ['1'],
|
|
|
- 'A': ['A1'],
|
|
|
- 'A1': ['A2'],
|
|
|
- 'A2': ['A3'],
|
|
|
- 'A3': ['1'],
|
|
|
- '1': ['2'],
|
|
|
- '2': [],
|
|
|
- }
|
|
|
- # assumes a theoretical merge M exists that merges B and C first
|
|
|
- # which actually means find the first LCA from either of B OR C with A
|
|
|
- test_passed = run_test(test6, ['A', 'B', 'C'], set(['1']))
|
|
|
- all_tests_passed = all_tests_passed and test_passed
|
|
|
- print('Test 6: LCA of 3 commits ', test_passed)
|
|
|
-
|
|
|
- # octopus algorithm test
|
|
|
- # test straight from git docs of A, B, and C
|
|
|
- # but this time use octopus to find lcas of A, B, and C simultaneously
|
|
|
- test7 = {
|
|
|
- 'C': ['C1'],
|
|
|
- 'C1': ['C2'],
|
|
|
- 'C2': ['C3'],
|
|
|
- 'C3': ['C4'],
|
|
|
- 'C4': ['2'],
|
|
|
- 'B': ['B1'],
|
|
|
- 'B1': ['B2'],
|
|
|
- 'B2': ['B3'],
|
|
|
- 'B3': ['1'],
|
|
|
- 'A': ['A1'],
|
|
|
- 'A1': ['A2'],
|
|
|
- 'A2': ['A3'],
|
|
|
- 'A3': ['1'],
|
|
|
- '1': ['2'],
|
|
|
- '2': [],
|
|
|
- }
|
|
|
- parents = test7
|
|
|
- lcas = ['A']
|
|
|
- others = ['B', 'C']
|
|
|
- for cmt in others:
|
|
|
- next_lcas = []
|
|
|
- for ca in lcas:
|
|
|
- res = _find_lcas(lookup_parents, cmt, [ca])
|
|
|
- next_lcas.extend(res)
|
|
|
- lcas = next_lcas[:]
|
|
|
- test_passed = set(lcas) == set(['2'])
|
|
|
- all_tests_passed = all_tests_passed and test_passed
|
|
|
- print('Test 7: Octopus LCA of 3 commits ', test_passed)
|
|
|
-
|
|
|
- if all_tests_passed:
|
|
|
- print('All Tests Succesfful')
|
|
|
- return 0
|
|
|
-
|
|
|
-
|
|
|
-if __name__ == '__main__':
|
|
|
- sys.exit(test())
|