comparison git_handler.py @ 51:1421d04f1ad2

merged from dulwich
author Scott Chacon <schacon@gmail.com>
date Wed, 29 Apr 2009 11:04:45 -0700
parents 3b62270c1fad d274092e3b24
children 87d462a6b796
comparison
equal deleted inserted replaced
47:3b62270c1fad 51:1421d04f1ad2
12 ShaFile, 12 ShaFile,
13 Tag, 13 Tag,
14 Tree, 14 Tree,
15 hex_to_sha 15 hex_to_sha
16 ) 16 )
17 17
18 import math 18 import math
19 19
20 def seconds_to_offset(time): 20 def seconds_to_offset(time):
21 hours = (float(time) / 60 / 60) 21 hours = (float(time) / 60 / 60)
22 hour_diff = math.fmod(time, 60) 22 hour_diff = math.fmod(time, 60)
76 def map_git_get(self, hgsha): 76 def map_git_get(self, hgsha):
77 if hgsha in self._map_hg: 77 if hgsha in self._map_hg:
78 return self._map_hg[hgsha] 78 return self._map_hg[hgsha]
79 else: 79 else:
80 return None 80 return None
81 81
82 def load_map(self): 82 def load_map(self):
83 self._map_git = {} 83 self._map_git = {}
84 self._map_hg = {} 84 self._map_hg = {}
85 if os.path.exists(self.repo.join('git-mapfile')): 85 if os.path.exists(self.repo.join('git-mapfile')):
86 for line in self.repo.opener('git-mapfile'): 86 for line in self.repo.opener('git-mapfile'):
139 if key in self._config: 139 if key in self._config:
140 name = self._config[key] 140 name = self._config[key]
141 print "URL for " + remote_name + " : " + name 141 print "URL for " + remote_name + " : " + name
142 else: 142 else:
143 print "No remote named : " + remote_name 143 print "No remote named : " + remote_name
144 return 144 return
145 145
146 def remote_list(self): 146 def remote_list(self):
147 for key, value in self._config.iteritems(): 147 for key, value in self._config.iteritems():
148 if key[0:6] == 'remote': 148 if key[0:6] == 'remote':
149 print key + "\t" + value 149 print key + "\t" + value
150 150
151 def remote_name_to_url(self, remote_name): 151 def remote_name_to_url(self, remote_name):
152 return self._config['remote.' + remote_name + '.url'] 152 return self._config['remote.' + remote_name + '.url']
153 153
154 def update_references(self): 154 def update_references(self):
155 # TODO : if bookmarks exist, add them as git branches 155 # TODO : if bookmarks exist, add them as git branches
156 c = self.map_git_get(hex(self.repo.changelog.tip())) 156 c = self.map_git_get(hex(self.repo.changelog.tip()))
157 self.git.set_ref('refs/heads/master', c) 157 self.git.set_ref('refs/heads/master', c)
158 158
159 def export_git_objects(self): 159 def export_git_objects(self):
160 print "exporting git objects" 160 print "exporting git objects"
161 for rev in self.repo.changelog: 161 for rev in self.repo.changelog:
162 self.export_hg_commit(rev) 162 self.export_hg_commit(rev)
163 163
164 # convert this commit into git objects 164 # convert this commit into git objects
165 # go through the manifest, convert all blobs/trees we don't have 165 # go through the manifest, convert all blobs/trees we don't have
166 # write the commit object (with metadata info) 166 # write the commit object (with metadata info)
167 def export_hg_commit(self, rev): 167 def export_hg_commit(self, rev):
168 # return if we've already processed this 168 # return if we've already processed this
169 node = self.repo.changelog.lookup(rev) 169 node = self.repo.changelog.lookup(rev)
170 phgsha = hex(node) 170 phgsha = hex(node)
171 pgit_sha = self.map_git_get(phgsha) 171 pgit_sha = self.map_git_get(phgsha)
172 if pgit_sha: 172 if pgit_sha:
173 return pgit_sha 173 return pgit_sha
174 174
175 print "converting revision " + str(rev) 175 print "converting revision " + str(rev)
176 176
177 # make sure parents are converted first 177 # make sure parents are converted first
178 parents = self.repo.parents(rev) 178 parents = self.repo.parents(rev)
179 for parent in parents: 179 for parent in parents:
180 p_rev = parent.rev() 180 p_rev = parent.rev()
181 hgsha = hex(parent.node()) 181 hgsha = hex(parent.node())
182 git_sha = self.map_git_get(hgsha) 182 git_sha = self.map_git_get(hgsha)
183 if not p_rev == -1: 183 if not p_rev == -1:
184 if not git_sha: 184 if not git_sha:
185 self.export_hg_commit(p_rev) 185 self.export_hg_commit(p_rev)
186 186
187 ctx = self.repo.changectx(rev) 187 ctx = self.repo.changectx(rev)
188 tree_sha = self.write_git_tree(ctx) 188 tree_sha = self.write_git_tree(ctx)
189 189
190 # TODO : something with tags? 190 # TODO : something with tags?
191 # TODO : explicit file renaming, copying? 191 # TODO : explicit file renaming, copying?
192 192
193 commit = {} 193 commit = {}
194 commit['tree'] = tree_sha 194 commit['tree'] = tree_sha
195 (time, timezone) = ctx.date() 195 (time, timezone) = ctx.date()
196 commit['author'] = ctx.user() + ' ' + str(int(time)) + ' ' + seconds_to_offset(timezone) 196 commit['author'] = ctx.user() + ' ' + str(int(time)) + ' ' + seconds_to_offset(timezone)
197 message = ctx.description() 197 message = ctx.description()
198 commit['message'] = ctx.description() 198 commit['message'] = ctx.description()
199 commit['message'] += "\n\n--HG--\n" 199 commit['message'] += "\n\n--HG--\n"
200 commit['message'] += "branch : " + ctx.branch() + "\n" 200 commit['message'] += "branch : " + ctx.branch() + "\n"
201 201
202 commit['parents'] = [] 202 commit['parents'] = []
203 for parent in parents: 203 for parent in parents:
204 hgsha = hex(parent.node()) 204 hgsha = hex(parent.node())
205 git_sha = self.map_git_get(hgsha) 205 git_sha = self.map_git_get(hgsha)
206 if git_sha: 206 if git_sha:
207 commit['parents'].append(git_sha) 207 commit['parents'].append(git_sha)
208 208
209 commit_sha = self.git.write_commit_hash(commit) # writing new blobs to git 209 commit_sha = self.git.write_commit_hash(commit) # writing new blobs to git
210 self.map_set(commit_sha, phgsha) 210 self.map_set(commit_sha, phgsha)
211 return commit_sha 211 return commit_sha
212 212
213 def write_git_tree(self, ctx): 213 def write_git_tree(self, ctx):
214 trees = {} 214 trees = {}
215 man = ctx.manifest() 215 man = ctx.manifest()
216 for filenm in man.keys(): 216 for filenm in man.keys():
217 # write blob if not in our git database 217 # write blob if not in our git database
218 fctx = ctx.filectx(filenm) 218 fctx = ctx.filectx(filenm)
219 is_exec = 'x' in fctx.flags() 219 is_exec = 'x' in fctx.flags()
220 is_link = 'l' in fctx.flags() 220 is_link = 'l' in fctx.flags()
221 file_id = hex(fctx.filenode()) 221 file_id = hex(fctx.filenode())
246 if parentpath not in trees: 246 if parentpath not in trees:
247 trees[parentpath] = [] 247 trees[parentpath] = []
248 if treeentry not in trees[parentpath]: 248 if treeentry not in trees[parentpath]:
249 trees[parentpath].append( treeentry ) 249 trees[parentpath].append( treeentry )
250 else: 250 else:
251 fileentry = ['blob', parts[0], blob_sha, is_exec, is_link] 251 fileentry = ['blob', parts[0], blob_sha, is_exec, is_link]
252 if '/' not in trees: 252 if '/' not in trees:
253 trees['/'] = [] 253 trees['/'] = []
254 trees['/'].append(fileentry) 254 trees['/'].append(fileentry)
255 255
256 # sort by tree depth, so we write the deepest trees first 256 # sort by tree depth, so we write the deepest trees first
257 dirs = trees.keys() 257 dirs = trees.keys()
258 dirs.sort(lambda a, b: len(b.split('/'))-len(a.split('/'))) 258 dirs.sort(lambda a, b: len(b.split('/'))-len(a.split('/')))
259 dirs.remove('/') 259 dirs.remove('/')
260 dirs.append('/') 260 dirs.append('/')
261 261
262 # write all the trees 262 # write all the trees
263 tree_sha = None 263 tree_sha = None
264 tree_shas = {} 264 tree_shas = {}
265 for dirnm in dirs: 265 for dirnm in dirs:
266 tree_data = [] 266 tree_data = []
271 entry[2] = sha 271 entry[2] = sha
272 tree_data.append(entry) 272 tree_data.append(entry)
273 tree_sha = self.git.write_tree_array(tree_data) # writing new trees to git 273 tree_sha = self.git.write_tree_array(tree_data) # writing new trees to git
274 tree_shas[dirnm] = tree_sha 274 tree_shas[dirnm] = tree_sha
275 return tree_sha # should be the last root tree sha 275 return tree_sha # should be the last root tree sha
276 276
277 def remote_head(self, remote_name): 277 def remote_head(self, remote_name):
278 for head, sha in self.git.remote_refs(remote_name).iteritems(): 278 for head, sha in self.git.remote_refs(remote_name).iteritems():
279 if head == 'HEAD': 279 if head == 'HEAD':
280 return self.map_hg_get(sha) 280 return self.map_hg_get(sha)
281 return None 281 return None
296 except: 296 except:
297 raise 297 raise
298 298
299 # TODO : for now, we'll just push all heads that match remote heads 299 # TODO : for now, we'll just push all heads that match remote heads
300 # * we should have specified push, tracking branches and --all 300 # * we should have specified push, tracking branches and --all
301 # takes a dict of refs:shas from the server and returns what should be 301 # takes a dict of refs:shas from the server and returns what should be
302 # pushed up 302 # pushed up
303 def get_changed_refs(self, refs): 303 def get_changed_refs(self, refs):
304 keys = refs.keys() 304 keys = refs.keys()
305 305
306 changed = [] 306 changed = []
307 if not keys: 307 if not keys:
308 return None 308 return None
309 309
310 # TODO : this is a huge hack 310 # TODO : this is a huge hack
311 if keys[0] == 'capabilities^{}': # nothing on the server yet - first push 311 if keys[0] == 'capabilities^{}': # nothing on the server yet - first push
312 changed.append(("0"*40, self.git.ref('master'), 'refs/heads/master')) 312 changed.append(("0"*40, self.git.ref('master'), 'refs/heads/master'))
313 313
314 for ref_name in keys: 314 for ref_name in keys:
315 parts = ref_name.split('/') 315 parts = ref_name.split('/')
316 if parts[0] == 'refs': # strip off 'refs/heads' 316 if parts[0] == 'refs': # strip off 'refs/heads'
317 if parts[1] == 'heads': 317 if parts[1] == 'heads':
318 head = "/".join([v for v in parts[2:]]) 318 head = "/".join([v for v in parts[2:]])
319 local_ref = self.git.ref(ref_name) 319 local_ref = self.git.ref(ref_name)
320 if local_ref: 320 if local_ref:
321 if not local_ref == refs[ref_name]: 321 if not local_ref == refs[ref_name]:
322 changed.append((refs[ref_name], local_ref, ref_name)) 322 changed.append((refs[ref_name], local_ref, ref_name))
323 return changed 323 return changed
324 324
325 # takes a list of shas the server wants and shas the server has 325 # takes a list of shas the server wants and shas the server has
326 # and generates a list of commit shas we need to push up 326 # and generates a list of commit shas we need to push up
327 def generate_pack_contents(self, want, have): 327 def generate_pack_contents(self, want, have):
328 graph_walker = SimpleFetchGraphWalker(want, self.git.get_parents) 328 graph_walker = SimpleFetchGraphWalker(want, self.git.get_parents)
329 next = graph_walker.next() 329 next = graph_walker.next()
332 if next in have: 332 if next in have:
333 graph_walker.ack(next) 333 graph_walker.ack(next)
334 else: 334 else:
335 shas.append(next) 335 shas.append(next)
336 next = graph_walker.next() 336 next = graph_walker.next()
337 337
338 # so now i have the shas, need to turn them into a list of 338 # so now i have the shas, need to turn them into a list of
339 # tuples (sha, path) for ALL the objects i'm sending 339 # tuples (sha, path) for ALL the objects i'm sending
340 # TODO : don't send blobs or trees they already have 340 # TODO : don't send blobs or trees they already have
341 def get_objects(tree, path): 341 def get_objects(tree, path):
342 changes = list() 342 changes = list()
343 changes.append((tree, path)) 343 changes.append((tree, path))
348 if isinstance (obj, Blob): 348 if isinstance (obj, Blob):
349 changes.append((obj, path + name)) 349 changes.append((obj, path + name))
350 elif isinstance(obj, Tree): 350 elif isinstance(obj, Tree):
351 changes.extend (get_objects (obj, path + name + '/')) 351 changes.extend (get_objects (obj, path + name + '/'))
352 return changes 352 return changes
353 353
354 objects = [] 354 objects = []
355 for commit_sha in shas: 355 for commit_sha in shas:
356 commit = self.git.commit(commit_sha) 356 commit = self.git.commit(commit_sha)
357 objects.append((commit, 'commit')) 357 objects.append((commit, 'commit'))
358 tree = self.git.get_object(commit.tree) 358 tree = self.git.get_object(commit.tree)
359 objects.extend( get_objects(tree, '/') ) 359 objects.extend( get_objects(tree, '/') )
360 360
361 return objects 361 return objects
362 362
363 def fetch_pack(self, remote_name): 363 def fetch_pack(self, remote_name):
364 git_url = self.remote_name_to_url(remote_name) 364 git_url = self.remote_name_to_url(remote_name)
365 client, path = self.get_transport_and_path(git_url) 365 client, path = self.get_transport_and_path(git_url)
366 graphwalker = SimpleFetchGraphWalker(self.git.heads().values(), self.git.get_parents) 366 graphwalker = SimpleFetchGraphWalker(self.git.heads().values(), self.git.get_parents)
367 f, commit = self.git.object_store.add_pack() 367 f, commit = self.git.object_store.add_pack()
425 425
426 def import_git_commit(self, commit): 426 def import_git_commit(self, commit):
427 print "importing: " + commit.id 427 print "importing: " + commit.id
428 # TODO : look for HG metadata in the message and use it 428 # TODO : look for HG metadata in the message and use it
429 # TODO : add extra Git data (committer info) as extras to changeset 429 # TODO : add extra Git data (committer info) as extras to changeset
430 430
431 # TODO : (?) have to handle merge contexts at some point (two parent files, etc) 431 # TODO : (?) have to handle merge contexts at some point (two parent files, etc)
432 # TODO : throw IOError for removed files 432 # TODO : Do something less coarse-grained than try/except on the
433 # get_file call for removed files
433 def getfilectx(repo, memctx, f): 434 def getfilectx(repo, memctx, f):
434 (e, sha, data) = self.git.get_file(commit, f) 435 try:
436 (e, sha, data) = self.git.get_file(commit, f)
437 except TypeError:
438 raise IOError()
435 e = '' # TODO : make this a real mode 439 e = '' # TODO : make this a real mode
436 return context.memfilectx(f, data, 'l' in e, 'x' in e, None) 440 return context.memfilectx(f, data, 'l' in e, 'x' in e, None)
437 441
438 p1 = "0" * 40 442 p1 = "0" * 40
439 p2 = "0" * 40 443 p2 = "0" * 40
482 return SubprocessGitClient(), uri 486 return SubprocessGitClient(), uri
483 487
484 def clear(self): 488 def clear(self):
485 git_dir = self.repo.join('git') 489 git_dir = self.repo.join('git')
486 mapfile = self.repo.join('git-mapfile') 490 mapfile = self.repo.join('git-mapfile')
487 if os.path.exists(git_dir): 491 if os.path.exists(git_dir):
488 for root, dirs, files in os.walk(git_dir, topdown=False): 492 for root, dirs, files in os.walk(git_dir, topdown=False):
489 for name in files: 493 for name in files:
490 os.remove(os.path.join(root, name)) 494 os.remove(os.path.join(root, name))
491 for name in dirs: 495 for name in dirs:
492 os.rmdir(os.path.join(root, name)) 496 os.rmdir(os.path.join(root, name))
493 os.rmdir(git_dir) 497 os.rmdir(git_dir)
494 if os.path.exists(mapfile): 498 if os.path.exists(mapfile):
495 os.remove(mapfile) 499 os.remove(mapfile)
496 500
497 501
498 '' 502 ''
499 """ 503 """
500 Tarjan's algorithm and topological sorting implementation in Python 504 Tarjan's algorithm and topological sorting implementation in Python
501 by Paul Harrison 505 by Paul Harrison