1 import sys, re, os, traceback
5 printList(args, sys.stderr)
8 def printList(list, file=sys.stdout):
14 if sys.version_info[0] < 2 or \
15 (sys.version_info[0] == 2 and sys.version_info[1] < 4):
16 die('Python version 2.4 required, found', \
17 str(sys.version_info[0])+'.'+str(sys.version_info[1])+'.'+ \
18 str(sys.version_info[2]))
26 functionsToDebug = Set()
30 functionsToDebug.add(func)
32 functionsToDebug.add(func.func_name)
36 funcName = traceback.extract_stack()[-2][2]
37 if funcName in functionsToDebug:
43 class ProgramError(Exception):
44 def __init__(self, progStr, error):
45 self.progStr = progStr
49 return self.progStr + ': ' + self.error
51 addDebug('runProgram')
52 def runProgram(prog, input=None, returnCode=False, env=None, pipeOutput=True):
53 debug('runProgram prog:', str(prog), 'input:', str(input))
57 progStr = ' '.join(prog)
61 stderr = subprocess.STDOUT
62 stdout = subprocess.PIPE
66 pop = subprocess.Popen(prog,
67 shell = type(prog) is str,
70 stdin=subprocess.PIPE,
73 debug('strerror:', e.strerror)
74 raise ProgramError(progStr, e.strerror)
77 pop.stdin.write(input)
81 out = pop.stdout.read()
90 if code != 0 and not returnCode:
91 debug('error output:', out)
93 raise ProgramError(progStr, out)
94 # debug('output:', out.replace('\0', '\n'))
97 # Code for computing common ancestors
98 # -----------------------------------
106 # The 'virtual' commit objects have SHAs which are integers
107 shaRE = re.compile('^[0-9a-f]{40}$')
109 return (type(obj) is str and bool(shaRE.match(obj))) or \
110 (type(obj) is int and obj >= 1)
113 def __init__(self, sha, parents, tree=None):
114 self.parents = parents
115 self.firstLineMsg = None
124 self.sha = getUniqueId()
126 self.firstLineMsg = 'virtual commit'
130 self.sha = sha.rstrip()
131 assert(isSha(self.sha))
135 assert(self._tree != None)
140 return str(self.sha) + ' ' + self.firstLineMsg
143 return self.shortInfo()
146 if self.virtual or self.firstLineMsg != None:
149 info = runProgram(['git-cat-file', 'commit', self.sha])
150 info = info.split('\n')
154 self.firstLineMsg = l
157 if l.startswith('tree'):
158 self._tree = l[5:].rstrip()
167 def addNode(self, node):
168 assert(isinstance(node, Commit))
169 self.shaMap[node.sha] = node
170 self.commits.append(node)
171 for p in node.parents:
172 p.children.append(node)
175 def reachableNodes(self, n1, n2):
186 def fixParents(self, node):
187 for x in range(0, len(node.parents)):
188 node.parents[x] = self.shaMap[node.parents[x]]
190 # addDebug('buildGraph')
191 def buildGraph(heads):
192 debug('buildGraph heads:', heads)
198 out = runProgram(['git-rev-list', '--parents'] + heads)
199 for l in out.split('\n'):
204 # This is a hack, we temporarily use the 'parents' attribute
205 # to contain a list of SHA1:s. They are later replaced by proper
207 c = Commit(shas[0], shas[1:])
220 # Write the empty tree to the object database and return its SHA1
221 def writeEmptyTree():
222 tmpIndex = os.environ['GIT_DIR'] + '/merge-tmp-index'
229 newEnv = os.environ.copy()
230 newEnv['GIT_INDEX_FILE'] = tmpIndex
231 res = runProgram(['git-write-tree'], env=newEnv).rstrip()
235 def addCommonRoot(graph):
237 for c in graph.commits:
238 if len(c.parents) == 0:
241 superRoot = Commit(sha=None, parents=[], tree=writeEmptyTree())
242 graph.addNode(superRoot)
244 r.parents = [superRoot]
245 superRoot.children = roots
248 def getCommonAncestors(graph, commit1, commit2):
249 '''Find the common ancestors for commit1 and commit2'''
250 assert(isinstance(commit1, Commit) and isinstance(commit2, Commit))
252 def traverse(start, set):
254 while len(stack) > 0:
262 traverse(commit1, h1Set)
263 traverse(commit2, h2Set)
264 shared = h1Set.intersection(h2Set)
267 shared = [addCommonRoot(graph)]
272 if len([c for c in s.children if c in shared]) == 0: