93 |
97 |
94 def read(self): |
98 def read(self): |
95 abspath = os.path.join(self.path, self.transplantfile) |
99 abspath = os.path.join(self.path, self.transplantfile) |
96 if self.transplantfile and os.path.exists(abspath): |
100 if self.transplantfile and os.path.exists(abspath): |
97 for line in self.opener.read(self.transplantfile).splitlines(): |
101 for line in self.opener.read(self.transplantfile).splitlines(): |
98 lnode, rnode = map(revlog.bin, line.split(b':')) |
102 lnode, rnode = map(bin, line.split(b':')) |
99 list = self.transplants.setdefault(rnode, []) |
103 list = self.transplants.setdefault(rnode, []) |
100 list.append(transplantentry(lnode, rnode)) |
104 list.append(transplantentry(lnode, rnode)) |
101 |
105 |
102 def write(self): |
106 def write(self): |
103 if self.dirty and self.transplantfile: |
107 if self.dirty and self.transplantfile: |
104 if not os.path.isdir(self.path): |
108 if not os.path.isdir(self.path): |
105 os.mkdir(self.path) |
109 os.mkdir(self.path) |
106 fp = self.opener(self.transplantfile, b'w') |
110 fp = self.opener(self.transplantfile, b'w') |
107 for list in pycompat.itervalues(self.transplants): |
111 for list in pycompat.itervalues(self.transplants): |
108 for t in list: |
112 for t in list: |
109 l, r = map(nodemod.hex, (t.lnode, t.rnode)) |
113 l, r = map(hex, (t.lnode, t.rnode)) |
110 fp.write(l + b':' + r + b'\n') |
114 fp.write(l + b':' + r + b'\n') |
111 fp.close() |
115 fp.close() |
112 self.dirty = False |
116 self.dirty = False |
113 |
117 |
114 def get(self, rnode): |
118 def get(self, rnode): |
181 try: |
185 try: |
182 lock = repo.lock() |
186 lock = repo.lock() |
183 tr = repo.transaction(b'transplant') |
187 tr = repo.transaction(b'transplant') |
184 for rev in revs: |
188 for rev in revs: |
185 node = revmap[rev] |
189 node = revmap[rev] |
186 revstr = b'%d:%s' % (rev, nodemod.short(node)) |
190 revstr = b'%d:%s' % (rev, short(node)) |
187 |
191 |
188 if self.applied(repo, node, p1): |
192 if self.applied(repo, node, p1): |
189 self.ui.warn( |
193 self.ui.warn( |
190 _(b'skipping already applied revision %s\n') % revstr |
194 _(b'skipping already applied revision %s\n') % revstr |
191 ) |
195 ) |
214 domerge = True |
218 domerge = True |
215 if not hasnode(repo, node): |
219 if not hasnode(repo, node): |
216 exchange.pull(repo, source.peer(), heads=[node]) |
220 exchange.pull(repo, source.peer(), heads=[node]) |
217 |
221 |
218 skipmerge = False |
222 skipmerge = False |
219 if parents[1] != revlog.nullid: |
223 if parents[1] != nullid: |
220 if not opts.get(b'parent'): |
224 if not opts.get(b'parent'): |
221 self.ui.note( |
225 self.ui.note( |
222 _(b'skipping merge changeset %d:%s\n') |
226 _(b'skipping merge changeset %d:%s\n') |
223 % (rev, nodemod.short(node)) |
227 % (rev, short(node)) |
224 ) |
228 ) |
225 skipmerge = True |
229 skipmerge = True |
226 else: |
230 else: |
227 parent = source.lookup(opts[b'parent']) |
231 parent = source.lookup(opts[b'parent']) |
228 if parent not in parents: |
232 if parent not in parents: |
229 raise error.Abort( |
233 raise error.Abort( |
230 _(b'%s is not a parent of %s') |
234 _(b'%s is not a parent of %s') |
231 % (nodemod.short(parent), nodemod.short(node)) |
235 % (short(parent), short(node)) |
232 ) |
236 ) |
233 else: |
237 else: |
234 parent = parents[0] |
238 parent = parents[0] |
235 |
239 |
236 if skipmerge: |
240 if skipmerge: |
261 # fix the merge or cancel everything |
265 # fix the merge or cancel everything |
262 tr.close() |
266 tr.close() |
263 raise |
267 raise |
264 if n and domerge: |
268 if n and domerge: |
265 self.ui.status( |
269 self.ui.status( |
266 _(b'%s merged at %s\n') |
270 _(b'%s merged at %s\n') % (revstr, short(n)) |
267 % (revstr, nodemod.short(n)) |
|
268 ) |
271 ) |
269 elif n: |
272 elif n: |
270 self.ui.status( |
273 self.ui.status( |
271 _(b'%s transplanted to %s\n') |
274 _(b'%s transplanted to %s\n') |
272 % (nodemod.short(node), nodemod.short(n)) |
275 % (short(node), short(n)) |
273 ) |
276 ) |
274 finally: |
277 finally: |
275 if patchfile: |
278 if patchfile: |
276 os.unlink(patchfile) |
279 os.unlink(patchfile) |
277 tr.close() |
280 tr.close() |
307 procutil.shellquote(headerfile), |
310 procutil.shellquote(headerfile), |
308 procutil.shellquote(patchfile), |
311 procutil.shellquote(patchfile), |
309 ), |
312 ), |
310 environ={ |
313 environ={ |
311 b'HGUSER': changelog[1], |
314 b'HGUSER': changelog[1], |
312 b'HGREVISION': nodemod.hex(node), |
315 b'HGREVISION': hex(node), |
313 }, |
316 }, |
314 onerr=error.Abort, |
317 onerr=error.Abort, |
315 errprefix=_(b'filter failed'), |
318 errprefix=_(b'filter failed'), |
316 blockedtag=b'transplant_filter', |
319 blockedtag=b'transplant_filter', |
317 ) |
320 ) |
331 if filter: |
334 if filter: |
332 (user, date, message) = self.filter(filter, node, cl, patchfile) |
335 (user, date, message) = self.filter(filter, node, cl, patchfile) |
333 |
336 |
334 if log: |
337 if log: |
335 # we don't translate messages inserted into commits |
338 # we don't translate messages inserted into commits |
336 message += b'\n(transplanted from %s)' % nodemod.hex(node) |
339 message += b'\n(transplanted from %s)' % hex(node) |
337 |
340 |
338 self.ui.status(_(b'applying %s\n') % nodemod.short(node)) |
341 self.ui.status(_(b'applying %s\n') % short(node)) |
339 self.ui.note(b'%s %s\n%s\n' % (user, date, message)) |
342 self.ui.note(b'%s %s\n%s\n' % (user, date, message)) |
340 |
343 |
341 if not patchfile and not merge: |
344 if not patchfile and not merge: |
342 raise error.Abort(_(b'can only omit patchfile if merging')) |
345 raise error.Abort(_(b'can only omit patchfile if merging')) |
343 if patchfile: |
346 if patchfile: |
393 '''recover last transaction and apply remaining changesets''' |
394 '''recover last transaction and apply remaining changesets''' |
394 if os.path.exists(os.path.join(self.path, b'journal')): |
395 if os.path.exists(os.path.join(self.path, b'journal')): |
395 n, node = self.recover(repo, source, opts) |
396 n, node = self.recover(repo, source, opts) |
396 if n: |
397 if n: |
397 self.ui.status( |
398 self.ui.status( |
398 _(b'%s transplanted as %s\n') |
399 _(b'%s transplanted as %s\n') % (short(node), short(n)) |
399 % (nodemod.short(node), nodemod.short(n)) |
|
400 ) |
400 ) |
401 else: |
401 else: |
402 self.ui.status( |
402 self.ui.status( |
403 _(b'%s skipped due to empty diff\n') |
403 _(b'%s skipped due to empty diff\n') % (short(node),) |
404 % (nodemod.short(node),) |
|
405 ) |
404 ) |
406 seriespath = os.path.join(self.path, b'series') |
405 seriespath = os.path.join(self.path, b'series') |
407 if not os.path.exists(seriespath): |
406 if not os.path.exists(seriespath): |
408 self.transplants.write() |
407 self.transplants.write() |
409 return |
408 return |
428 if opts.get(b'parent'): |
427 if opts.get(b'parent'): |
429 parent = source.lookup(opts[b'parent']) |
428 parent = source.lookup(opts[b'parent']) |
430 if parent not in parents: |
429 if parent not in parents: |
431 raise error.Abort( |
430 raise error.Abort( |
432 _(b'%s is not a parent of %s') |
431 _(b'%s is not a parent of %s') |
433 % (nodemod.short(parent), nodemod.short(node)) |
432 % (short(parent), short(node)) |
434 ) |
433 ) |
435 else: |
434 else: |
436 merge = True |
435 merge = True |
437 |
436 |
438 extra = {b'transplant_source': node} |
437 extra = {b'transplant_source': node} |
439 try: |
438 try: |
440 p1 = repo.dirstate.p1() |
439 p1 = repo.dirstate.p1() |
441 if p1 != parent: |
440 if p1 != parent: |
442 raise error.Abort( |
441 raise error.Abort( |
443 _(b'working directory not at transplant parent %s') |
442 _(b'working directory not at transplant parent %s') |
444 % nodemod.hex(parent) |
443 % hex(parent) |
445 ) |
444 ) |
446 if merge: |
445 if merge: |
447 repo.setparents(p1, parents[1]) |
446 repo.setparents(p1, parents[1]) |
448 st = repo.status() |
447 st = repo.status() |
449 modified, added, removed, deleted = ( |
448 modified, added, removed, deleted = ( |
492 cur = nodes |
491 cur = nodes |
493 for line in self.opener.read(b'series').splitlines(): |
492 for line in self.opener.read(b'series').splitlines(): |
494 if line.startswith(b'# Merges'): |
493 if line.startswith(b'# Merges'): |
495 cur = merges |
494 cur = merges |
496 continue |
495 continue |
497 cur.append(revlog.bin(line)) |
496 cur.append(bin(line)) |
498 |
497 |
499 return (nodes, merges) |
498 return (nodes, merges) |
500 |
499 |
501 def saveseries(self, revmap, merges): |
500 def saveseries(self, revmap, merges): |
502 if not revmap: |
501 if not revmap: |
504 |
503 |
505 if not os.path.isdir(self.path): |
504 if not os.path.isdir(self.path): |
506 os.mkdir(self.path) |
505 os.mkdir(self.path) |
507 series = self.opener(b'series', b'w') |
506 series = self.opener(b'series', b'w') |
508 for rev in sorted(revmap): |
507 for rev in sorted(revmap): |
509 series.write(nodemod.hex(revmap[rev]) + b'\n') |
508 series.write(hex(revmap[rev]) + b'\n') |
510 if merges: |
509 if merges: |
511 series.write(b'# Merges\n') |
510 series.write(b'# Merges\n') |
512 for m in merges: |
511 for m in merges: |
513 series.write(nodemod.hex(m) + b'\n') |
512 series.write(hex(m) + b'\n') |
514 series.close() |
513 series.close() |
515 |
514 |
516 def parselog(self, fp): |
515 def parselog(self, fp): |
517 parents = [] |
516 parents = [] |
518 message = [] |
517 message = [] |
519 node = revlog.nullid |
518 node = nullid |
520 inmsg = False |
519 inmsg = False |
521 user = None |
520 user = None |
522 date = None |
521 date = None |
523 for line in fp.read().splitlines(): |
522 for line in fp.read().splitlines(): |
524 if inmsg: |
523 if inmsg: |
526 elif line.startswith(b'# User '): |
525 elif line.startswith(b'# User '): |
527 user = line[7:] |
526 user = line[7:] |
528 elif line.startswith(b'# Date '): |
527 elif line.startswith(b'# Date '): |
529 date = line[7:] |
528 date = line[7:] |
530 elif line.startswith(b'# Node ID '): |
529 elif line.startswith(b'# Node ID '): |
531 node = revlog.bin(line[10:]) |
530 node = bin(line[10:]) |
532 elif line.startswith(b'# Parent '): |
531 elif line.startswith(b'# Parent '): |
533 parents.append(revlog.bin(line[9:])) |
532 parents.append(bin(line[9:])) |
534 elif not line.startswith(b'# '): |
533 elif not line.startswith(b'# '): |
535 inmsg = True |
534 inmsg = True |
536 message.append(line) |
535 message.append(line) |
537 if None in (user, date): |
536 if None in (user, date): |
538 raise error.Abort( |
537 raise error.Abort( |
546 if not os.path.isdir(self.path): |
545 if not os.path.isdir(self.path): |
547 os.mkdir(self.path) |
546 os.mkdir(self.path) |
548 fp = self.opener(b'journal', b'w') |
547 fp = self.opener(b'journal', b'w') |
549 fp.write(b'# User %s\n' % user) |
548 fp.write(b'# User %s\n' % user) |
550 fp.write(b'# Date %s\n' % date) |
549 fp.write(b'# Date %s\n' % date) |
551 fp.write(b'# Node ID %s\n' % nodemod.hex(p2)) |
550 fp.write(b'# Node ID %s\n' % hex(p2)) |
552 fp.write(b'# Parent ' + nodemod.hex(p1) + b'\n') |
551 fp.write(b'# Parent ' + hex(p1) + b'\n') |
553 if merge: |
552 if merge: |
554 fp.write(b'# Parent ' + nodemod.hex(p2) + b'\n') |
553 fp.write(b'# Parent ' + hex(p2) + b'\n') |
555 fp.write(message.rstrip() + b'\n') |
554 fp.write(message.rstrip() + b'\n') |
556 fp.close() |
555 fp.close() |
557 |
556 |
558 def readlog(self): |
557 def readlog(self): |
559 return self.parselog(self.opener(b'journal')) |
558 return self.parselog(self.opener(b'journal')) |
566 |
565 |
567 def transplantfilter(self, repo, source, root): |
566 def transplantfilter(self, repo, source, root): |
568 def matchfn(node): |
567 def matchfn(node): |
569 if self.applied(repo, node, root): |
568 if self.applied(repo, node, root): |
570 return False |
569 return False |
571 if source.changelog.parents(node)[1] != revlog.nullid: |
570 if source.changelog.parents(node)[1] != nullid: |
572 return False |
571 return False |
573 extra = source.changelog.read(node)[5] |
572 extra = source.changelog.read(node)[5] |
574 cnode = extra.get(b'transplant_source') |
573 cnode = extra.get(b'transplant_source') |
575 if cnode and self.applied(repo, cnode, root): |
574 if cnode and self.applied(repo, cnode, root): |
576 return False |
575 return False |
802 opts[b'filter'] = ui.config(b'transplant', b'filter') |
801 opts[b'filter'] = ui.config(b'transplant', b'filter') |
803 |
802 |
804 tp = transplanter(ui, repo, opts) |
803 tp = transplanter(ui, repo, opts) |
805 |
804 |
806 p1 = repo.dirstate.p1() |
805 p1 = repo.dirstate.p1() |
807 if len(repo) > 0 and p1 == revlog.nullid: |
806 if len(repo) > 0 and p1 == nullid: |
808 raise error.Abort(_(b'no revision checked out')) |
807 raise error.Abort(_(b'no revision checked out')) |
809 if opts.get(b'continue'): |
808 if opts.get(b'continue'): |
810 if not tp.canresume(): |
809 if not tp.canresume(): |
811 raise error.StateError(_(b'no transplant to continue')) |
810 raise error.StateError(_(b'no transplant to continue')) |
812 elif opts.get(b'stop'): |
811 elif opts.get(b'stop'): |
907 def kwtransplanted(context, mapping): |
906 def kwtransplanted(context, mapping): |
908 """String. The node identifier of the transplanted |
907 """String. The node identifier of the transplanted |
909 changeset if any.""" |
908 changeset if any.""" |
910 ctx = context.resource(mapping, b'ctx') |
909 ctx = context.resource(mapping, b'ctx') |
911 n = ctx.extra().get(b'transplant_source') |
910 n = ctx.extra().get(b'transplant_source') |
912 return n and nodemod.hex(n) or b'' |
911 return n and hex(n) or b'' |
913 |
912 |
914 |
913 |
915 def extsetup(ui): |
914 def extsetup(ui): |
916 statemod.addunfinished( |
915 statemod.addunfinished( |
917 b'transplant', |
916 b'transplant', |