##// END OF EJS Templates
transplant: added support for --stop flag...
Taapas Agrawal -
r43030:ac6121a2 default
parent child Browse files
Show More
@@ -1,777 +1,798 b''
1 1 # Patch transplanting extension for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''command to transplant changesets from another branch
9 9
10 10 This extension allows you to transplant changes to another parent revision,
11 11 possibly in another repository. The transplant is done using 'diff' patches.
12 12
13 13 Transplanted patches are recorded in .hg/transplant/transplants, as a
14 14 map from a changeset hash to its hash in the source repository.
15 15 '''
16 16 from __future__ import absolute_import
17 17
18 18 import os
19 19
20 20 from mercurial.i18n import _
21 21 from mercurial import (
22 22 bundlerepo,
23 23 cmdutil,
24 24 error,
25 25 exchange,
26 26 hg,
27 27 logcmdutil,
28 28 match,
29 29 merge,
30 30 node as nodemod,
31 31 patch,
32 32 pycompat,
33 33 registrar,
34 34 revlog,
35 35 revset,
36 36 scmutil,
37 37 smartset,
38 38 state as statemod,
39 39 util,
40 40 vfs as vfsmod,
41 41 )
42 42 from mercurial.utils import (
43 43 procutil,
44 44 stringutil,
45 45 )
46 46
47 47 class TransplantError(error.Abort):
48 48 pass
49 49
50 50 cmdtable = {}
51 51 command = registrar.command(cmdtable)
52 52 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
53 53 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
54 54 # be specifying the version(s) of Mercurial they are tested with, or
55 55 # leave the attribute unspecified.
56 56 testedwith = 'ships-with-hg-core'
57 57
58 58 configtable = {}
59 59 configitem = registrar.configitem(configtable)
60 60
61 61 configitem('transplant', 'filter',
62 62 default=None,
63 63 )
64 64 configitem('transplant', 'log',
65 65 default=None,
66 66 )
67 67
68 68 class transplantentry(object):
69 69 def __init__(self, lnode, rnode):
70 70 self.lnode = lnode
71 71 self.rnode = rnode
72 72
73 73 class transplants(object):
74 74 def __init__(self, path=None, transplantfile=None, opener=None):
75 75 self.path = path
76 76 self.transplantfile = transplantfile
77 77 self.opener = opener
78 78
79 79 if not opener:
80 80 self.opener = vfsmod.vfs(self.path)
81 81 self.transplants = {}
82 82 self.dirty = False
83 83 self.read()
84 84
85 85 def read(self):
86 86 abspath = os.path.join(self.path, self.transplantfile)
87 87 if self.transplantfile and os.path.exists(abspath):
88 88 for line in self.opener.read(self.transplantfile).splitlines():
89 89 lnode, rnode = map(revlog.bin, line.split(':'))
90 90 list = self.transplants.setdefault(rnode, [])
91 91 list.append(transplantentry(lnode, rnode))
92 92
93 93 def write(self):
94 94 if self.dirty and self.transplantfile:
95 95 if not os.path.isdir(self.path):
96 96 os.mkdir(self.path)
97 97 fp = self.opener(self.transplantfile, 'w')
98 98 for list in self.transplants.itervalues():
99 99 for t in list:
100 100 l, r = map(nodemod.hex, (t.lnode, t.rnode))
101 101 fp.write(l + ':' + r + '\n')
102 102 fp.close()
103 103 self.dirty = False
104 104
105 105 def get(self, rnode):
106 106 return self.transplants.get(rnode) or []
107 107
108 108 def set(self, lnode, rnode):
109 109 list = self.transplants.setdefault(rnode, [])
110 110 list.append(transplantentry(lnode, rnode))
111 111 self.dirty = True
112 112
113 113 def remove(self, transplant):
114 114 list = self.transplants.get(transplant.rnode)
115 115 if list:
116 116 del list[list.index(transplant)]
117 117 self.dirty = True
118 118
119 119 class transplanter(object):
120 120 def __init__(self, ui, repo, opts):
121 121 self.ui = ui
122 122 self.path = repo.vfs.join('transplant')
123 123 self.opener = vfsmod.vfs(self.path)
124 124 self.transplants = transplants(self.path, 'transplants',
125 125 opener=self.opener)
126 126 def getcommiteditor():
127 127 editform = cmdutil.mergeeditform(repo[None], 'transplant')
128 128 return cmdutil.getcommiteditor(editform=editform,
129 129 **pycompat.strkwargs(opts))
130 130 self.getcommiteditor = getcommiteditor
131 131
132 132 def applied(self, repo, node, parent):
133 133 '''returns True if a node is already an ancestor of parent
134 134 or is parent or has already been transplanted'''
135 135 if hasnode(repo, parent):
136 136 parentrev = repo.changelog.rev(parent)
137 137 if hasnode(repo, node):
138 138 rev = repo.changelog.rev(node)
139 139 reachable = repo.changelog.ancestors([parentrev], rev,
140 140 inclusive=True)
141 141 if rev in reachable:
142 142 return True
143 143 for t in self.transplants.get(node):
144 144 # it might have been stripped
145 145 if not hasnode(repo, t.lnode):
146 146 self.transplants.remove(t)
147 147 return False
148 148 lnoderev = repo.changelog.rev(t.lnode)
149 149 if lnoderev in repo.changelog.ancestors([parentrev], lnoderev,
150 150 inclusive=True):
151 151 return True
152 152 return False
153 153
154 154 def apply(self, repo, source, revmap, merges, opts=None):
155 155 '''apply the revisions in revmap one by one in revision order'''
156 156 if opts is None:
157 157 opts = {}
158 158 revs = sorted(revmap)
159 159 p1 = repo.dirstate.p1()
160 160 pulls = []
161 161 diffopts = patch.difffeatureopts(self.ui, opts)
162 162 diffopts.git = True
163 163
164 164 lock = tr = None
165 165 try:
166 166 lock = repo.lock()
167 167 tr = repo.transaction('transplant')
168 168 for rev in revs:
169 169 node = revmap[rev]
170 170 revstr = '%d:%s' % (rev, nodemod.short(node))
171 171
172 172 if self.applied(repo, node, p1):
173 173 self.ui.warn(_('skipping already applied revision %s\n') %
174 174 revstr)
175 175 continue
176 176
177 177 parents = source.changelog.parents(node)
178 178 if not (opts.get('filter') or opts.get('log')):
179 179 # If the changeset parent is the same as the
180 180 # wdir's parent, just pull it.
181 181 if parents[0] == p1:
182 182 pulls.append(node)
183 183 p1 = node
184 184 continue
185 185 if pulls:
186 186 if source != repo:
187 187 exchange.pull(repo, source.peer(), heads=pulls)
188 188 merge.update(repo, pulls[-1], branchmerge=False,
189 189 force=False)
190 190 p1 = repo.dirstate.p1()
191 191 pulls = []
192 192
193 193 domerge = False
194 194 if node in merges:
195 195 # pulling all the merge revs at once would mean we
196 196 # couldn't transplant after the latest even if
197 197 # transplants before them fail.
198 198 domerge = True
199 199 if not hasnode(repo, node):
200 200 exchange.pull(repo, source.peer(), heads=[node])
201 201
202 202 skipmerge = False
203 203 if parents[1] != revlog.nullid:
204 204 if not opts.get('parent'):
205 205 self.ui.note(_('skipping merge changeset %d:%s\n')
206 206 % (rev, nodemod.short(node)))
207 207 skipmerge = True
208 208 else:
209 209 parent = source.lookup(opts['parent'])
210 210 if parent not in parents:
211 211 raise error.Abort(_('%s is not a parent of %s') %
212 212 (nodemod.short(parent),
213 213 nodemod.short(node)))
214 214 else:
215 215 parent = parents[0]
216 216
217 217 if skipmerge:
218 218 patchfile = None
219 219 else:
220 220 fd, patchfile = pycompat.mkstemp(prefix='hg-transplant-')
221 221 fp = os.fdopen(fd, r'wb')
222 222 gen = patch.diff(source, parent, node, opts=diffopts)
223 223 for chunk in gen:
224 224 fp.write(chunk)
225 225 fp.close()
226 226
227 227 del revmap[rev]
228 228 if patchfile or domerge:
229 229 try:
230 230 try:
231 231 n = self.applyone(repo, node,
232 232 source.changelog.read(node),
233 233 patchfile, merge=domerge,
234 234 log=opts.get('log'),
235 235 filter=opts.get('filter'))
236 236 except TransplantError:
237 237 # Do not rollback, it is up to the user to
238 238 # fix the merge or cancel everything
239 239 tr.close()
240 240 raise
241 241 if n and domerge:
242 242 self.ui.status(_('%s merged at %s\n') % (revstr,
243 243 nodemod.short(n)))
244 244 elif n:
245 245 self.ui.status(_('%s transplanted to %s\n')
246 246 % (nodemod.short(node),
247 247 nodemod.short(n)))
248 248 finally:
249 249 if patchfile:
250 250 os.unlink(patchfile)
251 251 tr.close()
252 252 if pulls:
253 253 exchange.pull(repo, source.peer(), heads=pulls)
254 254 merge.update(repo, pulls[-1], branchmerge=False, force=False)
255 255 finally:
256 256 self.saveseries(revmap, merges)
257 257 self.transplants.write()
258 258 if tr:
259 259 tr.release()
260 260 if lock:
261 261 lock.release()
262 262
263 263 def filter(self, filter, node, changelog, patchfile):
264 264 '''arbitrarily rewrite changeset before applying it'''
265 265
266 266 self.ui.status(_('filtering %s\n') % patchfile)
267 267 user, date, msg = (changelog[1], changelog[2], changelog[4])
268 268 fd, headerfile = pycompat.mkstemp(prefix='hg-transplant-')
269 269 fp = os.fdopen(fd, r'wb')
270 270 fp.write("# HG changeset patch\n")
271 271 fp.write("# User %s\n" % user)
272 272 fp.write("# Date %d %d\n" % date)
273 273 fp.write(msg + '\n')
274 274 fp.close()
275 275
276 276 try:
277 277 self.ui.system('%s %s %s' % (filter,
278 278 procutil.shellquote(headerfile),
279 279 procutil.shellquote(patchfile)),
280 280 environ={'HGUSER': changelog[1],
281 281 'HGREVISION': nodemod.hex(node),
282 282 },
283 283 onerr=error.Abort, errprefix=_('filter failed'),
284 284 blockedtag='transplant_filter')
285 285 user, date, msg = self.parselog(open(headerfile, 'rb'))[1:4]
286 286 finally:
287 287 os.unlink(headerfile)
288 288
289 289 return (user, date, msg)
290 290
291 291 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
292 292 filter=None):
293 293 '''apply the patch in patchfile to the repository as a transplant'''
294 294 (manifest, user, (time, timezone), files, message) = cl[:5]
295 295 date = "%d %d" % (time, timezone)
296 296 extra = {'transplant_source': node}
297 297 if filter:
298 298 (user, date, message) = self.filter(filter, node, cl, patchfile)
299 299
300 300 if log:
301 301 # we don't translate messages inserted into commits
302 302 message += '\n(transplanted from %s)' % nodemod.hex(node)
303 303
304 304 self.ui.status(_('applying %s\n') % nodemod.short(node))
305 305 self.ui.note('%s %s\n%s\n' % (user, date, message))
306 306
307 307 if not patchfile and not merge:
308 308 raise error.Abort(_('can only omit patchfile if merging'))
309 309 if patchfile:
310 310 try:
311 311 files = set()
312 312 patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
313 313 files = list(files)
314 314 except Exception as inst:
315 315 seriespath = os.path.join(self.path, 'series')
316 316 if os.path.exists(seriespath):
317 317 os.unlink(seriespath)
318 318 p1 = repo.dirstate.p1()
319 319 p2 = node
320 320 self.log(user, date, message, p1, p2, merge=merge)
321 321 self.ui.write(stringutil.forcebytestr(inst) + '\n')
322 322 raise TransplantError(_('fix up the working directory and run '
323 323 'hg transplant --continue'))
324 324 else:
325 325 files = None
326 326 if merge:
327 327 p1 = repo.dirstate.p1()
328 328 repo.setparents(p1, node)
329 329 m = match.always()
330 330 else:
331 331 m = match.exact(files)
332 332
333 333 n = repo.commit(message, user, date, extra=extra, match=m,
334 334 editor=self.getcommiteditor())
335 335 if not n:
336 336 self.ui.warn(_('skipping emptied changeset %s\n') %
337 337 nodemod.short(node))
338 338 return None
339 339 if not merge:
340 340 self.transplants.set(n, node)
341 341
342 342 return n
343 343
344 344 def canresume(self):
345 345 return os.path.exists(os.path.join(self.path, 'journal'))
346 346
347 347 def resume(self, repo, source, opts):
348 348 '''recover last transaction and apply remaining changesets'''
349 349 if os.path.exists(os.path.join(self.path, 'journal')):
350 350 n, node = self.recover(repo, source, opts)
351 351 if n:
352 352 self.ui.status(_('%s transplanted as %s\n') %
353 353 (nodemod.short(node),
354 354 nodemod.short(n)))
355 355 else:
356 356 self.ui.status(_('%s skipped due to empty diff\n')
357 357 % (nodemod.short(node),))
358 358 seriespath = os.path.join(self.path, 'series')
359 359 if not os.path.exists(seriespath):
360 360 self.transplants.write()
361 361 return
362 362 nodes, merges = self.readseries()
363 363 revmap = {}
364 364 for n in nodes:
365 365 revmap[source.changelog.rev(n)] = n
366 366 os.unlink(seriespath)
367 367
368 368 self.apply(repo, source, revmap, merges, opts)
369 369
370 370 def recover(self, repo, source, opts):
371 371 '''commit working directory using journal metadata'''
372 372 node, user, date, message, parents = self.readlog()
373 373 merge = False
374 374
375 375 if not user or not date or not message or not parents[0]:
376 376 raise error.Abort(_('transplant log file is corrupt'))
377 377
378 378 parent = parents[0]
379 379 if len(parents) > 1:
380 380 if opts.get('parent'):
381 381 parent = source.lookup(opts['parent'])
382 382 if parent not in parents:
383 383 raise error.Abort(_('%s is not a parent of %s') %
384 384 (nodemod.short(parent),
385 385 nodemod.short(node)))
386 386 else:
387 387 merge = True
388 388
389 389 extra = {'transplant_source': node}
390 390 try:
391 391 p1 = repo.dirstate.p1()
392 392 if p1 != parent:
393 393 raise error.Abort(_('working directory not at transplant '
394 394 'parent %s') % nodemod.hex(parent))
395 395 if merge:
396 396 repo.setparents(p1, parents[1])
397 397 modified, added, removed, deleted = repo.status()[:4]
398 398 if merge or modified or added or removed or deleted:
399 399 n = repo.commit(message, user, date, extra=extra,
400 400 editor=self.getcommiteditor())
401 401 if not n:
402 402 raise error.Abort(_('commit failed'))
403 403 if not merge:
404 404 self.transplants.set(n, node)
405 405 else:
406 406 n = None
407 407 self.unlog()
408 408
409 409 return n, node
410 410 finally:
411 411 # TODO: get rid of this meaningless try/finally enclosing.
412 412 # this is kept only to reduce changes in a patch.
413 413 pass
414 414
415 def stop(self, ui, repo):
416 """logic to stop an interrupted transplant"""
417 if self.canresume():
418 startctx = repo['.']
419 hg.updaterepo(repo, startctx.node(), overwrite=True)
420 ui.status(_("stopped the interrupted transplant\n"))
421 ui.status(_("working directory is now at %s\n") %
422 startctx.hex()[:12])
423 self.unlog()
424 return 0
425
415 426 def readseries(self):
416 427 nodes = []
417 428 merges = []
418 429 cur = nodes
419 430 for line in self.opener.read('series').splitlines():
420 431 if line.startswith('# Merges'):
421 432 cur = merges
422 433 continue
423 434 cur.append(revlog.bin(line))
424 435
425 436 return (nodes, merges)
426 437
427 438 def saveseries(self, revmap, merges):
428 439 if not revmap:
429 440 return
430 441
431 442 if not os.path.isdir(self.path):
432 443 os.mkdir(self.path)
433 444 series = self.opener('series', 'w')
434 445 for rev in sorted(revmap):
435 446 series.write(nodemod.hex(revmap[rev]) + '\n')
436 447 if merges:
437 448 series.write('# Merges\n')
438 449 for m in merges:
439 450 series.write(nodemod.hex(m) + '\n')
440 451 series.close()
441 452
442 453 def parselog(self, fp):
443 454 parents = []
444 455 message = []
445 456 node = revlog.nullid
446 457 inmsg = False
447 458 user = None
448 459 date = None
449 460 for line in fp.read().splitlines():
450 461 if inmsg:
451 462 message.append(line)
452 463 elif line.startswith('# User '):
453 464 user = line[7:]
454 465 elif line.startswith('# Date '):
455 466 date = line[7:]
456 467 elif line.startswith('# Node ID '):
457 468 node = revlog.bin(line[10:])
458 469 elif line.startswith('# Parent '):
459 470 parents.append(revlog.bin(line[9:]))
460 471 elif not line.startswith('# '):
461 472 inmsg = True
462 473 message.append(line)
463 474 if None in (user, date):
464 475 raise error.Abort(_("filter corrupted changeset (no user or date)"))
465 476 return (node, user, date, '\n'.join(message), parents)
466 477
467 478 def log(self, user, date, message, p1, p2, merge=False):
468 479 '''journal changelog metadata for later recover'''
469 480
470 481 if not os.path.isdir(self.path):
471 482 os.mkdir(self.path)
472 483 fp = self.opener('journal', 'w')
473 484 fp.write('# User %s\n' % user)
474 485 fp.write('# Date %s\n' % date)
475 486 fp.write('# Node ID %s\n' % nodemod.hex(p2))
476 487 fp.write('# Parent ' + nodemod.hex(p1) + '\n')
477 488 if merge:
478 489 fp.write('# Parent ' + nodemod.hex(p2) + '\n')
479 490 fp.write(message.rstrip() + '\n')
480 491 fp.close()
481 492
482 493 def readlog(self):
483 494 return self.parselog(self.opener('journal'))
484 495
485 496 def unlog(self):
486 497 '''remove changelog journal'''
487 498 absdst = os.path.join(self.path, 'journal')
488 499 if os.path.exists(absdst):
489 500 os.unlink(absdst)
490 501
491 502 def transplantfilter(self, repo, source, root):
492 503 def matchfn(node):
493 504 if self.applied(repo, node, root):
494 505 return False
495 506 if source.changelog.parents(node)[1] != revlog.nullid:
496 507 return False
497 508 extra = source.changelog.read(node)[5]
498 509 cnode = extra.get('transplant_source')
499 510 if cnode and self.applied(repo, cnode, root):
500 511 return False
501 512 return True
502 513
503 514 return matchfn
504 515
505 516 def hasnode(repo, node):
506 517 try:
507 518 return repo.changelog.rev(node) is not None
508 519 except error.StorageError:
509 520 return False
510 521
511 522 def browserevs(ui, repo, nodes, opts):
512 523 '''interactively transplant changesets'''
513 524 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
514 525 transplants = []
515 526 merges = []
516 527 prompt = _('apply changeset? [ynmpcq?]:'
517 528 '$$ &yes, transplant this changeset'
518 529 '$$ &no, skip this changeset'
519 530 '$$ &merge at this changeset'
520 531 '$$ show &patch'
521 532 '$$ &commit selected changesets'
522 533 '$$ &quit and cancel transplant'
523 534 '$$ &? (show this help)')
524 535 for node in nodes:
525 536 displayer.show(repo[node])
526 537 action = None
527 538 while not action:
528 539 choice = ui.promptchoice(prompt)
529 540 action = 'ynmpcq?'[choice:choice + 1]
530 541 if action == '?':
531 542 for c, t in ui.extractchoices(prompt)[1]:
532 543 ui.write('%s: %s\n' % (c, t))
533 544 action = None
534 545 elif action == 'p':
535 546 parent = repo.changelog.parents(node)[0]
536 547 for chunk in patch.diff(repo, parent, node):
537 548 ui.write(chunk)
538 549 action = None
539 550 if action == 'y':
540 551 transplants.append(node)
541 552 elif action == 'm':
542 553 merges.append(node)
543 554 elif action == 'c':
544 555 break
545 556 elif action == 'q':
546 557 transplants = ()
547 558 merges = ()
548 559 break
549 560 displayer.close()
550 561 return (transplants, merges)
551 562
552 563 @command('transplant',
553 564 [('s', 'source', '', _('transplant changesets from REPO'), _('REPO')),
554 565 ('b', 'branch', [], _('use this source changeset as head'), _('REV')),
555 566 ('a', 'all', None, _('pull all changesets up to the --branch revisions')),
556 567 ('p', 'prune', [], _('skip over REV'), _('REV')),
557 568 ('m', 'merge', [], _('merge at REV'), _('REV')),
558 569 ('', 'parent', '',
559 570 _('parent to choose when transplanting merge'), _('REV')),
560 571 ('e', 'edit', False, _('invoke editor on commit messages')),
561 572 ('', 'log', None, _('append transplant info to log message')),
573 ('', 'stop', False, _('stop interrupted transplant')),
562 574 ('c', 'continue', None, _('continue last transplant session '
563 575 'after fixing conflicts')),
564 576 ('', 'filter', '',
565 577 _('filter changesets through command'), _('CMD'))],
566 578 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
567 579 '[-m REV] [REV]...'),
568 580 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
569 581 def transplant(ui, repo, *revs, **opts):
570 582 '''transplant changesets from another branch
571 583
572 584 Selected changesets will be applied on top of the current working
573 585 directory with the log of the original changeset. The changesets
574 586 are copied and will thus appear twice in the history with different
575 587 identities.
576 588
577 589 Consider using the graft command if everything is inside the same
578 590 repository - it will use merges and will usually give a better result.
579 591 Use the rebase extension if the changesets are unpublished and you want
580 592 to move them instead of copying them.
581 593
582 594 If --log is specified, log messages will have a comment appended
583 595 of the form::
584 596
585 597 (transplanted from CHANGESETHASH)
586 598
587 599 You can rewrite the changelog message with the --filter option.
588 600 Its argument will be invoked with the current changelog message as
589 601 $1 and the patch as $2.
590 602
591 603 --source/-s specifies another repository to use for selecting changesets,
592 604 just as if it temporarily had been pulled.
593 605 If --branch/-b is specified, these revisions will be used as
594 606 heads when deciding which changesets to transplant, just as if only
595 607 these revisions had been pulled.
596 608 If --all/-a is specified, all the revisions up to the heads specified
597 609 with --branch will be transplanted.
598 610
599 611 Example:
600 612
601 613 - transplant all changes up to REV on top of your current revision::
602 614
603 615 hg transplant --branch REV --all
604 616
605 617 You can optionally mark selected transplanted changesets as merge
606 618 changesets. You will not be prompted to transplant any ancestors
607 619 of a merged transplant, and you can merge descendants of them
608 620 normally instead of transplanting them.
609 621
610 622 Merge changesets may be transplanted directly by specifying the
611 623 proper parent changeset by calling :hg:`transplant --parent`.
612 624
613 625 If no merges or revisions are provided, :hg:`transplant` will
614 626 start an interactive changeset browser.
615 627
616 628 If a changeset application fails, you can fix the merge by hand
617 629 and then resume where you left off by calling :hg:`transplant
618 630 --continue/-c`.
619 631 '''
620 632 with repo.wlock():
621 633 return _dotransplant(ui, repo, *revs, **opts)
622 634
623 635 def _dotransplant(ui, repo, *revs, **opts):
624 636 def incwalk(repo, csets, match=util.always):
625 637 for node in csets:
626 638 if match(node):
627 639 yield node
628 640
629 641 def transplantwalk(repo, dest, heads, match=util.always):
630 642 '''Yield all nodes that are ancestors of a head but not ancestors
631 643 of dest.
632 644 If no heads are specified, the heads of repo will be used.'''
633 645 if not heads:
634 646 heads = repo.heads()
635 647 ancestors = []
636 648 ctx = repo[dest]
637 649 for head in heads:
638 650 ancestors.append(ctx.ancestor(repo[head]).node())
639 651 for node in repo.changelog.nodesbetween(ancestors, heads)[0]:
640 652 if match(node):
641 653 yield node
642 654
643 655 def checkopts(opts, revs):
644 656 if opts.get('continue'):
645 657 if opts.get('branch') or opts.get('all') or opts.get('merge'):
646 658 raise error.Abort(_('--continue is incompatible with '
647 659 '--branch, --all and --merge'))
648 660 return
661 if opts.get('stop'):
662 if opts.get('branch') or opts.get('all') or opts.get('merge'):
663 raise error.Abort(_('--stop is incompatible with '
664 '--branch, --all and --merge'))
665 return
649 666 if not (opts.get('source') or revs or
650 667 opts.get('merge') or opts.get('branch')):
651 668 raise error.Abort(_('no source URL, branch revision, or revision '
652 669 'list provided'))
653 670 if opts.get('all'):
654 671 if not opts.get('branch'):
655 672 raise error.Abort(_('--all requires a branch revision'))
656 673 if revs:
657 674 raise error.Abort(_('--all is incompatible with a '
658 675 'revision list'))
659 676
660 677 opts = pycompat.byteskwargs(opts)
661 678 checkopts(opts, revs)
662 679
663 680 if not opts.get('log'):
664 681 # deprecated config: transplant.log
665 682 opts['log'] = ui.config('transplant', 'log')
666 683 if not opts.get('filter'):
667 684 # deprecated config: transplant.filter
668 685 opts['filter'] = ui.config('transplant', 'filter')
669 686
670 687 tp = transplanter(ui, repo, opts)
671 688
672 689 p1 = repo.dirstate.p1()
673 690 if len(repo) > 0 and p1 == revlog.nullid:
674 691 raise error.Abort(_('no revision checked out'))
675 692 if opts.get('continue'):
676 693 if not tp.canresume():
677 694 raise error.Abort(_('no transplant to continue'))
678 695 else:
696 if opts.get('stop'):
697 if not tp.canresume():
698 raise error.Abort(_('no interrupted transplant found'))
699 return tp.stop(ui, repo)
679 700 cmdutil.checkunfinished(repo)
680 701 cmdutil.bailifchanged(repo)
681 702
682 703 sourcerepo = opts.get('source')
683 704 if sourcerepo:
684 705 peer = hg.peer(repo, opts, ui.expandpath(sourcerepo))
685 706 heads = pycompat.maplist(peer.lookup, opts.get('branch', ()))
686 707 target = set(heads)
687 708 for r in revs:
688 709 try:
689 710 target.add(peer.lookup(r))
690 711 except error.RepoError:
691 712 pass
692 713 source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer,
693 714 onlyheads=sorted(target), force=True)
694 715 else:
695 716 source = repo
696 717 heads = pycompat.maplist(source.lookup, opts.get('branch', ()))
697 718 cleanupfn = None
698 719
699 720 try:
700 721 if opts.get('continue'):
701 722 tp.resume(repo, source, opts)
702 723 return
703 724
704 725 tf = tp.transplantfilter(repo, source, p1)
705 726 if opts.get('prune'):
706 727 prune = set(source[r].node()
707 728 for r in scmutil.revrange(source, opts.get('prune')))
708 729 matchfn = lambda x: tf(x) and x not in prune
709 730 else:
710 731 matchfn = tf
711 732 merges = pycompat.maplist(source.lookup, opts.get('merge', ()))
712 733 revmap = {}
713 734 if revs:
714 735 for r in scmutil.revrange(source, revs):
715 736 revmap[int(r)] = source[r].node()
716 737 elif opts.get('all') or not merges:
717 738 if source != repo:
718 739 alltransplants = incwalk(source, csets, match=matchfn)
719 740 else:
720 741 alltransplants = transplantwalk(source, p1, heads,
721 742 match=matchfn)
722 743 if opts.get('all'):
723 744 revs = alltransplants
724 745 else:
725 746 revs, newmerges = browserevs(ui, source, alltransplants, opts)
726 747 merges.extend(newmerges)
727 748 for r in revs:
728 749 revmap[source.changelog.rev(r)] = r
729 750 for r in merges:
730 751 revmap[source.changelog.rev(r)] = r
731 752
732 753 tp.apply(repo, source, revmap, merges, opts)
733 754 finally:
734 755 if cleanupfn:
735 756 cleanupfn()
736 757
737 758 def continuecmd(ui, repo):
738 759 """logic to resume an interrupted transplant using
739 760 'hg continue'"""
740 761 with repo.wlock():
741 762 tp = transplanter(ui, repo, {})
742 763 return tp.resume(repo, repo, {})
743 764
744 765 revsetpredicate = registrar.revsetpredicate()
745 766
746 767 @revsetpredicate('transplanted([set])')
747 768 def revsettransplanted(repo, subset, x):
748 769 """Transplanted changesets in set, or all transplanted changesets.
749 770 """
750 771 if x:
751 772 s = revset.getset(repo, subset, x)
752 773 else:
753 774 s = subset
754 775 return smartset.baseset([r for r in s if
755 776 repo[r].extra().get('transplant_source')])
756 777
757 778 templatekeyword = registrar.templatekeyword()
758 779
759 780 @templatekeyword('transplanted', requires={'ctx'})
760 781 def kwtransplanted(context, mapping):
761 782 """String. The node identifier of the transplanted
762 783 changeset if any."""
763 784 ctx = context.resource(mapping, 'ctx')
764 785 n = ctx.extra().get('transplant_source')
765 786 return n and nodemod.hex(n) or ''
766 787
767 788 def extsetup(ui):
768 789 statemod.addunfinished (
769 790 'transplant', fname='transplant/journal', clearable=True,
770 791 continuefunc=continuecmd,
771 792 statushint=_('To continue: hg transplant --continue\n'
772 'To abort: hg update'),
773 cmdhint=_("use 'hg transplant --continue' or 'hg update' to abort")
793 'To stop: hg transplant --stop'),
794 cmdhint=_("use 'hg transplant --continue' or 'hg transplant --stop'")
774 795 )
775 796
776 797 # tell hggettext to extract docstrings from these functions:
777 798 i18nfunctions = [revsettransplanted, kwtransplanted]
@@ -1,1026 +1,1107 b''
1 1 #testcases commandmode continueflag
2 2 $ cat <<EOF >> $HGRCPATH
3 3 > [extensions]
4 4 > transplant=
5 > graphlog=
5 6 > EOF
6 7
7 8 #if continueflag
8 9 $ cat >> $HGRCPATH <<EOF
9 10 > [alias]
10 11 > continue = transplant --continue
11 12 > EOF
12 13 #endif
13 14
14 15 $ hg init t
15 16 $ cd t
16 17 $ hg transplant
17 18 abort: no source URL, branch revision, or revision list provided
18 19 [255]
19 20 $ hg transplant --continue --all
20 21 abort: --continue is incompatible with --branch, --all and --merge
21 22 [255]
23 $ hg transplant --stop --all
24 abort: --stop is incompatible with --branch, --all and --merge
25 [255]
22 26 $ hg transplant --all tip
23 27 abort: --all requires a branch revision
24 28 [255]
25 29 $ hg transplant --all --branch default tip
26 30 abort: --all is incompatible with a revision list
27 31 [255]
28 32 $ echo r1 > r1
29 33 $ hg ci -Amr1 -d'0 0'
30 34 adding r1
31 35 $ hg co -q null
32 36 $ hg transplant tip
33 37 abort: no revision checked out
34 38 [255]
35 39 $ hg up -q
36 40 $ echo r2 > r2
37 41 $ hg ci -Amr2 -d'1 0'
38 42 adding r2
39 43 $ hg up 0
40 44 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
41 45
42 46 $ echo b1 > b1
43 47 $ hg ci -Amb1 -d '0 0'
44 48 adding b1
45 49 created new head
46 50 $ hg merge 1
47 51 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 52 (branch merge, don't forget to commit)
49 53 $ hg transplant 1
50 54 abort: outstanding uncommitted merge
51 55 (use 'hg commit' or 'hg merge --abort')
52 56 [255]
53 57 $ hg up -qC tip
54 58 $ echo b0 > b1
55 59 $ hg transplant 1
56 60 abort: uncommitted changes
57 61 [255]
58 62 $ hg up -qC tip
59 63 $ echo b2 > b2
60 64 $ hg ci -Amb2 -d '1 0'
61 65 adding b2
62 66 $ echo b3 > b3
63 67 $ hg ci -Amb3 -d '2 0'
64 68 adding b3
65 69
66 70 $ hg log --template '{rev} {parents} {desc}\n'
67 71 4 b3
68 72 3 b2
69 73 2 0:17ab29e464c6 b1
70 74 1 r2
71 75 0 r1
72 76
73 77 $ hg clone . ../rebase
74 78 updating to branch default
75 79 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 80 $ hg init ../emptydest
77 81 $ cd ../emptydest
78 82 $ hg transplant --source=../t > /dev/null
79 83 $ cd ../rebase
80 84
81 85 $ hg up -C 1
82 86 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
83 87
84 88 rebase b onto r1
85 89 (this also tests that editor is not invoked if '--edit' is not specified)
86 90
87 91 $ HGEDITOR=cat hg transplant -a -b tip
88 92 applying 37a1297eb21b
89 93 37a1297eb21b transplanted to e234d668f844
90 94 applying 722f4667af76
91 95 722f4667af76 transplanted to 539f377d78df
92 96 applying a53251cdf717
93 97 a53251cdf717 transplanted to ffd6818a3975
94 98 $ hg log --template '{rev} {parents} {desc}\n'
95 99 7 b3
96 100 6 b2
97 101 5 1:d11e3596cc1a b1
98 102 4 b3
99 103 3 b2
100 104 2 0:17ab29e464c6 b1
101 105 1 r2
102 106 0 r1
103 107
104 108 test format of transplant_source
105 109
106 110 $ hg log -r7 --debug | grep transplant_source
107 111 extra: transplant_source=\xa52Q\xcd\xf7\x17g\x9d\x19\x07\xb2\x89\xf9\x91SK\xe0\\\x99z
108 112 $ hg log -r7 -T '{extras}\n'
109 113 branch=defaulttransplant_source=\xa52Q\xcd\xf7\x17g\x9d\x19\x07\xb2\x89\xf9\x91SK\xe0\\\x99z
110 114 $ hg log -r7 -T '{join(extras, " ")}\n'
111 115 branch=default transplant_source=\xa52Q\xcd\xf7\x17g\x9d\x19\x07\xb2\x89\xf9\x91SK\xe0\\\x99z
112 116
113 117 test transplanted revset
114 118
115 119 $ hg log -r 'transplanted()' --template '{rev} {parents} {desc}\n'
116 120 5 1:d11e3596cc1a b1
117 121 6 b2
118 122 7 b3
119 123 $ hg log -r 'transplanted(head())' --template '{rev} {parents} {desc}\n'
120 124 7 b3
121 125 $ hg help revisions.transplanted
122 126 "transplanted([set])"
123 127 Transplanted changesets in set, or all transplanted changesets.
124 128
125 129
126 130 test transplanted keyword
127 131
128 132 $ hg log --template '{rev} {transplanted}\n'
129 133 7 a53251cdf717679d1907b289f991534be05c997a
130 134 6 722f4667af767100cb15b6a79324bf8abbfe1ef4
131 135 5 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21
132 136 4
133 137 3
134 138 2
135 139 1
136 140 0
137 141
138 142 test destination() revset predicate with a transplant of a transplant; new
139 143 clone so subsequent rollback isn't affected
140 144 (this also tests that editor is invoked if '--edit' is specified)
141 145
142 146 $ hg clone -q . ../destination
143 147 $ cd ../destination
144 148 $ hg up -Cq 0
145 149 $ hg branch -q b4
146 150 $ hg ci -qm "b4"
147 151 $ hg status --rev "7^1" --rev 7
148 152 A b3
149 153 $ cat > $TESTTMP/checkeditform.sh <<EOF
150 154 > env | grep HGEDITFORM
151 155 > true
152 156 > EOF
153 157 $ cat > $TESTTMP/checkeditform-n-cat.sh <<EOF
154 158 > env | grep HGEDITFORM
155 159 > cat \$*
156 160 > EOF
157 161 $ HGEDITOR="sh $TESTTMP/checkeditform-n-cat.sh" hg transplant --edit 7
158 162 applying ffd6818a3975
159 163 HGEDITFORM=transplant.normal
160 164 b3
161 165
162 166
163 167 HG: Enter commit message. Lines beginning with 'HG:' are removed.
164 168 HG: Leave message empty to abort commit.
165 169 HG: --
166 170 HG: user: test
167 171 HG: branch 'b4'
168 172 HG: added b3
169 173 ffd6818a3975 transplanted to 502236fa76bb
170 174
171 175
172 176 $ hg log -r 'destination()'
173 177 changeset: 5:e234d668f844
174 178 parent: 1:d11e3596cc1a
175 179 user: test
176 180 date: Thu Jan 01 00:00:00 1970 +0000
177 181 summary: b1
178 182
179 183 changeset: 6:539f377d78df
180 184 user: test
181 185 date: Thu Jan 01 00:00:01 1970 +0000
182 186 summary: b2
183 187
184 188 changeset: 7:ffd6818a3975
185 189 user: test
186 190 date: Thu Jan 01 00:00:02 1970 +0000
187 191 summary: b3
188 192
189 193 changeset: 9:502236fa76bb
190 194 branch: b4
191 195 tag: tip
192 196 user: test
193 197 date: Thu Jan 01 00:00:02 1970 +0000
194 198 summary: b3
195 199
196 200 $ hg log -r 'destination(a53251cdf717)'
197 201 changeset: 7:ffd6818a3975
198 202 user: test
199 203 date: Thu Jan 01 00:00:02 1970 +0000
200 204 summary: b3
201 205
202 206 changeset: 9:502236fa76bb
203 207 branch: b4
204 208 tag: tip
205 209 user: test
206 210 date: Thu Jan 01 00:00:02 1970 +0000
207 211 summary: b3
208 212
209 213
210 214 test subset parameter in reverse order
211 215 $ hg log -r 'reverse(all()) and destination(a53251cdf717)'
212 216 changeset: 9:502236fa76bb
213 217 branch: b4
214 218 tag: tip
215 219 user: test
216 220 date: Thu Jan 01 00:00:02 1970 +0000
217 221 summary: b3
218 222
219 223 changeset: 7:ffd6818a3975
220 224 user: test
221 225 date: Thu Jan 01 00:00:02 1970 +0000
222 226 summary: b3
223 227
224 228
225 229 back to the original dir
226 230 $ cd ../rebase
227 231
228 232 rollback the transplant
229 233 $ hg rollback
230 234 repository tip rolled back to revision 4 (undo transplant)
231 235 working directory now based on revision 1
232 236 $ hg tip -q
233 237 4:a53251cdf717
234 238 $ hg parents -q
235 239 1:d11e3596cc1a
236 240 $ hg status
237 241 ? b1
238 242 ? b2
239 243 ? b3
240 244
241 245 $ hg clone ../t ../prune
242 246 updating to branch default
243 247 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
244 248 $ cd ../prune
245 249
246 250 $ hg up -C 1
247 251 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
248 252
249 253 rebase b onto r1, skipping b2
250 254
251 255 $ hg transplant -a -b tip -p 3
252 256 applying 37a1297eb21b
253 257 37a1297eb21b transplanted to e234d668f844
254 258 applying a53251cdf717
255 259 a53251cdf717 transplanted to 7275fda4d04f
256 260 $ hg log --template '{rev} {parents} {desc}\n'
257 261 6 b3
258 262 5 1:d11e3596cc1a b1
259 263 4 b3
260 264 3 b2
261 265 2 0:17ab29e464c6 b1
262 266 1 r2
263 267 0 r1
264 268
265 269 test same-parent transplant with --log
266 270
267 271 $ hg clone -r 1 ../t ../sameparent
268 272 adding changesets
269 273 adding manifests
270 274 adding file changes
271 275 added 2 changesets with 2 changes to 2 files
272 276 new changesets 17ab29e464c6:d11e3596cc1a
273 277 updating to branch default
274 278 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
275 279 $ cd ../sameparent
276 280 $ hg transplant --log -s ../prune 5
277 281 searching for changes
278 282 applying e234d668f844
279 283 e234d668f844 transplanted to e07aea8ecf9c
280 284 $ hg log --template '{rev} {parents} {desc}\n'
281 285 2 b1
282 286 (transplanted from e234d668f844e1b1a765f01db83a32c0c7bfa170)
283 287 1 r2
284 288 0 r1
285 289 remote transplant, and also test that transplant doesn't break with
286 290 format-breaking diffopts
287 291
288 292 $ hg clone -r 1 ../t ../remote
289 293 adding changesets
290 294 adding manifests
291 295 adding file changes
292 296 added 2 changesets with 2 changes to 2 files
293 297 new changesets 17ab29e464c6:d11e3596cc1a
294 298 updating to branch default
295 299 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 300 $ cd ../remote
297 301 $ hg --config diff.noprefix=True transplant --log -s ../t 2 4
298 302 searching for changes
299 303 applying 37a1297eb21b
300 304 37a1297eb21b transplanted to c19cf0ccb069
301 305 applying a53251cdf717
302 306 a53251cdf717 transplanted to f7fe5bf98525
303 307 $ hg log --template '{rev} {parents} {desc}\n'
304 308 3 b3
305 309 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
306 310 2 b1
307 311 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
308 312 1 r2
309 313 0 r1
310 314
311 315 skip previous transplants
312 316
313 317 $ hg transplant -s ../t -a -b 4
314 318 searching for changes
315 319 applying 722f4667af76
316 320 722f4667af76 transplanted to 47156cd86c0b
317 321 $ hg log --template '{rev} {parents} {desc}\n'
318 322 4 b2
319 323 3 b3
320 324 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
321 325 2 b1
322 326 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
323 327 1 r2
324 328 0 r1
325 329
326 330 skip local changes transplanted to the source
327 331
328 332 $ echo b4 > b4
329 333 $ hg ci -Amb4 -d '3 0'
330 334 adding b4
331 335 $ hg clone ../t ../pullback
332 336 updating to branch default
333 337 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
334 338 $ cd ../pullback
335 339 $ hg transplant -s ../remote -a -b tip
336 340 searching for changes
337 341 applying 4333daefcb15
338 342 4333daefcb15 transplanted to 5f42c04e07cc
339 343
340 344
341 345 remote transplant with pull
342 346
343 347 $ hg serve -R ../t -p $HGPORT -d --pid-file=../t.pid
344 348 $ cat ../t.pid >> $DAEMON_PIDS
345 349
346 350 $ hg clone -r 0 ../t ../rp
347 351 adding changesets
348 352 adding manifests
349 353 adding file changes
350 354 added 1 changesets with 1 changes to 1 files
351 355 new changesets 17ab29e464c6
352 356 updating to branch default
353 357 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
354 358 $ cd ../rp
355 359 $ hg transplant -s http://localhost:$HGPORT/ 37a1297eb21b a53251cdf717
356 360 searching for changes
357 361 searching for changes
358 362 adding changesets
359 363 adding manifests
360 364 adding file changes
361 365 added 1 changesets with 1 changes to 1 files
362 366 applying a53251cdf717
363 367 a53251cdf717 transplanted to 8d9279348abb
364 368 $ hg log --template '{rev} {parents} {desc}\n'
365 369 2 b3
366 370 1 b1
367 371 0 r1
368 372
369 373 remote transplant without pull
370 374 (It was using "2" and "4" (as the previous transplant used to) which referenced
371 375 revision different from one run to another)
372 376
373 377 $ hg pull -q http://localhost:$HGPORT/
374 378 $ hg transplant -s http://localhost:$HGPORT/ 8d9279348abb 722f4667af76
375 379 skipping already applied revision 2:8d9279348abb
376 380 applying 722f4667af76
377 381 722f4667af76 transplanted to 76e321915884
378 382
379 transplant --continue
383
384 transplant --continue and --stop behaviour
380 385
381 386 $ hg init ../tc
382 387 $ cd ../tc
383 388 $ cat <<EOF > foo
384 389 > foo
385 390 > bar
386 391 > baz
387 392 > EOF
388 393 $ echo toremove > toremove
389 394 $ echo baz > baz
390 395 $ hg ci -Amfoo
391 396 adding baz
392 397 adding foo
393 398 adding toremove
394 399 $ cat <<EOF > foo
395 400 > foo2
396 401 > bar2
397 402 > baz2
398 403 > EOF
399 404 $ rm toremove
400 405 $ echo added > added
401 406 $ hg ci -Amfoo2
402 407 adding added
403 408 removing toremove
404 409 $ echo bar > bar
405 410 $ cat > baz <<EOF
406 411 > before baz
407 412 > baz
408 413 > after baz
409 414 > EOF
410 415 $ hg ci -Ambar
411 416 adding bar
412 417 $ echo bar2 >> bar
413 418 $ hg ci -mbar2
414 419 $ hg up 0
415 420 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
416 421 $ echo foobar > foo
417 422 $ hg ci -mfoobar
418 423 created new head
424
425 Repo log before transplant
426 $ hg glog
427 @ changeset: 4:e8643552fde5
428 | tag: tip
429 | parent: 0:493149fa1541
430 | user: test
431 | date: Thu Jan 01 00:00:00 1970 +0000
432 | summary: foobar
433 |
434 | o changeset: 3:1dab759070cf
435 | | user: test
436 | | date: Thu Jan 01 00:00:00 1970 +0000
437 | | summary: bar2
438 | |
439 | o changeset: 2:9d6d6b5a8275
440 | | user: test
441 | | date: Thu Jan 01 00:00:00 1970 +0000
442 | | summary: bar
443 | |
444 | o changeset: 1:46ae92138f3c
445 |/ user: test
446 | date: Thu Jan 01 00:00:00 1970 +0000
447 | summary: foo2
448 |
449 o changeset: 0:493149fa1541
450 user: test
451 date: Thu Jan 01 00:00:00 1970 +0000
452 summary: foo
453
419 454 $ hg transplant 1:3
420 455 applying 46ae92138f3c
421 456 patching file foo
422 457 Hunk #1 FAILED at 0
423 458 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
424 459 patch failed to apply
425 460 abort: fix up the working directory and run hg transplant --continue
426 461 [255]
427 462
463 $ hg transplant --stop
464 stopped the interrupted transplant
465 working directory is now at e8643552fde5
466 Repo log after abort
467 $ hg glog
468 @ changeset: 4:e8643552fde5
469 | tag: tip
470 | parent: 0:493149fa1541
471 | user: test
472 | date: Thu Jan 01 00:00:00 1970 +0000
473 | summary: foobar
474 |
475 | o changeset: 3:1dab759070cf
476 | | user: test
477 | | date: Thu Jan 01 00:00:00 1970 +0000
478 | | summary: bar2
479 | |
480 | o changeset: 2:9d6d6b5a8275
481 | | user: test
482 | | date: Thu Jan 01 00:00:00 1970 +0000
483 | | summary: bar
484 | |
485 | o changeset: 1:46ae92138f3c
486 |/ user: test
487 | date: Thu Jan 01 00:00:00 1970 +0000
488 | summary: foo2
489 |
490 o changeset: 0:493149fa1541
491 user: test
492 date: Thu Jan 01 00:00:00 1970 +0000
493 summary: foo
494
495 $ hg transplant 1:3
496 applying 46ae92138f3c
497 file added already exists
498 1 out of 1 hunks FAILED -- saving rejects to file added.rej
499 patching file foo
500 Hunk #1 FAILED at 0
501 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
502 patch failed to apply
503 abort: fix up the working directory and run hg transplant --continue
504 [255]
505
428 506 transplant -c shouldn't use an old changeset
429 507
430 508 $ hg up -C
431 509 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
432 510 updated to "e8643552fde5: foobar"
433 511 1 other heads for branch "default"
434 512 $ rm added
435 513 $ hg continue
436 514 abort: no transplant to continue (continueflag !)
437 515 abort: no operation in progress (no-continueflag !)
438 516 [255]
517 $ hg transplant --stop
518 abort: no interrupted transplant found
519 [255]
439 520 $ hg transplant 1
440 521 applying 46ae92138f3c
441 522 patching file foo
442 523 Hunk #1 FAILED at 0
443 524 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
444 525 patch failed to apply
445 526 abort: fix up the working directory and run hg transplant --continue
446 527 [255]
447 528 $ cp .hg/transplant/journal .hg/transplant/journal.orig
448 529 $ cat .hg/transplant/journal
449 530 # User test
450 531 # Date 0 0
451 532 # Node ID 46ae92138f3ce0249f6789650403286ead052b6d
452 533 # Parent e8643552fde58f57515e19c4b373a57c96e62af3
453 534 foo2
454 535 $ grep -v 'Date' .hg/transplant/journal.orig > .hg/transplant/journal
455 536 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
456 537 abort: filter corrupted changeset (no user or date)
457 538 [255]
458 539 $ cp .hg/transplant/journal.orig .hg/transplant/journal
459 540 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
460 541 HGEDITFORM=transplant.normal
461 542 46ae92138f3c transplanted as 9159dada197d
462 543 $ hg transplant 1:3
463 544 skipping already applied revision 1:46ae92138f3c
464 545 applying 9d6d6b5a8275
465 546 9d6d6b5a8275 transplanted to 2d17a10c922f
466 547 applying 1dab759070cf
467 548 1dab759070cf transplanted to e06a69927eb0
468 549 $ hg locate
469 550 added
470 551 bar
471 552 baz
472 553 foo
473 554
474 555 test multiple revisions, --continue and hg status --verbose
475 556
476 557 $ hg up -qC 0
477 558 $ echo bazbaz > baz
478 559 $ hg ci -Am anotherbaz baz
479 560 created new head
480 561 $ hg transplant 1:3
481 562 applying 46ae92138f3c
482 563 46ae92138f3c transplanted to 1024233ea0ba
483 564 applying 9d6d6b5a8275
484 565 patching file baz
485 566 Hunk #1 FAILED at 0
486 567 1 out of 1 hunks FAILED -- saving rejects to file baz.rej
487 568 patch failed to apply
488 569 abort: fix up the working directory and run hg transplant --continue
489 570 [255]
490 571 $ hg transplant 1:3
491 572 abort: transplant in progress
492 (use 'hg transplant --continue' or 'hg update' to abort)
573 (use 'hg transplant --continue' or 'hg transplant --stop')
493 574 [255]
494 575 $ hg status -v
495 576 A bar
577 ? added.rej
496 578 ? baz.rej
497 579 ? foo.rej
498 580 # The repository is in an unfinished *transplant* state.
499 581
500 582 # To continue: hg transplant --continue
501 # To abort: hg update
583 # To stop: hg transplant --stop
502 584
503 585 $ echo fixed > baz
504 586 $ hg continue
505 587 9d6d6b5a8275 transplanted as d80c49962290
506 588 applying 1dab759070cf
507 589 1dab759070cf transplanted to aa0ffe6bd5ae
508
509 590 $ cd ..
510 591
511 592 Issue1111: Test transplant --merge
512 593
513 594 $ hg init t1111
514 595 $ cd t1111
515 596 $ echo a > a
516 597 $ hg ci -Am adda
517 598 adding a
518 599 $ echo b >> a
519 600 $ hg ci -m appendb
520 601 $ echo c >> a
521 602 $ hg ci -m appendc
522 603 $ hg up -C 0
523 604 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
524 605 $ echo d >> a
525 606 $ hg ci -m appendd
526 607 created new head
527 608
528 609 transplant
529 610
530 611 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant -m 1 -e
531 612 applying 42dc4432fd35
532 613 HGEDITFORM=transplant.merge
533 614 1:42dc4432fd35 merged at a9f4acbac129
534 615 $ hg update -q -C 2
535 616 $ cat > a <<EOF
536 617 > x
537 618 > y
538 619 > z
539 620 > EOF
540 621 $ hg commit -m replace
541 622 $ hg update -q -C 4
542 623 $ hg transplant -m 5
543 624 applying 600a3cdcb41d
544 625 patching file a
545 626 Hunk #1 FAILED at 0
546 627 1 out of 1 hunks FAILED -- saving rejects to file a.rej
547 628 patch failed to apply
548 629 abort: fix up the working directory and run hg transplant --continue
549 630 [255]
550 631 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
551 632 HGEDITFORM=transplant.merge
552 633 600a3cdcb41d transplanted as a3f88be652e0
553 634
554 635 $ cd ..
555 636
556 637 test transplant into empty repository
557 638
558 639 $ hg init empty
559 640 $ cd empty
560 641 $ hg transplant -s ../t -b tip -a
561 642 adding changesets
562 643 adding manifests
563 644 adding file changes
564 645 added 4 changesets with 4 changes to 4 files
565 646 new changesets 17ab29e464c6:a53251cdf717
566 647
567 648 test "--merge" causing pull from source repository on local host
568 649
569 650 $ hg --config extensions.mq= -q strip 2
570 651 $ hg transplant -s ../t --merge tip
571 652 searching for changes
572 653 searching for changes
573 654 adding changesets
574 655 adding manifests
575 656 adding file changes
576 657 added 2 changesets with 2 changes to 2 files
577 658 applying a53251cdf717
578 659 4:a53251cdf717 merged at 4831f4dc831a
579 660
580 661 test interactive transplant
581 662
582 663 $ hg --config extensions.strip= -q strip 0
583 664 $ hg -R ../t log -G --template "{rev}:{node|short}"
584 665 @ 4:a53251cdf717
585 666 |
586 667 o 3:722f4667af76
587 668 |
588 669 o 2:37a1297eb21b
589 670 |
590 671 | o 1:d11e3596cc1a
591 672 |/
592 673 o 0:17ab29e464c6
593 674
594 675 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
595 676 > ?
596 677 > x
597 678 > q
598 679 > EOF
599 680 0:17ab29e464c6
600 681 apply changeset? [ynmpcq?]: ?
601 682 y: yes, transplant this changeset
602 683 n: no, skip this changeset
603 684 m: merge at this changeset
604 685 p: show patch
605 686 c: commit selected changesets
606 687 q: quit and cancel transplant
607 688 ?: ? (show this help)
608 689 apply changeset? [ynmpcq?]: x
609 690 unrecognized response
610 691 apply changeset? [ynmpcq?]: q
611 692 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
612 693 > p
613 694 > y
614 695 > n
615 696 > n
616 697 > m
617 698 > c
618 699 > EOF
619 700 0:17ab29e464c6
620 701 apply changeset? [ynmpcq?]: p
621 702 diff -r 000000000000 -r 17ab29e464c6 r1
622 703 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
623 704 +++ b/r1 Thu Jan 01 00:00:00 1970 +0000
624 705 @@ -0,0 +1,1 @@
625 706 +r1
626 707 apply changeset? [ynmpcq?]: y
627 708 1:d11e3596cc1a
628 709 apply changeset? [ynmpcq?]: n
629 710 2:37a1297eb21b
630 711 apply changeset? [ynmpcq?]: n
631 712 3:722f4667af76
632 713 apply changeset? [ynmpcq?]: m
633 714 4:a53251cdf717
634 715 apply changeset? [ynmpcq?]: c
635 716 $ hg log -G --template "{node|short}"
636 717 @ 88be5dde5260
637 718 |\
638 719 | o 722f4667af76
639 720 | |
640 721 | o 37a1297eb21b
641 722 |/
642 723 o 17ab29e464c6
643 724
644 725 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
645 726 > x
646 727 > ?
647 728 > y
648 729 > q
649 730 > EOF
650 731 1:d11e3596cc1a
651 732 apply changeset? [ynmpcq?]: x
652 733 unrecognized response
653 734 apply changeset? [ynmpcq?]: ?
654 735 y: yes, transplant this changeset
655 736 n: no, skip this changeset
656 737 m: merge at this changeset
657 738 p: show patch
658 739 c: commit selected changesets
659 740 q: quit and cancel transplant
660 741 ?: ? (show this help)
661 742 apply changeset? [ynmpcq?]: y
662 743 4:a53251cdf717
663 744 apply changeset? [ynmpcq?]: q
664 745 $ hg heads --template "{node|short}\n"
665 746 88be5dde5260
666 747
667 748 $ cd ..
668 749
669 750
670 751 #if unix-permissions system-sh
671 752
672 753 test filter
673 754
674 755 $ hg init filter
675 756 $ cd filter
676 757 $ cat <<'EOF' >test-filter
677 758 > #!/bin/sh
678 759 > sed 's/r1/r2/' $1 > $1.new
679 760 > mv $1.new $1
680 761 > EOF
681 762 $ chmod +x test-filter
682 763 $ hg transplant -s ../t -b tip -a --filter ./test-filter
683 764 filtering * (glob)
684 765 applying 17ab29e464c6
685 766 17ab29e464c6 transplanted to e9ffc54ea104
686 767 filtering * (glob)
687 768 applying 37a1297eb21b
688 769 37a1297eb21b transplanted to 348b36d0b6a5
689 770 filtering * (glob)
690 771 applying 722f4667af76
691 772 722f4667af76 transplanted to 0aa6979afb95
692 773 filtering * (glob)
693 774 applying a53251cdf717
694 775 a53251cdf717 transplanted to 14f8512272b5
695 776 $ hg log --template '{rev} {parents} {desc}\n'
696 777 3 b3
697 778 2 b2
698 779 1 b1
699 780 0 r2
700 781 $ cd ..
701 782
702 783
703 784 test filter with failed patch
704 785
705 786 $ cd filter
706 787 $ hg up 0
707 788 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
708 789 $ echo foo > b1
709 790 $ hg ci -Am foo
710 791 adding b1
711 792 adding test-filter
712 793 created new head
713 794 $ hg transplant 1 --filter ./test-filter
714 795 filtering * (glob)
715 796 applying 348b36d0b6a5
716 797 file b1 already exists
717 798 1 out of 1 hunks FAILED -- saving rejects to file b1.rej
718 799 patch failed to apply
719 800 abort: fix up the working directory and run hg transplant --continue
720 801 [255]
721 802 $ cd ..
722 803
723 804 test environment passed to filter
724 805
725 806 $ hg init filter-environment
726 807 $ cd filter-environment
727 808 $ cat <<'EOF' >test-filter-environment
728 809 > #!/bin/sh
729 810 > echo "Transplant by $HGUSER" >> $1
730 811 > echo "Transplant from rev $HGREVISION" >> $1
731 812 > EOF
732 813 $ chmod +x test-filter-environment
733 814 $ hg transplant -s ../t --filter ./test-filter-environment 0
734 815 filtering * (glob)
735 816 applying 17ab29e464c6
736 817 17ab29e464c6 transplanted to 5190e68026a0
737 818
738 819 $ hg log --template '{rev} {parents} {desc}\n'
739 820 0 r1
740 821 Transplant by test
741 822 Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f
742 823 $ cd ..
743 824
744 825 test transplant with filter handles invalid changelog
745 826
746 827 $ hg init filter-invalid-log
747 828 $ cd filter-invalid-log
748 829 $ cat <<'EOF' >test-filter-invalid-log
749 830 > #!/bin/sh
750 831 > echo "" > $1
751 832 > EOF
752 833 $ chmod +x test-filter-invalid-log
753 834 $ hg transplant -s ../t --filter ./test-filter-invalid-log 0
754 835 filtering * (glob)
755 836 abort: filter corrupted changeset (no user or date)
756 837 [255]
757 838 $ cd ..
758 839
759 840 #endif
760 841
761 842
762 843 test with a win32ext like setup (differing EOLs)
763 844
764 845 $ hg init twin1
765 846 $ cd twin1
766 847 $ echo a > a
767 848 $ echo b > b
768 849 $ echo b >> b
769 850 $ hg ci -Am t
770 851 adding a
771 852 adding b
772 853 $ echo a > b
773 854 $ echo b >> b
774 855 $ hg ci -m changeb
775 856 $ cd ..
776 857
777 858 $ hg init twin2
778 859 $ cd twin2
779 860 $ echo '[patch]' >> .hg/hgrc
780 861 $ echo 'eol = crlf' >> .hg/hgrc
781 862 $ "$PYTHON" -c "open('b', 'wb').write(b'b\r\nb\r\n')"
782 863 $ hg ci -Am addb
783 864 adding b
784 865 $ hg transplant -s ../twin1 tip
785 866 searching for changes
786 867 warning: repository is unrelated
787 868 applying 2e849d776c17
788 869 2e849d776c17 transplanted to 8e65bebc063e
789 870 $ cat b
790 871 a\r (esc)
791 872 b\r (esc)
792 873 $ cd ..
793 874
794 875 test transplant with merge changeset is skipped
795 876
796 877 $ hg init merge1a
797 878 $ cd merge1a
798 879 $ echo a > a
799 880 $ hg ci -Am a
800 881 adding a
801 882 $ hg branch b
802 883 marked working directory as branch b
803 884 (branches are permanent and global, did you want a bookmark?)
804 885 $ hg ci -m branchb
805 886 $ echo b > b
806 887 $ hg ci -Am b
807 888 adding b
808 889 $ hg update default
809 890 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
810 891 $ hg merge b
811 892 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
812 893 (branch merge, don't forget to commit)
813 894 $ hg ci -m mergeb
814 895 $ cd ..
815 896
816 897 $ hg init merge1b
817 898 $ cd merge1b
818 899 $ hg transplant -s ../merge1a tip
819 900 $ cd ..
820 901
821 902 test transplant with merge changeset accepts --parent
822 903
823 904 $ hg init merge2a
824 905 $ cd merge2a
825 906 $ echo a > a
826 907 $ hg ci -Am a
827 908 adding a
828 909 $ hg branch b
829 910 marked working directory as branch b
830 911 (branches are permanent and global, did you want a bookmark?)
831 912 $ hg ci -m branchb
832 913 $ echo b > b
833 914 $ hg ci -Am b
834 915 adding b
835 916 $ hg update default
836 917 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
837 918 $ hg merge b
838 919 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
839 920 (branch merge, don't forget to commit)
840 921 $ hg ci -m mergeb
841 922 $ cd ..
842 923
843 924 $ hg init merge2b
844 925 $ cd merge2b
845 926 $ hg transplant -s ../merge2a --parent tip tip
846 927 abort: be9f9b39483f is not a parent of be9f9b39483f
847 928 [255]
848 929 $ hg transplant -s ../merge2a --parent 0 tip
849 930 applying be9f9b39483f
850 931 be9f9b39483f transplanted to 9959e51f94d1
851 932 $ cd ..
852 933
853 934 test transplanting a patch turning into a no-op
854 935
855 936 $ hg init binarysource
856 937 $ cd binarysource
857 938 $ echo a > a
858 939 $ hg ci -Am adda a
859 940 >>> open('b', 'wb').write(b'\0b1') and None
860 941 $ hg ci -Am addb b
861 942 >>> open('b', 'wb').write(b'\0b2') and None
862 943 $ hg ci -m changeb b
863 944 $ cd ..
864 945
865 946 $ hg clone -r0 binarysource binarydest
866 947 adding changesets
867 948 adding manifests
868 949 adding file changes
869 950 added 1 changesets with 1 changes to 1 files
870 951 new changesets 07f494440405
871 952 updating to branch default
872 953 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
873 954 $ cd binarydest
874 955 $ cp ../binarysource/b b
875 956 $ hg ci -Am addb2 b
876 957 $ hg transplant -s ../binarysource 2
877 958 searching for changes
878 959 applying 7a7d57e15850
879 960 skipping emptied changeset 7a7d57e15850
880 961
881 962 Test empty result in --continue
882 963
883 964 $ hg transplant -s ../binarysource 1
884 965 searching for changes
885 966 applying 645035761929
886 967 file b already exists
887 968 1 out of 1 hunks FAILED -- saving rejects to file b.rej
888 969 patch failed to apply
889 970 abort: fix up the working directory and run hg transplant --continue
890 971 [255]
891 972 $ hg status
892 973 ? b.rej
893 974 $ hg continue
894 975 645035761929 skipped due to empty diff
895 976
896 977 $ cd ..
897 978
898 979 Explicitly kill daemons to let the test exit on Windows
899 980
900 981 $ killdaemons.py
901 982
902 983 Test that patch-ed files are treated as "modified", when transplant is
903 984 aborted by failure of patching, even if none of mode, size and
904 985 timestamp of them isn't changed on the filesystem (see also issue4583)
905 986
906 987 $ cd t
907 988
908 989 $ cat > $TESTTMP/abort.py <<EOF
909 990 > # emulate that patch.patch() is aborted at patching on "abort" file
910 991 > from mercurial import error, extensions, patch as patchmod
911 992 > def patch(orig, ui, repo, patchname,
912 993 > strip=1, prefix=b'', files=None,
913 994 > eolmode=b'strict', similarity=0):
914 995 > if files is None:
915 996 > files = set()
916 997 > r = orig(ui, repo, patchname,
917 998 > strip=strip, prefix=prefix, files=files,
918 999 > eolmode=eolmode, similarity=similarity)
919 1000 > if b'abort' in files:
920 1001 > raise error.PatchError('intentional error while patching')
921 1002 > return r
922 1003 > def extsetup(ui):
923 1004 > extensions.wrapfunction(patchmod, 'patch', patch)
924 1005 > EOF
925 1006
926 1007 $ echo X1 > r1
927 1008 $ hg diff --nodates r1
928 1009 diff -r a53251cdf717 r1
929 1010 --- a/r1
930 1011 +++ b/r1
931 1012 @@ -1,1 +1,1 @@
932 1013 -r1
933 1014 +X1
934 1015 $ hg commit -m "X1 as r1"
935 1016
936 1017 $ echo 'marking to abort patching' > abort
937 1018 $ hg add abort
938 1019 $ echo Y1 > r1
939 1020 $ hg diff --nodates r1
940 1021 diff -r 22c515968f13 r1
941 1022 --- a/r1
942 1023 +++ b/r1
943 1024 @@ -1,1 +1,1 @@
944 1025 -X1
945 1026 +Y1
946 1027 $ hg commit -m "Y1 as r1"
947 1028
948 1029 $ hg update -q -C d11e3596cc1a
949 1030 $ cat r1
950 1031 r1
951 1032
952 1033 $ cat >> .hg/hgrc <<EOF
953 1034 > [fakedirstatewritetime]
954 1035 > # emulate invoking dirstate.write() via repo.status() or markcommitted()
955 1036 > # at 2000-01-01 00:00
956 1037 > fakenow = 200001010000
957 1038 >
958 1039 > # emulate invoking patch.internalpatch() at 2000-01-01 00:00
959 1040 > [fakepatchtime]
960 1041 > fakenow = 200001010000
961 1042 >
962 1043 > [extensions]
963 1044 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
964 1045 > fakepatchtime = $TESTDIR/fakepatchtime.py
965 1046 > abort = $TESTTMP/abort.py
966 1047 > EOF
967 1048 $ hg transplant "22c515968f13::"
968 1049 applying 22c515968f13
969 1050 22c515968f13 transplanted to * (glob)
970 1051 applying e38700ba9dd3
971 1052 intentional error while patching
972 1053 abort: fix up the working directory and run hg transplant --continue
973 1054 [255]
974 1055 $ cat >> .hg/hgrc <<EOF
975 1056 > [hooks]
976 1057 > fakedirstatewritetime = !
977 1058 > fakepatchtime = !
978 1059 > [extensions]
979 1060 > abort = !
980 1061 > EOF
981 1062
982 1063 $ cat r1
983 1064 Y1
984 1065 $ hg debugstate | grep ' r1$'
985 1066 n 644 3 unset r1
986 1067 $ hg status -A r1
987 1068 M r1
988 1069
989 1070 Test that rollback by unexpected failure after transplanting the first
990 1071 revision restores dirstate correctly.
991 1072
992 1073 $ hg rollback -q
993 1074 $ rm -f abort
994 1075 $ hg update -q -C d11e3596cc1a
995 1076 $ hg parents -T "{node|short}\n"
996 1077 d11e3596cc1a
997 1078 $ hg status -A
998 1079 C r1
999 1080 C r2
1000 1081
1001 1082 $ cat >> .hg/hgrc <<EOF
1002 1083 > [hooks]
1003 1084 > # emulate failure at transplanting the 2nd revision
1004 1085 > pretxncommit.abort = test ! -f abort
1005 1086 > EOF
1006 1087 $ hg transplant "22c515968f13::"
1007 1088 applying 22c515968f13
1008 1089 22c515968f13 transplanted to * (glob)
1009 1090 applying e38700ba9dd3
1010 1091 transaction abort!
1011 1092 rollback completed
1012 1093 abort: pretxncommit.abort hook exited with status 1
1013 1094 [255]
1014 1095 $ cat >> .hg/hgrc <<EOF
1015 1096 > [hooks]
1016 1097 > pretxncommit.abort = !
1017 1098 > EOF
1018 1099
1019 1100 $ hg parents -T "{node|short}\n"
1020 1101 d11e3596cc1a
1021 1102 $ hg status -A
1022 1103 M r1
1023 1104 ? abort
1024 1105 C r2
1025 1106
1026 1107 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now