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