From 9279cd57cf306421f371dcf744c4b687197ea907 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Mon, 12 Feb 2007 23:25:41 +0100 Subject: [PATCH] Use common ancestor for start point - cleanups - allow pull to fail everywhere (merger stuff?) - don't use non-tip From commit if we merge tip commits in --- darcs2git.py | 166 +++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 117 insertions(+), 49 deletions(-) diff --git a/darcs2git.py b/darcs2git.py index 0903e79..e45ec27 100644 --- a/darcs2git.py +++ b/darcs2git.py @@ -31,6 +31,7 @@ log_file = None options = None mail_to_name_dict = {} pending_patches = {} +git_commits = {} used_tags = {} ################################################################ @@ -158,6 +159,32 @@ class DarcsConversionRepo: self._current_number = -1 self._is_valid = -1 + def is_valid (self): + if not self._is_valid: + return False + + darcs_dir = self.dir + '/_darcs' + inv = self.inventory () + for p in self.patches[:self._current_number + 1]: + if p.short_id () not in inv: + return False + if self._current_number + 1 != len (inv.split ('\n[')): + return False + + return True + + def has_patch (self, p): + id = p.attributes['hash'] + darcs_dir = self.dir + '/_darcs' + f = darcs_dir + '/patches/' + id + if not os.path.exists (f): + return False + + return p.short_id () in self.inventory () + + def pristine_tree (self): + return self.dir + '/_darcs/pristine' + def go_back (self, count): assert self._is_valid @@ -168,9 +195,11 @@ class DarcsConversionRepo: def clean (self): system ('rm -rf %s' % self.dir) + def checkout (self): dir = self.dir system ('rsync -a %(dir)s/_darcs/pristine/ %(dir)s/' % locals ()) + def pull (self, patch): id = patch.attributes['hash'] source_repo = patch.dir @@ -225,11 +254,6 @@ class DarcsConversionRepo: It might be quicker and/or more correct to wind/rewind the repo with pull and unpull.""" - try: - progress ('from %d to %d cur%d' % (from_patch.number, - to_patch.number)) - except : - pass dir = self.dir source = to_patch.dir @@ -254,27 +278,10 @@ class DarcsConversionRepo: if not success: raise PullConflict () - def is_valid (self): - ok = self._is_valid - darcs_dir = self.dir + '/_darcs' - ok = ok and len (glob.glob (darcs_dir + '/patches/*z')) == self._current_number + 1 - ok = ok and self._current_number + 1 == len (self.inventory ().split ('\n[')) - return ok - - def has_patch (self, p): - id = p.attributes['hash'] - darcs_dir = self.dir + '/_darcs' - f = darcs_dir + '/patches/' + id - if not os.path.exists (f): - return False - - short_id = id.split ('-')[0] - return short_id in self.inventory () - - def pristine_tree (self): - return self.dir + '/_darcs/pristine' - class DarcsPatch: + def __repr__ (self): + return 'patch %d' % self.number + def __init__ (self, xml, dir): self.xml = xml self.dir = dir @@ -288,6 +295,9 @@ class DarcsPatch: self.extract_message () self.extract_time () + def short_id (self): + return '%s**%s' % (self.author_mail, self.attributes['hash'].split ('-')[0]) + def filename (self): return self.dir + '/_darcs/patches/' + self.attributes['hash'] @@ -386,6 +396,43 @@ def get_darcs_patches (darcs_repo): ################################################################ # GIT export + +class GitCommit: + def __init__ (self, parent, darcs_patch): + self.parent = parent + self.darcs_patch = darcs_patch + if parent: + self.depth = parent.depth + 1 + else: + self.depth = 0 + + def number (self): + return self.darcs_patch.number + + def parent_patch (self): + if self.parent: + return self.parent.darcs_patch + else: + return None + +def common_ancestor (a, b): + while 1: + if a.depth < b.depth: + b = b.parent + elif a.depth > b.depth: + a = a.parent + else: + break + + while a and b: + if a == b: + return a + + a = a.parent + b = b.parent + + return None + def export_checkpoint (gfi): gfi.write ('checkpoint\n\n') @@ -415,26 +462,39 @@ def export_commit (repo, patch, last_patch, gfi): msg += '\n\n#%d\n' % patch.number gfi.write ('data %d\n%s\n' % (len (msg), msg)) + + if last_patch: - gfi.write ('from :%d\n' % (last_patch.number + 1)) - if pending_patches.has_key (last_patch.number): - del pending_patches[last_patch.number] - - for (n, p) in pending_patches.items (): - if repo.has_patch (p): - gfi.write ('merge :%d\n' % (n + 1)) - del pending_patches[n] + mergers = [] + for (n, p) in pending_patches.items (): + if repo.has_patch (p): + mergers.append (n) + del pending_patches[n] + + if mergers == []: + mergers = [last_patch.number] + + gfi.write ('from :%d\n' % (mergers[0] + 1)) + for m in mergers[1:]: + gfi.write ('merge :%d\n' % (m + 1)) + pending_patches[patch.number] = patch export_tree (repo.pristine_tree (), gfi) + + n = -1 + if last_patch: + n = last_patch.number + git_commits[patch.number] = GitCommit (git_commits.get (n, None), + patch) + def export_pending (gfi): if len (pending_patches.items ()) == 1: gfi.write ('reset refs/heads/master\n') gfi.write ('from :%d\n\n' % (pending_patches.values()[0].number+1)) return - for (n, p) in pending_patches.items (): gfi.write ('reset refs/heads/master%d\n' % n) gfi.write ('from :%d\n\n' % (n+1)) @@ -497,21 +557,26 @@ def main (): for p in patches: parent_patch = None parent_number = -1 - for (n, q) in reversed (sorted (pending_patches.items ())): + + combinations = [(v, w) for v in pending_patches.values () + for w in pending_patches.values ()] + candidates = [common_ancestor (git_commits[c[0].number], git_commits[c[1].number]) for c in combinations] + candidates = sorted ([(-a.depth, a) for a in candidates]) + for (depth, c) in candidates: + q = c.darcs_patch try: conv_repo.go_from_to (q, p) - parent_patch = q parent_number = q.number - progress ('Found branch tip as predecessor') + progress ('Found existing common parent as predecessor') break + except PullConflict: pass ## no branches found where we could attach. ## try previous commits one by one. - if not parent_patch: parent_number = p.number - 2 while 1: @@ -527,18 +592,21 @@ def main (): parent_number -= 1 if parent_number < 0: - raise Exception('urg') - - - progress ('Export %d -> %d (total %d)' % (parent_number, - p.number, len (patches))) - export_commit (conv_repo, p, parent_patch, gfi) - if p.tag_name (): - export_tag (p, gfi) + break + + + if parent_number >= 0 or p.number == 0: + progress ('Export %d -> %d (total %d)' % (parent_number, + p.number, len (patches))) + export_commit (conv_repo, p, parent_patch, gfi) + if p.tag_name (): + export_tag (p, gfi) + + if p.number % options.checkpoint_frequency == 0: + export_checkpoint (gfi) + else: + progress ("Can't import patch %d, need conflict resolution patch?" % p.number) - if p.number % options.checkpoint_frequency == 0: - export_checkpoint (gfi) - export_pending (gfi) gfi.close () -- 2.11.4.GIT