1 import sys
, re
, os
, traceback
4 if sys
.version_info
[0] < 2 or \
5 (sys
.version_info
[0] == 2 and sys
.version_info
[1] < 4):
6 print 'Python version 2.4 required, found', \
7 str(sys
.version_info
[0])+'.'+str(sys
.version_info
[1])+'.'+ \
8 str(sys
.version_info
[2])
14 printList(args
, sys
.stderr
)
21 functionsToDebug
= Set()
25 functionsToDebug
.add(func
)
27 functionsToDebug
.add(func
.func_name
)
31 funcName
= traceback
.extract_stack()[-2][2]
32 if funcName
in functionsToDebug
:
35 def printList(list, file=sys
.stdout
):
44 class ProgramError(Exception):
45 def __init__(self
, progStr
, error
):
46 self
.progStr
= progStr
50 return self
.progStr
+ ': ' + self
.error
52 addDebug('runProgram')
53 def runProgram(prog
, input=None, returnCode
=False, env
=None, pipeOutput
=True):
54 debug('runProgram prog:', str(prog
), 'input:', str(input))
58 progStr
= ' '.join(prog
)
62 stderr
= subprocess
.STDOUT
63 stdout
= subprocess
.PIPE
67 pop
= subprocess
.Popen(prog
,
68 shell
= type(prog
) is str,
71 stdin
=subprocess
.PIPE
,
74 debug('strerror:', e
.strerror
)
75 raise ProgramError(progStr
, e
.strerror
)
78 pop
.stdin
.write(input)
82 out
= pop
.stdout
.read()
91 if code
!= 0 and not returnCode
:
92 debug('error output:', out
)
94 raise ProgramError(progStr
, out
)
95 # debug('output:', out.replace('\0', '\n'))
98 # Code for computing common ancestors
99 # -----------------------------------
107 # The 'virtual' commit objects have SHAs which are integers
108 shaRE
= re
.compile('^[0-9a-f]{40}$')
110 return (type(obj
) is str and bool(shaRE
.match(obj
))) or \
111 (type(obj
) is int and obj
>= 1)
114 def __init__(self
, sha
, parents
, tree
=None):
115 self
.parents
= parents
116 self
.firstLineMsg
= None
125 self
.sha
= getUniqueId()
127 self
.firstLineMsg
= 'virtual commit'
131 self
.sha
= sha
.rstrip()
132 assert(isSha(self
.sha
))
136 assert(self
._tree
!= None)
141 return str(self
.sha
) + ' ' + self
.firstLineMsg
144 return self
.shortInfo()
147 if self
.virtual
or self
.firstLineMsg
!= None:
150 info
= runProgram(['git-cat-file', 'commit', self
.sha
])
151 info
= info
.split('\n')
155 self
.firstLineMsg
= l
158 if l
.startswith('tree'):
159 self
._tree
= l
[5:].rstrip()
168 def addNode(self
, node
):
169 assert(isinstance(node
, Commit
))
170 self
.shaMap
[node
.sha
] = node
171 self
.commits
.append(node
)
172 for p
in node
.parents
:
173 p
.children
.append(node
)
176 def reachableNodes(self
, n1
, n2
):
187 def fixParents(self
, node
):
188 for x
in range(0, len(node
.parents
)):
189 node
.parents
[x
] = self
.shaMap
[node
.parents
[x
]]
191 # addDebug('buildGraph')
192 def buildGraph(heads
):
193 debug('buildGraph heads:', heads
)
199 out
= runProgram(['git-rev-list', '--parents'] + heads
)
200 for l
in out
.split('\n'):
205 # This is a hack, we temporarily use the 'parents' attribute
206 # to contain a list of SHA1:s. They are later replaced by proper
208 c
= Commit(shas
[0], shas
[1:])
221 # Write the empty tree to the object database and return its SHA1
222 def writeEmptyTree():
223 tmpIndex
= os
.environ
['GIT_DIR'] + '/merge-tmp-index'
230 newEnv
= os
.environ
.copy()
231 newEnv
['GIT_INDEX_FILE'] = tmpIndex
232 res
= runProgram(['git-write-tree'], env
=newEnv
).rstrip()
236 def addCommonRoot(graph
):
238 for c
in graph
.commits
:
239 if len(c
.parents
) == 0:
242 superRoot
= Commit(sha
=None, parents
=[], tree
=writeEmptyTree())
243 graph
.addNode(superRoot
)
245 r
.parents
= [superRoot
]
246 superRoot
.children
= roots
249 def getCommonAncestors(graph
, commit1
, commit2
):
250 '''Find the common ancestors for commit1 and commit2'''
251 assert(isinstance(commit1
, Commit
) and isinstance(commit2
, Commit
))
253 def traverse(start
, set):
255 while len(stack
) > 0:
263 traverse(commit1
, h1Set
)
264 traverse(commit2
, h2Set
)
265 shared
= h1Set
.intersection(h2Set
)
268 shared
= [addCommonRoot(graph
)]
273 if len([c
for c
in s
.children
if c
in shared
]) == 0: