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