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