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