9 class GitExporter(object):
10 def __init__(self, repo):
11 self.git_hg = repo.git_hg
13 self.gitmarksfile = os.path.join(repo.hash, 'git.marks')
14 self.prefix = repo.prefix
15 self.nullref = "0" * 40
19 self.use_options = False
25 def tohex(self, binhex):
26 return binascii.hexlify(binhex)
40 def parents(self, parents):
41 parents = [self.tohex(i.node()) for i in parents]
42 parents = [i for i in parents if i != self.nullref]
43 assert all(i in self.mapping for i in parents)
44 parents = [':%d' % self.mapping[i] for i in parents]
49 return self.prefix + ctx.branch()
51 def write(self, *args):
52 msg = ''.join([str(i) for i in args])
57 self.write('#', SP, msg, LF)
59 def feature(self, feature, value=None):
60 if not self.use_features:
64 self.write('feature', SP, feature, '=', value, LF)
66 self.write('feature', SP, feature, LF)
68 def option(self, option, value=None):
69 if not self.use_options:
73 self.write('option', SP, option, '=', value, LF)
75 self.write('option', SP, option, LF)
77 def option_quiet(self):
80 def feature_relative_marks(self):
81 self.feature('relative-marks')
83 def feature_export_marks(self, marks):
84 self.feature('export-marks', marks)
86 def feature_import_marks(self, marks):
87 self.feature('import-marks', marks)
89 def feature_force(self):
92 def progress(self, message):
93 self.write('progress', SP, message, LF)
95 def write_data(self, data):
97 self.write('data', SP, count, LF)
100 def write_mark(self, idnum):
101 self.write('mark', SP, ':', idnum, LF)
103 def write_blob(self, data, idnum):
104 self.write('blob', LF)
105 self.write_mark(idnum)
106 self.write_data(data)
108 def write_file(self, ctx, file, idnum):
109 fctx = ctx.filectx(file)
112 self.write_blob(data, idnum)
114 def write_commit(self, ref):
115 self.write('commit', SP, ref, LF)
117 def write_author(self, author):
118 self.write('author', SP, author, LF)
120 def write_committer(self, committer):
121 self.write('committer', SP, committer, LF)
123 def write_from(self, parent):
124 self.write('from', SP, parent, LF)
126 def write_merge(self, parent):
127 self.write('merge', SP, parent, LF)
129 def write_reset(self, ref, idnum):
130 self.write('reset', SP, ref, LF)
131 self.write('from', SP, ':', idnum, LF)
133 def write_parents(self, parents):
134 parents = self.parents(parents)
142 self.write_from(parent)
144 for parent in parents[1:]:
145 self.write_merge(parent)
147 def write_filedeleteall(self):
148 self.write('deleteall', LF)
150 def write_filedelete(self, ctx, name):
151 self.write('D', SP, name, LF)
153 def write_filemodify_mark(self, mode, name, mark):
154 self.write('M', SP, mode, SP, ':', mark, SP, name, LF)
156 def write_filemodify_inline(self, mode, name, data):
157 self.write('M', SP, mode, SP, 'inline', SP, name, LF)
158 self.write_data(data)
160 def write_filemodify(self, ctx, name):
161 fctx = ctx.filectx(name)
164 hash = self.tohex(nodesha)
165 mode = self.mode(fctx)
167 if hash in self.mapping:
168 mark = self.mapping[hash]
169 self.write_filemodify_mark(mode, name, mark)
172 self.write_filemodify_inline(mode, name, data)
174 def write_files(self, ctx):
177 if len(ctx.parents()) == 2:
178 self.write_filedeleteall()
180 self.write_filemodify(ctx, name)
182 for name in ctx.files():
185 self.write_filedelete(ctx, name)
187 self.write_filemodify(ctx, name)
189 def export_files(self, ctx):
192 for name in [i for i in ctx.files() if i in man]:
193 idnum = self.nextid()
195 hash = self.tohex(nodesha)
197 self.write_file(ctx, name, idnum)
198 self.mapping[hash] = idnum
200 def export_commit(self, ctx, ref, idnum, msg, parents):
201 author = self.git_hg.get_author(ctx)
202 committer = self.git_hg.get_committer(ctx)
203 committer = committer if committer else author
205 self.debug('exporting commit')
206 self.write_commit(ref)
207 self.write_mark(idnum)
208 self.write_author(author)
209 self.write_committer(committer)
211 self.write_parents(parents)
212 self.write_files(ctx)
213 self.debug('commit exported')
215 def export_revision(self, ctx):
217 hash = self.tohex(nodesha)
219 if hash in self.mapping:
222 self.export_files(ctx)
224 idnum = self.nextid()
227 msg = self.git_hg.get_message(ctx)
228 parents = self.git_hg.get_parents(ctx)
230 self.export_commit(ctx, ref, idnum, msg, parents)
231 self.mapping[hash] = idnum
235 def export_branch(self, name, rev):
236 ctx = self.repo.changectx(rev)
238 hash = self.tohex(nodesha)
239 idnum = self.mapping[hash]
241 ref = self.prefix + name
243 self.write_reset(ref, idnum)
245 def export_repo(self):
246 exported = printed= False
248 for rev in self.repo.changelog:
249 ctx = self.repo.changectx(rev)
250 exported = self.export_revision(ctx) or exported
252 if (exported and not printed) or (exported and rev%1000 == 0):
253 self.progress("Exported revision %d.\n" % rev)
256 def write_marks(self, base):
257 dirname = self.repo.get_base_path(base)
258 path = os.path.join(dirname, 'hg.marks')
259 if not os.path.exists(dirname):
261 f = open(path, 'w') #self.repo.opener(self.marksfile, 'w', atomictemp=True)
263 second = lambda (a, b): b
265 for hash, mark in sorted(self.mapping.iteritems(), key=second):
266 f.write(':%d %s\n' % (mark, hash))
268 f.close() #f.rename()
270 def read_marks(self, base):
271 dirname = self.repo.get_base_path(base)
272 path = os.path.join(dirname, 'hg.marks')
274 if not os.path.exists(path):
275 sys.stderr.write("warning: cannot find " + path)
278 f = open(path) #self.repo.opener(self.marksfile)
280 marks = [i.strip().split(' ') for i in f.readlines()]
282 self.mapping = dict((i[1], int(i[0][1:])) for i in marks)
283 self.next_id = max(self.mapping.values())
285 def setup(self, resume, base, use_options, use_features):
286 self.use_options = use_options
287 self.use_features = use_features
291 self.feature_relative_marks()
292 self.feature_export_marks(self.gitmarksfile)
294 dirname = self.repo.get_base_path(base)
295 path = os.path.abspath(os.path.join(dirname, 'git.marks'))
297 if resume and os.path.exists(path):
298 self.feature_import_marks(self.gitmarksfile)
299 self.read_marks(base)
301 def fast_export(self, base, resume=False, use_options=False, use_features=False):
302 self.setup(resume, base, use_options, use_features)
304 self.progress("Exporting '%s'.\n" % self.repo.path)
308 except KeyboardInterrupt, e:
309 self.progress("Interrupted.")
311 self.write_marks(base)
313 self.progress("Done!\n")