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