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