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