##// END OF EJS Templates
transplant: do not rollback on patching error (issue3379)...
Patrick Mezard -
r16507:1f020021 stable
parent child Browse files
Show More
@@ -1,670 +1,679 b''
1 # Patch transplanting extension for Mercurial
1 # Patch transplanting extension for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to transplant changesets from another branch
8 '''command to transplant changesets from another branch
9
9
10 This extension allows you to transplant patches from another branch.
10 This extension allows you to transplant patches from another branch.
11
11
12 Transplanted patches are recorded in .hg/transplant/transplants, as a
12 Transplanted patches are recorded in .hg/transplant/transplants, as a
13 map from a changeset hash to its hash in the source repository.
13 map from a changeset hash to its hash in the source repository.
14 '''
14 '''
15
15
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 import os, tempfile
17 import os, tempfile
18 from mercurial.node import short
18 from mercurial.node import short
19 from mercurial import bundlerepo, hg, merge, match
19 from mercurial import bundlerepo, hg, merge, match
20 from mercurial import patch, revlog, scmutil, util, error, cmdutil
20 from mercurial import patch, revlog, scmutil, util, error, cmdutil
21 from mercurial import revset, templatekw
21 from mercurial import revset, templatekw
22
22
23 class TransplantError(error.Abort):
24 pass
25
23 cmdtable = {}
26 cmdtable = {}
24 command = cmdutil.command(cmdtable)
27 command = cmdutil.command(cmdtable)
25
28
26 class transplantentry(object):
29 class transplantentry(object):
27 def __init__(self, lnode, rnode):
30 def __init__(self, lnode, rnode):
28 self.lnode = lnode
31 self.lnode = lnode
29 self.rnode = rnode
32 self.rnode = rnode
30
33
31 class transplants(object):
34 class transplants(object):
32 def __init__(self, path=None, transplantfile=None, opener=None):
35 def __init__(self, path=None, transplantfile=None, opener=None):
33 self.path = path
36 self.path = path
34 self.transplantfile = transplantfile
37 self.transplantfile = transplantfile
35 self.opener = opener
38 self.opener = opener
36
39
37 if not opener:
40 if not opener:
38 self.opener = scmutil.opener(self.path)
41 self.opener = scmutil.opener(self.path)
39 self.transplants = {}
42 self.transplants = {}
40 self.dirty = False
43 self.dirty = False
41 self.read()
44 self.read()
42
45
43 def read(self):
46 def read(self):
44 abspath = os.path.join(self.path, self.transplantfile)
47 abspath = os.path.join(self.path, self.transplantfile)
45 if self.transplantfile and os.path.exists(abspath):
48 if self.transplantfile and os.path.exists(abspath):
46 for line in self.opener.read(self.transplantfile).splitlines():
49 for line in self.opener.read(self.transplantfile).splitlines():
47 lnode, rnode = map(revlog.bin, line.split(':'))
50 lnode, rnode = map(revlog.bin, line.split(':'))
48 list = self.transplants.setdefault(rnode, [])
51 list = self.transplants.setdefault(rnode, [])
49 list.append(transplantentry(lnode, rnode))
52 list.append(transplantentry(lnode, rnode))
50
53
51 def write(self):
54 def write(self):
52 if self.dirty and self.transplantfile:
55 if self.dirty and self.transplantfile:
53 if not os.path.isdir(self.path):
56 if not os.path.isdir(self.path):
54 os.mkdir(self.path)
57 os.mkdir(self.path)
55 fp = self.opener(self.transplantfile, 'w')
58 fp = self.opener(self.transplantfile, 'w')
56 for list in self.transplants.itervalues():
59 for list in self.transplants.itervalues():
57 for t in list:
60 for t in list:
58 l, r = map(revlog.hex, (t.lnode, t.rnode))
61 l, r = map(revlog.hex, (t.lnode, t.rnode))
59 fp.write(l + ':' + r + '\n')
62 fp.write(l + ':' + r + '\n')
60 fp.close()
63 fp.close()
61 self.dirty = False
64 self.dirty = False
62
65
63 def get(self, rnode):
66 def get(self, rnode):
64 return self.transplants.get(rnode) or []
67 return self.transplants.get(rnode) or []
65
68
66 def set(self, lnode, rnode):
69 def set(self, lnode, rnode):
67 list = self.transplants.setdefault(rnode, [])
70 list = self.transplants.setdefault(rnode, [])
68 list.append(transplantentry(lnode, rnode))
71 list.append(transplantentry(lnode, rnode))
69 self.dirty = True
72 self.dirty = True
70
73
71 def remove(self, transplant):
74 def remove(self, transplant):
72 list = self.transplants.get(transplant.rnode)
75 list = self.transplants.get(transplant.rnode)
73 if list:
76 if list:
74 del list[list.index(transplant)]
77 del list[list.index(transplant)]
75 self.dirty = True
78 self.dirty = True
76
79
77 class transplanter(object):
80 class transplanter(object):
78 def __init__(self, ui, repo):
81 def __init__(self, ui, repo):
79 self.ui = ui
82 self.ui = ui
80 self.path = repo.join('transplant')
83 self.path = repo.join('transplant')
81 self.opener = scmutil.opener(self.path)
84 self.opener = scmutil.opener(self.path)
82 self.transplants = transplants(self.path, 'transplants',
85 self.transplants = transplants(self.path, 'transplants',
83 opener=self.opener)
86 opener=self.opener)
84 self.editor = None
87 self.editor = None
85
88
86 def applied(self, repo, node, parent):
89 def applied(self, repo, node, parent):
87 '''returns True if a node is already an ancestor of parent
90 '''returns True if a node is already an ancestor of parent
88 or has already been transplanted'''
91 or has already been transplanted'''
89 if hasnode(repo, node):
92 if hasnode(repo, node):
90 if node in repo.changelog.reachable(parent, stop=node):
93 if node in repo.changelog.reachable(parent, stop=node):
91 return True
94 return True
92 for t in self.transplants.get(node):
95 for t in self.transplants.get(node):
93 # it might have been stripped
96 # it might have been stripped
94 if not hasnode(repo, t.lnode):
97 if not hasnode(repo, t.lnode):
95 self.transplants.remove(t)
98 self.transplants.remove(t)
96 return False
99 return False
97 if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
100 if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
98 return True
101 return True
99 return False
102 return False
100
103
101 def apply(self, repo, source, revmap, merges, opts={}):
104 def apply(self, repo, source, revmap, merges, opts={}):
102 '''apply the revisions in revmap one by one in revision order'''
105 '''apply the revisions in revmap one by one in revision order'''
103 revs = sorted(revmap)
106 revs = sorted(revmap)
104 p1, p2 = repo.dirstate.parents()
107 p1, p2 = repo.dirstate.parents()
105 pulls = []
108 pulls = []
106 diffopts = patch.diffopts(self.ui, opts)
109 diffopts = patch.diffopts(self.ui, opts)
107 diffopts.git = True
110 diffopts.git = True
108
111
109 lock = wlock = tr = None
112 lock = wlock = tr = None
110 try:
113 try:
111 wlock = repo.wlock()
114 wlock = repo.wlock()
112 lock = repo.lock()
115 lock = repo.lock()
113 tr = repo.transaction('transplant')
116 tr = repo.transaction('transplant')
114 for rev in revs:
117 for rev in revs:
115 node = revmap[rev]
118 node = revmap[rev]
116 revstr = '%s:%s' % (rev, short(node))
119 revstr = '%s:%s' % (rev, short(node))
117
120
118 if self.applied(repo, node, p1):
121 if self.applied(repo, node, p1):
119 self.ui.warn(_('skipping already applied revision %s\n') %
122 self.ui.warn(_('skipping already applied revision %s\n') %
120 revstr)
123 revstr)
121 continue
124 continue
122
125
123 parents = source.changelog.parents(node)
126 parents = source.changelog.parents(node)
124 if not opts.get('filter'):
127 if not opts.get('filter'):
125 # If the changeset parent is the same as the
128 # If the changeset parent is the same as the
126 # wdir's parent, just pull it.
129 # wdir's parent, just pull it.
127 if parents[0] == p1:
130 if parents[0] == p1:
128 pulls.append(node)
131 pulls.append(node)
129 p1 = node
132 p1 = node
130 continue
133 continue
131 if pulls:
134 if pulls:
132 if source != repo:
135 if source != repo:
133 repo.pull(source, heads=pulls)
136 repo.pull(source, heads=pulls)
134 merge.update(repo, pulls[-1], False, False, None)
137 merge.update(repo, pulls[-1], False, False, None)
135 p1, p2 = repo.dirstate.parents()
138 p1, p2 = repo.dirstate.parents()
136 pulls = []
139 pulls = []
137
140
138 domerge = False
141 domerge = False
139 if node in merges:
142 if node in merges:
140 # pulling all the merge revs at once would mean we
143 # pulling all the merge revs at once would mean we
141 # couldn't transplant after the latest even if
144 # couldn't transplant after the latest even if
142 # transplants before them fail.
145 # transplants before them fail.
143 domerge = True
146 domerge = True
144 if not hasnode(repo, node):
147 if not hasnode(repo, node):
145 repo.pull(source, heads=[node])
148 repo.pull(source, heads=[node])
146
149
147 skipmerge = False
150 skipmerge = False
148 if parents[1] != revlog.nullid:
151 if parents[1] != revlog.nullid:
149 if not opts.get('parent'):
152 if not opts.get('parent'):
150 self.ui.note(_('skipping merge changeset %s:%s\n')
153 self.ui.note(_('skipping merge changeset %s:%s\n')
151 % (rev, short(node)))
154 % (rev, short(node)))
152 skipmerge = True
155 skipmerge = True
153 else:
156 else:
154 parent = source.lookup(opts['parent'])
157 parent = source.lookup(opts['parent'])
155 if parent not in parents:
158 if parent not in parents:
156 raise util.Abort(_('%s is not a parent of %s') %
159 raise util.Abort(_('%s is not a parent of %s') %
157 (short(parent), short(node)))
160 (short(parent), short(node)))
158 else:
161 else:
159 parent = parents[0]
162 parent = parents[0]
160
163
161 if skipmerge:
164 if skipmerge:
162 patchfile = None
165 patchfile = None
163 else:
166 else:
164 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
167 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
165 fp = os.fdopen(fd, 'w')
168 fp = os.fdopen(fd, 'w')
166 gen = patch.diff(source, parent, node, opts=diffopts)
169 gen = patch.diff(source, parent, node, opts=diffopts)
167 for chunk in gen:
170 for chunk in gen:
168 fp.write(chunk)
171 fp.write(chunk)
169 fp.close()
172 fp.close()
170
173
171 del revmap[rev]
174 del revmap[rev]
172 if patchfile or domerge:
175 if patchfile or domerge:
173 try:
176 try:
174 n = self.applyone(repo, node,
177 try:
175 source.changelog.read(node),
178 n = self.applyone(repo, node,
176 patchfile, merge=domerge,
179 source.changelog.read(node),
177 log=opts.get('log'),
180 patchfile, merge=domerge,
178 filter=opts.get('filter'))
181 log=opts.get('log'),
182 filter=opts.get('filter'))
183 except TransplantError:
184 # Do not rollback, it is up to the user to
185 # fix the merge or cancel everything
186 tr.close()
187 raise
179 if n and domerge:
188 if n and domerge:
180 self.ui.status(_('%s merged at %s\n') % (revstr,
189 self.ui.status(_('%s merged at %s\n') % (revstr,
181 short(n)))
190 short(n)))
182 elif n:
191 elif n:
183 self.ui.status(_('%s transplanted to %s\n')
192 self.ui.status(_('%s transplanted to %s\n')
184 % (short(node),
193 % (short(node),
185 short(n)))
194 short(n)))
186 finally:
195 finally:
187 if patchfile:
196 if patchfile:
188 os.unlink(patchfile)
197 os.unlink(patchfile)
189 tr.close()
198 tr.close()
190 if pulls:
199 if pulls:
191 repo.pull(source, heads=pulls)
200 repo.pull(source, heads=pulls)
192 merge.update(repo, pulls[-1], False, False, None)
201 merge.update(repo, pulls[-1], False, False, None)
193 finally:
202 finally:
194 self.saveseries(revmap, merges)
203 self.saveseries(revmap, merges)
195 self.transplants.write()
204 self.transplants.write()
196 if tr:
205 if tr:
197 tr.release()
206 tr.release()
198 lock.release()
207 lock.release()
199 wlock.release()
208 wlock.release()
200
209
201 def filter(self, filter, node, changelog, patchfile):
210 def filter(self, filter, node, changelog, patchfile):
202 '''arbitrarily rewrite changeset before applying it'''
211 '''arbitrarily rewrite changeset before applying it'''
203
212
204 self.ui.status(_('filtering %s\n') % patchfile)
213 self.ui.status(_('filtering %s\n') % patchfile)
205 user, date, msg = (changelog[1], changelog[2], changelog[4])
214 user, date, msg = (changelog[1], changelog[2], changelog[4])
206 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
215 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
207 fp = os.fdopen(fd, 'w')
216 fp = os.fdopen(fd, 'w')
208 fp.write("# HG changeset patch\n")
217 fp.write("# HG changeset patch\n")
209 fp.write("# User %s\n" % user)
218 fp.write("# User %s\n" % user)
210 fp.write("# Date %d %d\n" % date)
219 fp.write("# Date %d %d\n" % date)
211 fp.write(msg + '\n')
220 fp.write(msg + '\n')
212 fp.close()
221 fp.close()
213
222
214 try:
223 try:
215 util.system('%s %s %s' % (filter, util.shellquote(headerfile),
224 util.system('%s %s %s' % (filter, util.shellquote(headerfile),
216 util.shellquote(patchfile)),
225 util.shellquote(patchfile)),
217 environ={'HGUSER': changelog[1],
226 environ={'HGUSER': changelog[1],
218 'HGREVISION': revlog.hex(node),
227 'HGREVISION': revlog.hex(node),
219 },
228 },
220 onerr=util.Abort, errprefix=_('filter failed'),
229 onerr=util.Abort, errprefix=_('filter failed'),
221 out=self.ui.fout)
230 out=self.ui.fout)
222 user, date, msg = self.parselog(file(headerfile))[1:4]
231 user, date, msg = self.parselog(file(headerfile))[1:4]
223 finally:
232 finally:
224 os.unlink(headerfile)
233 os.unlink(headerfile)
225
234
226 return (user, date, msg)
235 return (user, date, msg)
227
236
228 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
237 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
229 filter=None):
238 filter=None):
230 '''apply the patch in patchfile to the repository as a transplant'''
239 '''apply the patch in patchfile to the repository as a transplant'''
231 (manifest, user, (time, timezone), files, message) = cl[:5]
240 (manifest, user, (time, timezone), files, message) = cl[:5]
232 date = "%d %d" % (time, timezone)
241 date = "%d %d" % (time, timezone)
233 extra = {'transplant_source': node}
242 extra = {'transplant_source': node}
234 if filter:
243 if filter:
235 (user, date, message) = self.filter(filter, node, cl, patchfile)
244 (user, date, message) = self.filter(filter, node, cl, patchfile)
236
245
237 if log:
246 if log:
238 # we don't translate messages inserted into commits
247 # we don't translate messages inserted into commits
239 message += '\n(transplanted from %s)' % revlog.hex(node)
248 message += '\n(transplanted from %s)' % revlog.hex(node)
240
249
241 self.ui.status(_('applying %s\n') % short(node))
250 self.ui.status(_('applying %s\n') % short(node))
242 self.ui.note('%s %s\n%s\n' % (user, date, message))
251 self.ui.note('%s %s\n%s\n' % (user, date, message))
243
252
244 if not patchfile and not merge:
253 if not patchfile and not merge:
245 raise util.Abort(_('can only omit patchfile if merging'))
254 raise util.Abort(_('can only omit patchfile if merging'))
246 if patchfile:
255 if patchfile:
247 try:
256 try:
248 files = set()
257 files = set()
249 patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
258 patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
250 files = list(files)
259 files = list(files)
251 if not files:
260 if not files:
252 self.ui.warn(_('%s: empty changeset') % revlog.hex(node))
261 self.ui.warn(_('%s: empty changeset') % revlog.hex(node))
253 return None
262 return None
254 except Exception, inst:
263 except Exception, inst:
255 seriespath = os.path.join(self.path, 'series')
264 seriespath = os.path.join(self.path, 'series')
256 if os.path.exists(seriespath):
265 if os.path.exists(seriespath):
257 os.unlink(seriespath)
266 os.unlink(seriespath)
258 p1 = repo.dirstate.p1()
267 p1 = repo.dirstate.p1()
259 p2 = node
268 p2 = node
260 self.log(user, date, message, p1, p2, merge=merge)
269 self.log(user, date, message, p1, p2, merge=merge)
261 self.ui.write(str(inst) + '\n')
270 self.ui.write(str(inst) + '\n')
262 raise util.Abort(_('fix up the merge and run '
271 raise TransplantError(_('fix up the merge and run '
263 'hg transplant --continue'))
272 'hg transplant --continue'))
264 else:
273 else:
265 files = None
274 files = None
266 if merge:
275 if merge:
267 p1, p2 = repo.dirstate.parents()
276 p1, p2 = repo.dirstate.parents()
268 repo.dirstate.setparents(p1, node)
277 repo.dirstate.setparents(p1, node)
269 m = match.always(repo.root, '')
278 m = match.always(repo.root, '')
270 else:
279 else:
271 m = match.exact(repo.root, '', files)
280 m = match.exact(repo.root, '', files)
272
281
273 n = repo.commit(message, user, date, extra=extra, match=m,
282 n = repo.commit(message, user, date, extra=extra, match=m,
274 editor=self.editor)
283 editor=self.editor)
275 if not n:
284 if not n:
276 # Crash here to prevent an unclear crash later, in
285 # Crash here to prevent an unclear crash later, in
277 # transplants.write(). This can happen if patch.patch()
286 # transplants.write(). This can happen if patch.patch()
278 # does nothing but claims success or if repo.status() fails
287 # does nothing but claims success or if repo.status() fails
279 # to report changes done by patch.patch(). These both
288 # to report changes done by patch.patch(). These both
280 # appear to be bugs in other parts of Mercurial, but dying
289 # appear to be bugs in other parts of Mercurial, but dying
281 # here, as soon as we can detect the problem, is preferable
290 # here, as soon as we can detect the problem, is preferable
282 # to silently dropping changesets on the floor.
291 # to silently dropping changesets on the floor.
283 raise RuntimeError('nothing committed after transplant')
292 raise RuntimeError('nothing committed after transplant')
284 if not merge:
293 if not merge:
285 self.transplants.set(n, node)
294 self.transplants.set(n, node)
286
295
287 return n
296 return n
288
297
289 def resume(self, repo, source, opts=None):
298 def resume(self, repo, source, opts=None):
290 '''recover last transaction and apply remaining changesets'''
299 '''recover last transaction and apply remaining changesets'''
291 if os.path.exists(os.path.join(self.path, 'journal')):
300 if os.path.exists(os.path.join(self.path, 'journal')):
292 n, node = self.recover(repo)
301 n, node = self.recover(repo)
293 self.ui.status(_('%s transplanted as %s\n') % (short(node),
302 self.ui.status(_('%s transplanted as %s\n') % (short(node),
294 short(n)))
303 short(n)))
295 seriespath = os.path.join(self.path, 'series')
304 seriespath = os.path.join(self.path, 'series')
296 if not os.path.exists(seriespath):
305 if not os.path.exists(seriespath):
297 self.transplants.write()
306 self.transplants.write()
298 return
307 return
299 nodes, merges = self.readseries()
308 nodes, merges = self.readseries()
300 revmap = {}
309 revmap = {}
301 for n in nodes:
310 for n in nodes:
302 revmap[source.changelog.rev(n)] = n
311 revmap[source.changelog.rev(n)] = n
303 os.unlink(seriespath)
312 os.unlink(seriespath)
304
313
305 self.apply(repo, source, revmap, merges, opts)
314 self.apply(repo, source, revmap, merges, opts)
306
315
307 def recover(self, repo):
316 def recover(self, repo):
308 '''commit working directory using journal metadata'''
317 '''commit working directory using journal metadata'''
309 node, user, date, message, parents = self.readlog()
318 node, user, date, message, parents = self.readlog()
310 merge = False
319 merge = False
311
320
312 if not user or not date or not message or not parents[0]:
321 if not user or not date or not message or not parents[0]:
313 raise util.Abort(_('transplant log file is corrupt'))
322 raise util.Abort(_('transplant log file is corrupt'))
314
323
315 parent = parents[0]
324 parent = parents[0]
316 if len(parents) > 1:
325 if len(parents) > 1:
317 if opts.get('parent'):
326 if opts.get('parent'):
318 parent = source.lookup(opts['parent'])
327 parent = source.lookup(opts['parent'])
319 if parent not in parents:
328 if parent not in parents:
320 raise util.Abort(_('%s is not a parent of %s') %
329 raise util.Abort(_('%s is not a parent of %s') %
321 (short(parent), short(node)))
330 (short(parent), short(node)))
322 else:
331 else:
323 merge = True
332 merge = True
324
333
325 extra = {'transplant_source': node}
334 extra = {'transplant_source': node}
326 wlock = repo.wlock()
335 wlock = repo.wlock()
327 try:
336 try:
328 p1, p2 = repo.dirstate.parents()
337 p1, p2 = repo.dirstate.parents()
329 if p1 != parent:
338 if p1 != parent:
330 raise util.Abort(
339 raise util.Abort(
331 _('working dir not at transplant parent %s') %
340 _('working dir not at transplant parent %s') %
332 revlog.hex(parent))
341 revlog.hex(parent))
333 if merge:
342 if merge:
334 repo.dirstate.setparents(p1, parents[1])
343 repo.dirstate.setparents(p1, parents[1])
335 n = repo.commit(message, user, date, extra=extra,
344 n = repo.commit(message, user, date, extra=extra,
336 editor=self.editor)
345 editor=self.editor)
337 if not n:
346 if not n:
338 raise util.Abort(_('commit failed'))
347 raise util.Abort(_('commit failed'))
339 if not merge:
348 if not merge:
340 self.transplants.set(n, node)
349 self.transplants.set(n, node)
341 self.unlog()
350 self.unlog()
342
351
343 return n, node
352 return n, node
344 finally:
353 finally:
345 wlock.release()
354 wlock.release()
346
355
347 def readseries(self):
356 def readseries(self):
348 nodes = []
357 nodes = []
349 merges = []
358 merges = []
350 cur = nodes
359 cur = nodes
351 for line in self.opener.read('series').splitlines():
360 for line in self.opener.read('series').splitlines():
352 if line.startswith('# Merges'):
361 if line.startswith('# Merges'):
353 cur = merges
362 cur = merges
354 continue
363 continue
355 cur.append(revlog.bin(line))
364 cur.append(revlog.bin(line))
356
365
357 return (nodes, merges)
366 return (nodes, merges)
358
367
359 def saveseries(self, revmap, merges):
368 def saveseries(self, revmap, merges):
360 if not revmap:
369 if not revmap:
361 return
370 return
362
371
363 if not os.path.isdir(self.path):
372 if not os.path.isdir(self.path):
364 os.mkdir(self.path)
373 os.mkdir(self.path)
365 series = self.opener('series', 'w')
374 series = self.opener('series', 'w')
366 for rev in sorted(revmap):
375 for rev in sorted(revmap):
367 series.write(revlog.hex(revmap[rev]) + '\n')
376 series.write(revlog.hex(revmap[rev]) + '\n')
368 if merges:
377 if merges:
369 series.write('# Merges\n')
378 series.write('# Merges\n')
370 for m in merges:
379 for m in merges:
371 series.write(revlog.hex(m) + '\n')
380 series.write(revlog.hex(m) + '\n')
372 series.close()
381 series.close()
373
382
374 def parselog(self, fp):
383 def parselog(self, fp):
375 parents = []
384 parents = []
376 message = []
385 message = []
377 node = revlog.nullid
386 node = revlog.nullid
378 inmsg = False
387 inmsg = False
379 user = None
388 user = None
380 date = None
389 date = None
381 for line in fp.read().splitlines():
390 for line in fp.read().splitlines():
382 if inmsg:
391 if inmsg:
383 message.append(line)
392 message.append(line)
384 elif line.startswith('# User '):
393 elif line.startswith('# User '):
385 user = line[7:]
394 user = line[7:]
386 elif line.startswith('# Date '):
395 elif line.startswith('# Date '):
387 date = line[7:]
396 date = line[7:]
388 elif line.startswith('# Node ID '):
397 elif line.startswith('# Node ID '):
389 node = revlog.bin(line[10:])
398 node = revlog.bin(line[10:])
390 elif line.startswith('# Parent '):
399 elif line.startswith('# Parent '):
391 parents.append(revlog.bin(line[9:]))
400 parents.append(revlog.bin(line[9:]))
392 elif not line.startswith('# '):
401 elif not line.startswith('# '):
393 inmsg = True
402 inmsg = True
394 message.append(line)
403 message.append(line)
395 if None in (user, date):
404 if None in (user, date):
396 raise util.Abort(_("filter corrupted changeset (no user or date)"))
405 raise util.Abort(_("filter corrupted changeset (no user or date)"))
397 return (node, user, date, '\n'.join(message), parents)
406 return (node, user, date, '\n'.join(message), parents)
398
407
399 def log(self, user, date, message, p1, p2, merge=False):
408 def log(self, user, date, message, p1, p2, merge=False):
400 '''journal changelog metadata for later recover'''
409 '''journal changelog metadata for later recover'''
401
410
402 if not os.path.isdir(self.path):
411 if not os.path.isdir(self.path):
403 os.mkdir(self.path)
412 os.mkdir(self.path)
404 fp = self.opener('journal', 'w')
413 fp = self.opener('journal', 'w')
405 fp.write('# User %s\n' % user)
414 fp.write('# User %s\n' % user)
406 fp.write('# Date %s\n' % date)
415 fp.write('# Date %s\n' % date)
407 fp.write('# Node ID %s\n' % revlog.hex(p2))
416 fp.write('# Node ID %s\n' % revlog.hex(p2))
408 fp.write('# Parent ' + revlog.hex(p1) + '\n')
417 fp.write('# Parent ' + revlog.hex(p1) + '\n')
409 if merge:
418 if merge:
410 fp.write('# Parent ' + revlog.hex(p2) + '\n')
419 fp.write('# Parent ' + revlog.hex(p2) + '\n')
411 fp.write(message.rstrip() + '\n')
420 fp.write(message.rstrip() + '\n')
412 fp.close()
421 fp.close()
413
422
414 def readlog(self):
423 def readlog(self):
415 return self.parselog(self.opener('journal'))
424 return self.parselog(self.opener('journal'))
416
425
417 def unlog(self):
426 def unlog(self):
418 '''remove changelog journal'''
427 '''remove changelog journal'''
419 absdst = os.path.join(self.path, 'journal')
428 absdst = os.path.join(self.path, 'journal')
420 if os.path.exists(absdst):
429 if os.path.exists(absdst):
421 os.unlink(absdst)
430 os.unlink(absdst)
422
431
423 def transplantfilter(self, repo, source, root):
432 def transplantfilter(self, repo, source, root):
424 def matchfn(node):
433 def matchfn(node):
425 if self.applied(repo, node, root):
434 if self.applied(repo, node, root):
426 return False
435 return False
427 if source.changelog.parents(node)[1] != revlog.nullid:
436 if source.changelog.parents(node)[1] != revlog.nullid:
428 return False
437 return False
429 extra = source.changelog.read(node)[5]
438 extra = source.changelog.read(node)[5]
430 cnode = extra.get('transplant_source')
439 cnode = extra.get('transplant_source')
431 if cnode and self.applied(repo, cnode, root):
440 if cnode and self.applied(repo, cnode, root):
432 return False
441 return False
433 return True
442 return True
434
443
435 return matchfn
444 return matchfn
436
445
437 def hasnode(repo, node):
446 def hasnode(repo, node):
438 try:
447 try:
439 return repo.changelog.rev(node) is not None
448 return repo.changelog.rev(node) is not None
440 except error.RevlogError:
449 except error.RevlogError:
441 return False
450 return False
442
451
443 def browserevs(ui, repo, nodes, opts):
452 def browserevs(ui, repo, nodes, opts):
444 '''interactively transplant changesets'''
453 '''interactively transplant changesets'''
445 def browsehelp(ui):
454 def browsehelp(ui):
446 ui.write(_('y: transplant this changeset\n'
455 ui.write(_('y: transplant this changeset\n'
447 'n: skip this changeset\n'
456 'n: skip this changeset\n'
448 'm: merge at this changeset\n'
457 'm: merge at this changeset\n'
449 'p: show patch\n'
458 'p: show patch\n'
450 'c: commit selected changesets\n'
459 'c: commit selected changesets\n'
451 'q: cancel transplant\n'
460 'q: cancel transplant\n'
452 '?: show this help\n'))
461 '?: show this help\n'))
453
462
454 displayer = cmdutil.show_changeset(ui, repo, opts)
463 displayer = cmdutil.show_changeset(ui, repo, opts)
455 transplants = []
464 transplants = []
456 merges = []
465 merges = []
457 for node in nodes:
466 for node in nodes:
458 displayer.show(repo[node])
467 displayer.show(repo[node])
459 action = None
468 action = None
460 while not action:
469 while not action:
461 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
470 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
462 if action == '?':
471 if action == '?':
463 browsehelp(ui)
472 browsehelp(ui)
464 action = None
473 action = None
465 elif action == 'p':
474 elif action == 'p':
466 parent = repo.changelog.parents(node)[0]
475 parent = repo.changelog.parents(node)[0]
467 for chunk in patch.diff(repo, parent, node):
476 for chunk in patch.diff(repo, parent, node):
468 ui.write(chunk)
477 ui.write(chunk)
469 action = None
478 action = None
470 elif action not in ('y', 'n', 'm', 'c', 'q'):
479 elif action not in ('y', 'n', 'm', 'c', 'q'):
471 ui.write(_('no such option\n'))
480 ui.write(_('no such option\n'))
472 action = None
481 action = None
473 if action == 'y':
482 if action == 'y':
474 transplants.append(node)
483 transplants.append(node)
475 elif action == 'm':
484 elif action == 'm':
476 merges.append(node)
485 merges.append(node)
477 elif action == 'c':
486 elif action == 'c':
478 break
487 break
479 elif action == 'q':
488 elif action == 'q':
480 transplants = ()
489 transplants = ()
481 merges = ()
490 merges = ()
482 break
491 break
483 displayer.close()
492 displayer.close()
484 return (transplants, merges)
493 return (transplants, merges)
485
494
486 @command('transplant',
495 @command('transplant',
487 [('s', 'source', '', _('pull patches from REPO'), _('REPO')),
496 [('s', 'source', '', _('pull patches from REPO'), _('REPO')),
488 ('b', 'branch', [],
497 ('b', 'branch', [],
489 _('pull patches from branch BRANCH'), _('BRANCH')),
498 _('pull patches from branch BRANCH'), _('BRANCH')),
490 ('a', 'all', None, _('pull all changesets up to BRANCH')),
499 ('a', 'all', None, _('pull all changesets up to BRANCH')),
491 ('p', 'prune', [], _('skip over REV'), _('REV')),
500 ('p', 'prune', [], _('skip over REV'), _('REV')),
492 ('m', 'merge', [], _('merge at REV'), _('REV')),
501 ('m', 'merge', [], _('merge at REV'), _('REV')),
493 ('', 'parent', '',
502 ('', 'parent', '',
494 _('parent to choose when transplanting merge'), _('REV')),
503 _('parent to choose when transplanting merge'), _('REV')),
495 ('e', 'edit', False, _('invoke editor on commit messages')),
504 ('e', 'edit', False, _('invoke editor on commit messages')),
496 ('', 'log', None, _('append transplant info to log message')),
505 ('', 'log', None, _('append transplant info to log message')),
497 ('c', 'continue', None, _('continue last transplant session '
506 ('c', 'continue', None, _('continue last transplant session '
498 'after repair')),
507 'after repair')),
499 ('', 'filter', '',
508 ('', 'filter', '',
500 _('filter changesets through command'), _('CMD'))],
509 _('filter changesets through command'), _('CMD'))],
501 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
510 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
502 '[-m REV] [REV]...'))
511 '[-m REV] [REV]...'))
503 def transplant(ui, repo, *revs, **opts):
512 def transplant(ui, repo, *revs, **opts):
504 '''transplant changesets from another branch
513 '''transplant changesets from another branch
505
514
506 Selected changesets will be applied on top of the current working
515 Selected changesets will be applied on top of the current working
507 directory with the log of the original changeset. The changesets
516 directory with the log of the original changeset. The changesets
508 are copied and will thus appear twice in the history. Use the
517 are copied and will thus appear twice in the history. Use the
509 rebase extension instead if you want to move a whole branch of
518 rebase extension instead if you want to move a whole branch of
510 unpublished changesets.
519 unpublished changesets.
511
520
512 If --log is specified, log messages will have a comment appended
521 If --log is specified, log messages will have a comment appended
513 of the form::
522 of the form::
514
523
515 (transplanted from CHANGESETHASH)
524 (transplanted from CHANGESETHASH)
516
525
517 You can rewrite the changelog message with the --filter option.
526 You can rewrite the changelog message with the --filter option.
518 Its argument will be invoked with the current changelog message as
527 Its argument will be invoked with the current changelog message as
519 $1 and the patch as $2.
528 $1 and the patch as $2.
520
529
521 If --source/-s is specified, selects changesets from the named
530 If --source/-s is specified, selects changesets from the named
522 repository. If --branch/-b is specified, selects changesets from
531 repository. If --branch/-b is specified, selects changesets from
523 the branch holding the named revision, up to that revision. If
532 the branch holding the named revision, up to that revision. If
524 --all/-a is specified, all changesets on the branch will be
533 --all/-a is specified, all changesets on the branch will be
525 transplanted, otherwise you will be prompted to select the
534 transplanted, otherwise you will be prompted to select the
526 changesets you want.
535 changesets you want.
527
536
528 :hg:`transplant --branch REVISION --all` will transplant the
537 :hg:`transplant --branch REVISION --all` will transplant the
529 selected branch (up to the named revision) onto your current
538 selected branch (up to the named revision) onto your current
530 working directory.
539 working directory.
531
540
532 You can optionally mark selected transplanted changesets as merge
541 You can optionally mark selected transplanted changesets as merge
533 changesets. You will not be prompted to transplant any ancestors
542 changesets. You will not be prompted to transplant any ancestors
534 of a merged transplant, and you can merge descendants of them
543 of a merged transplant, and you can merge descendants of them
535 normally instead of transplanting them.
544 normally instead of transplanting them.
536
545
537 Merge changesets may be transplanted directly by specifying the
546 Merge changesets may be transplanted directly by specifying the
538 proper parent changeset by calling :hg:`transplant --parent`.
547 proper parent changeset by calling :hg:`transplant --parent`.
539
548
540 If no merges or revisions are provided, :hg:`transplant` will
549 If no merges or revisions are provided, :hg:`transplant` will
541 start an interactive changeset browser.
550 start an interactive changeset browser.
542
551
543 If a changeset application fails, you can fix the merge by hand
552 If a changeset application fails, you can fix the merge by hand
544 and then resume where you left off by calling :hg:`transplant
553 and then resume where you left off by calling :hg:`transplant
545 --continue/-c`.
554 --continue/-c`.
546 '''
555 '''
547 def incwalk(repo, csets, match=util.always):
556 def incwalk(repo, csets, match=util.always):
548 for node in csets:
557 for node in csets:
549 if match(node):
558 if match(node):
550 yield node
559 yield node
551
560
552 def transplantwalk(repo, root, branches, match=util.always):
561 def transplantwalk(repo, root, branches, match=util.always):
553 if not branches:
562 if not branches:
554 branches = repo.heads()
563 branches = repo.heads()
555 ancestors = []
564 ancestors = []
556 for branch in branches:
565 for branch in branches:
557 ancestors.append(repo.changelog.ancestor(root, branch))
566 ancestors.append(repo.changelog.ancestor(root, branch))
558 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
567 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
559 if match(node):
568 if match(node):
560 yield node
569 yield node
561
570
562 def checkopts(opts, revs):
571 def checkopts(opts, revs):
563 if opts.get('continue'):
572 if opts.get('continue'):
564 if opts.get('branch') or opts.get('all') or opts.get('merge'):
573 if opts.get('branch') or opts.get('all') or opts.get('merge'):
565 raise util.Abort(_('--continue is incompatible with '
574 raise util.Abort(_('--continue is incompatible with '
566 'branch, all or merge'))
575 'branch, all or merge'))
567 return
576 return
568 if not (opts.get('source') or revs or
577 if not (opts.get('source') or revs or
569 opts.get('merge') or opts.get('branch')):
578 opts.get('merge') or opts.get('branch')):
570 raise util.Abort(_('no source URL, branch tag or revision '
579 raise util.Abort(_('no source URL, branch tag or revision '
571 'list provided'))
580 'list provided'))
572 if opts.get('all'):
581 if opts.get('all'):
573 if not opts.get('branch'):
582 if not opts.get('branch'):
574 raise util.Abort(_('--all requires a branch revision'))
583 raise util.Abort(_('--all requires a branch revision'))
575 if revs:
584 if revs:
576 raise util.Abort(_('--all is incompatible with a '
585 raise util.Abort(_('--all is incompatible with a '
577 'revision list'))
586 'revision list'))
578
587
579 checkopts(opts, revs)
588 checkopts(opts, revs)
580
589
581 if not opts.get('log'):
590 if not opts.get('log'):
582 opts['log'] = ui.config('transplant', 'log')
591 opts['log'] = ui.config('transplant', 'log')
583 if not opts.get('filter'):
592 if not opts.get('filter'):
584 opts['filter'] = ui.config('transplant', 'filter')
593 opts['filter'] = ui.config('transplant', 'filter')
585
594
586 tp = transplanter(ui, repo)
595 tp = transplanter(ui, repo)
587 if opts.get('edit'):
596 if opts.get('edit'):
588 tp.editor = cmdutil.commitforceeditor
597 tp.editor = cmdutil.commitforceeditor
589
598
590 p1, p2 = repo.dirstate.parents()
599 p1, p2 = repo.dirstate.parents()
591 if len(repo) > 0 and p1 == revlog.nullid:
600 if len(repo) > 0 and p1 == revlog.nullid:
592 raise util.Abort(_('no revision checked out'))
601 raise util.Abort(_('no revision checked out'))
593 if not opts.get('continue'):
602 if not opts.get('continue'):
594 if p2 != revlog.nullid:
603 if p2 != revlog.nullid:
595 raise util.Abort(_('outstanding uncommitted merges'))
604 raise util.Abort(_('outstanding uncommitted merges'))
596 m, a, r, d = repo.status()[:4]
605 m, a, r, d = repo.status()[:4]
597 if m or a or r or d:
606 if m or a or r or d:
598 raise util.Abort(_('outstanding local changes'))
607 raise util.Abort(_('outstanding local changes'))
599
608
600 sourcerepo = opts.get('source')
609 sourcerepo = opts.get('source')
601 if sourcerepo:
610 if sourcerepo:
602 source = hg.peer(ui, opts, ui.expandpath(sourcerepo))
611 source = hg.peer(ui, opts, ui.expandpath(sourcerepo))
603 branches = map(source.lookup, opts.get('branch', ()))
612 branches = map(source.lookup, opts.get('branch', ()))
604 source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, source,
613 source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, source,
605 onlyheads=branches, force=True)
614 onlyheads=branches, force=True)
606 else:
615 else:
607 source = repo
616 source = repo
608 branches = map(source.lookup, opts.get('branch', ()))
617 branches = map(source.lookup, opts.get('branch', ()))
609 cleanupfn = None
618 cleanupfn = None
610
619
611 try:
620 try:
612 if opts.get('continue'):
621 if opts.get('continue'):
613 tp.resume(repo, source, opts)
622 tp.resume(repo, source, opts)
614 return
623 return
615
624
616 tf = tp.transplantfilter(repo, source, p1)
625 tf = tp.transplantfilter(repo, source, p1)
617 if opts.get('prune'):
626 if opts.get('prune'):
618 prune = [source.lookup(r)
627 prune = [source.lookup(r)
619 for r in scmutil.revrange(source, opts.get('prune'))]
628 for r in scmutil.revrange(source, opts.get('prune'))]
620 matchfn = lambda x: tf(x) and x not in prune
629 matchfn = lambda x: tf(x) and x not in prune
621 else:
630 else:
622 matchfn = tf
631 matchfn = tf
623 merges = map(source.lookup, opts.get('merge', ()))
632 merges = map(source.lookup, opts.get('merge', ()))
624 revmap = {}
633 revmap = {}
625 if revs:
634 if revs:
626 for r in scmutil.revrange(source, revs):
635 for r in scmutil.revrange(source, revs):
627 revmap[int(r)] = source.lookup(r)
636 revmap[int(r)] = source.lookup(r)
628 elif opts.get('all') or not merges:
637 elif opts.get('all') or not merges:
629 if source != repo:
638 if source != repo:
630 alltransplants = incwalk(source, csets, match=matchfn)
639 alltransplants = incwalk(source, csets, match=matchfn)
631 else:
640 else:
632 alltransplants = transplantwalk(source, p1, branches,
641 alltransplants = transplantwalk(source, p1, branches,
633 match=matchfn)
642 match=matchfn)
634 if opts.get('all'):
643 if opts.get('all'):
635 revs = alltransplants
644 revs = alltransplants
636 else:
645 else:
637 revs, newmerges = browserevs(ui, source, alltransplants, opts)
646 revs, newmerges = browserevs(ui, source, alltransplants, opts)
638 merges.extend(newmerges)
647 merges.extend(newmerges)
639 for r in revs:
648 for r in revs:
640 revmap[source.changelog.rev(r)] = r
649 revmap[source.changelog.rev(r)] = r
641 for r in merges:
650 for r in merges:
642 revmap[source.changelog.rev(r)] = r
651 revmap[source.changelog.rev(r)] = r
643
652
644 tp.apply(repo, source, revmap, merges, opts)
653 tp.apply(repo, source, revmap, merges, opts)
645 finally:
654 finally:
646 if cleanupfn:
655 if cleanupfn:
647 cleanupfn()
656 cleanupfn()
648
657
649 def revsettransplanted(repo, subset, x):
658 def revsettransplanted(repo, subset, x):
650 """``transplanted([set])``
659 """``transplanted([set])``
651 Transplanted changesets in set, or all transplanted changesets.
660 Transplanted changesets in set, or all transplanted changesets.
652 """
661 """
653 if x:
662 if x:
654 s = revset.getset(repo, subset, x)
663 s = revset.getset(repo, subset, x)
655 else:
664 else:
656 s = subset
665 s = subset
657 return [r for r in s if repo[r].extra().get('transplant_source')]
666 return [r for r in s if repo[r].extra().get('transplant_source')]
658
667
659 def kwtransplanted(repo, ctx, **args):
668 def kwtransplanted(repo, ctx, **args):
660 """:transplanted: String. The node identifier of the transplanted
669 """:transplanted: String. The node identifier of the transplanted
661 changeset if any."""
670 changeset if any."""
662 n = ctx.extra().get('transplant_source')
671 n = ctx.extra().get('transplant_source')
663 return n and revlog.hex(n) or ''
672 return n and revlog.hex(n) or ''
664
673
665 def extsetup(ui):
674 def extsetup(ui):
666 revset.symbols['transplanted'] = revsettransplanted
675 revset.symbols['transplanted'] = revsettransplanted
667 templatekw.keywords['transplanted'] = kwtransplanted
676 templatekw.keywords['transplanted'] = kwtransplanted
668
677
669 # tell hggettext to extract docstrings from these functions:
678 # tell hggettext to extract docstrings from these functions:
670 i18nfunctions = [revsettransplanted, kwtransplanted]
679 i18nfunctions = [revsettransplanted, kwtransplanted]
@@ -1,480 +1,511 b''
1 $ "$TESTDIR/hghave" serve || exit 80
1 $ "$TESTDIR/hghave" serve || exit 80
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 $ echo r1 > r1
10 $ echo r1 > r1
11 $ hg ci -Amr1 -d'0 0'
11 $ hg ci -Amr1 -d'0 0'
12 adding r1
12 adding r1
13 $ echo r2 > r2
13 $ echo r2 > r2
14 $ hg ci -Amr2 -d'1 0'
14 $ hg ci -Amr2 -d'1 0'
15 adding r2
15 adding r2
16 $ hg up 0
16 $ hg up 0
17 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
17 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
18
18
19 $ echo b1 > b1
19 $ echo b1 > b1
20 $ hg ci -Amb1 -d '0 0'
20 $ hg ci -Amb1 -d '0 0'
21 adding b1
21 adding b1
22 created new head
22 created new head
23 $ echo b2 > b2
23 $ echo b2 > b2
24 $ hg ci -Amb2 -d '1 0'
24 $ hg ci -Amb2 -d '1 0'
25 adding b2
25 adding b2
26 $ echo b3 > b3
26 $ echo b3 > b3
27 $ hg ci -Amb3 -d '2 0'
27 $ hg ci -Amb3 -d '2 0'
28 adding b3
28 adding b3
29
29
30 $ hg log --template '{rev} {parents} {desc}\n'
30 $ hg log --template '{rev} {parents} {desc}\n'
31 4 b3
31 4 b3
32 3 b2
32 3 b2
33 2 0:17ab29e464c6 b1
33 2 0:17ab29e464c6 b1
34 1 r2
34 1 r2
35 0 r1
35 0 r1
36
36
37 $ hg clone . ../rebase
37 $ hg clone . ../rebase
38 updating to branch default
38 updating to branch default
39 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
39 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
40 $ cd ../rebase
40 $ cd ../rebase
41
41
42 $ hg up -C 1
42 $ hg up -C 1
43 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
43 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
44
44
45 rebase b onto r1
45 rebase b onto r1
46
46
47 $ hg transplant -a -b tip
47 $ hg transplant -a -b tip
48 applying 37a1297eb21b
48 applying 37a1297eb21b
49 37a1297eb21b transplanted to e234d668f844
49 37a1297eb21b transplanted to e234d668f844
50 applying 722f4667af76
50 applying 722f4667af76
51 722f4667af76 transplanted to 539f377d78df
51 722f4667af76 transplanted to 539f377d78df
52 applying a53251cdf717
52 applying a53251cdf717
53 a53251cdf717 transplanted to ffd6818a3975
53 a53251cdf717 transplanted to ffd6818a3975
54 $ hg log --template '{rev} {parents} {desc}\n'
54 $ hg log --template '{rev} {parents} {desc}\n'
55 7 b3
55 7 b3
56 6 b2
56 6 b2
57 5 1:d11e3596cc1a b1
57 5 1:d11e3596cc1a b1
58 4 b3
58 4 b3
59 3 b2
59 3 b2
60 2 0:17ab29e464c6 b1
60 2 0:17ab29e464c6 b1
61 1 r2
61 1 r2
62 0 r1
62 0 r1
63
63
64 test transplanted revset
64 test transplanted revset
65
65
66 $ hg log -r 'transplanted()' --template '{rev} {parents} {desc}\n'
66 $ hg log -r 'transplanted()' --template '{rev} {parents} {desc}\n'
67 5 1:d11e3596cc1a b1
67 5 1:d11e3596cc1a b1
68 6 b2
68 6 b2
69 7 b3
69 7 b3
70 $ hg help revsets | grep transplanted
70 $ hg help revsets | grep transplanted
71 "transplanted([set])"
71 "transplanted([set])"
72 Transplanted changesets in set, or all transplanted changesets.
72 Transplanted changesets in set, or all transplanted changesets.
73
73
74 test tranplanted keyword
74 test tranplanted keyword
75
75
76 $ hg log --template '{rev} {transplanted}\n'
76 $ hg log --template '{rev} {transplanted}\n'
77 7 a53251cdf717679d1907b289f991534be05c997a
77 7 a53251cdf717679d1907b289f991534be05c997a
78 6 722f4667af767100cb15b6a79324bf8abbfe1ef4
78 6 722f4667af767100cb15b6a79324bf8abbfe1ef4
79 5 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21
79 5 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21
80 4
80 4
81 3
81 3
82 2
82 2
83 1
83 1
84 0
84 0
85
85
86 rollback the transplant
86 rollback the transplant
87 $ hg rollback
87 $ hg rollback
88 repository tip rolled back to revision 4 (undo transplant)
88 repository tip rolled back to revision 4 (undo transplant)
89 working directory now based on revision 1
89 working directory now based on revision 1
90 $ hg tip -q
90 $ hg tip -q
91 4:a53251cdf717
91 4:a53251cdf717
92 $ hg parents -q
92 $ hg parents -q
93 1:d11e3596cc1a
93 1:d11e3596cc1a
94 $ hg status
94 $ hg status
95 ? b1
95 ? b1
96 ? b2
96 ? b2
97 ? b3
97 ? b3
98
98
99 $ hg clone ../t ../prune
99 $ hg clone ../t ../prune
100 updating to branch default
100 updating to branch default
101 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 $ cd ../prune
102 $ cd ../prune
103
103
104 $ hg up -C 1
104 $ hg up -C 1
105 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
105 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
106
106
107 rebase b onto r1, skipping b2
107 rebase b onto r1, skipping b2
108
108
109 $ hg transplant -a -b tip -p 3
109 $ hg transplant -a -b tip -p 3
110 applying 37a1297eb21b
110 applying 37a1297eb21b
111 37a1297eb21b transplanted to e234d668f844
111 37a1297eb21b transplanted to e234d668f844
112 applying a53251cdf717
112 applying a53251cdf717
113 a53251cdf717 transplanted to 7275fda4d04f
113 a53251cdf717 transplanted to 7275fda4d04f
114 $ hg log --template '{rev} {parents} {desc}\n'
114 $ hg log --template '{rev} {parents} {desc}\n'
115 6 b3
115 6 b3
116 5 1:d11e3596cc1a b1
116 5 1:d11e3596cc1a b1
117 4 b3
117 4 b3
118 3 b2
118 3 b2
119 2 0:17ab29e464c6 b1
119 2 0:17ab29e464c6 b1
120 1 r2
120 1 r2
121 0 r1
121 0 r1
122
122
123
123
124 remote transplant
124 remote transplant
125
125
126 $ hg clone -r 1 ../t ../remote
126 $ hg clone -r 1 ../t ../remote
127 adding changesets
127 adding changesets
128 adding manifests
128 adding manifests
129 adding file changes
129 adding file changes
130 added 2 changesets with 2 changes to 2 files
130 added 2 changesets with 2 changes to 2 files
131 updating to branch default
131 updating to branch default
132 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
132 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
133 $ cd ../remote
133 $ cd ../remote
134 $ hg transplant --log -s ../t 2 4
134 $ hg transplant --log -s ../t 2 4
135 searching for changes
135 searching for changes
136 applying 37a1297eb21b
136 applying 37a1297eb21b
137 37a1297eb21b transplanted to c19cf0ccb069
137 37a1297eb21b transplanted to c19cf0ccb069
138 applying a53251cdf717
138 applying a53251cdf717
139 a53251cdf717 transplanted to f7fe5bf98525
139 a53251cdf717 transplanted to f7fe5bf98525
140 $ hg log --template '{rev} {parents} {desc}\n'
140 $ hg log --template '{rev} {parents} {desc}\n'
141 3 b3
141 3 b3
142 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
142 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
143 2 b1
143 2 b1
144 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
144 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
145 1 r2
145 1 r2
146 0 r1
146 0 r1
147
147
148 skip previous transplants
148 skip previous transplants
149
149
150 $ hg transplant -s ../t -a -b 4
150 $ hg transplant -s ../t -a -b 4
151 searching for changes
151 searching for changes
152 applying 722f4667af76
152 applying 722f4667af76
153 722f4667af76 transplanted to 47156cd86c0b
153 722f4667af76 transplanted to 47156cd86c0b
154 $ hg log --template '{rev} {parents} {desc}\n'
154 $ hg log --template '{rev} {parents} {desc}\n'
155 4 b2
155 4 b2
156 3 b3
156 3 b3
157 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
157 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
158 2 b1
158 2 b1
159 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
159 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
160 1 r2
160 1 r2
161 0 r1
161 0 r1
162
162
163 skip local changes transplanted to the source
163 skip local changes transplanted to the source
164
164
165 $ echo b4 > b4
165 $ echo b4 > b4
166 $ hg ci -Amb4 -d '3 0'
166 $ hg ci -Amb4 -d '3 0'
167 adding b4
167 adding b4
168 $ hg clone ../t ../pullback
168 $ hg clone ../t ../pullback
169 updating to branch default
169 updating to branch default
170 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
171 $ cd ../pullback
171 $ cd ../pullback
172 $ hg transplant -s ../remote -a -b tip
172 $ hg transplant -s ../remote -a -b tip
173 searching for changes
173 searching for changes
174 applying 4333daefcb15
174 applying 4333daefcb15
175 4333daefcb15 transplanted to 5f42c04e07cc
175 4333daefcb15 transplanted to 5f42c04e07cc
176
176
177
177
178 remote transplant with pull
178 remote transplant with pull
179
179
180 $ hg -R ../t serve -p $HGPORT -d --pid-file=../t.pid
180 $ hg -R ../t serve -p $HGPORT -d --pid-file=../t.pid
181 $ cat ../t.pid >> $DAEMON_PIDS
181 $ cat ../t.pid >> $DAEMON_PIDS
182
182
183 $ hg clone -r 0 ../t ../rp
183 $ hg clone -r 0 ../t ../rp
184 adding changesets
184 adding changesets
185 adding manifests
185 adding manifests
186 adding file changes
186 adding file changes
187 added 1 changesets with 1 changes to 1 files
187 added 1 changesets with 1 changes to 1 files
188 updating to branch default
188 updating to branch default
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 $ cd ../rp
190 $ cd ../rp
191 $ hg transplant -s http://localhost:$HGPORT/ 2 4
191 $ hg transplant -s http://localhost:$HGPORT/ 2 4
192 searching for changes
192 searching for changes
193 searching for changes
193 searching for changes
194 adding changesets
194 adding changesets
195 adding manifests
195 adding manifests
196 adding file changes
196 adding file changes
197 added 1 changesets with 1 changes to 1 files
197 added 1 changesets with 1 changes to 1 files
198 applying a53251cdf717
198 applying a53251cdf717
199 a53251cdf717 transplanted to 8d9279348abb
199 a53251cdf717 transplanted to 8d9279348abb
200 $ hg log --template '{rev} {parents} {desc}\n'
200 $ hg log --template '{rev} {parents} {desc}\n'
201 2 b3
201 2 b3
202 1 b1
202 1 b1
203 0 r1
203 0 r1
204
204
205 transplant --continue
205 transplant --continue
206
206
207 $ hg init ../tc
207 $ hg init ../tc
208 $ cd ../tc
208 $ cd ../tc
209 $ cat <<EOF > foo
209 $ cat <<EOF > foo
210 > foo
210 > foo
211 > bar
211 > bar
212 > baz
212 > baz
213 > EOF
213 > EOF
214 $ echo toremove > toremove
214 $ echo toremove > toremove
215 $ echo baz > baz
215 $ hg ci -Amfoo
216 $ hg ci -Amfoo
217 adding baz
216 adding foo
218 adding foo
217 adding toremove
219 adding toremove
218 $ cat <<EOF > foo
220 $ cat <<EOF > foo
219 > foo2
221 > foo2
220 > bar2
222 > bar2
221 > baz2
223 > baz2
222 > EOF
224 > EOF
223 $ rm toremove
225 $ rm toremove
224 $ echo added > added
226 $ echo added > added
225 $ hg ci -Amfoo2
227 $ hg ci -Amfoo2
226 adding added
228 adding added
227 removing toremove
229 removing toremove
228 $ echo bar > bar
230 $ echo bar > bar
231 $ cat > baz <<EOF
232 > before baz
233 > baz
234 > after baz
235 > EOF
229 $ hg ci -Ambar
236 $ hg ci -Ambar
230 adding bar
237 adding bar
231 $ echo bar2 >> bar
238 $ echo bar2 >> bar
232 $ hg ci -mbar2
239 $ hg ci -mbar2
233 $ hg up 0
240 $ hg up 0
234 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
241 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
235 $ echo foobar > foo
242 $ echo foobar > foo
236 $ hg ci -mfoobar
243 $ hg ci -mfoobar
237 created new head
244 created new head
238 $ hg transplant 1:3
245 $ hg transplant 1:3
239 applying a1e30dd1b8e7
246 applying 46ae92138f3c
240 patching file foo
247 patching file foo
241 Hunk #1 FAILED at 0
248 Hunk #1 FAILED at 0
242 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
249 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
243 patch failed to apply
250 patch failed to apply
244 abort: fix up the merge and run hg transplant --continue
251 abort: fix up the merge and run hg transplant --continue
245 [255]
252 [255]
246
253
247 transplant -c shouldn't use an old changeset
254 transplant -c shouldn't use an old changeset
248
255
249 $ hg up -C
256 $ hg up -C
250 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
251 $ rm added
258 $ rm added
252 $ hg transplant 1
259 $ hg transplant 1
253 applying a1e30dd1b8e7
260 applying 46ae92138f3c
254 patching file foo
261 patching file foo
255 Hunk #1 FAILED at 0
262 Hunk #1 FAILED at 0
256 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
263 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
257 patch failed to apply
264 patch failed to apply
258 abort: fix up the merge and run hg transplant --continue
265 abort: fix up the merge and run hg transplant --continue
259 [255]
266 [255]
260 $ hg transplant --continue
267 $ hg transplant --continue
261 a1e30dd1b8e7 transplanted as f1563cf27039
268 46ae92138f3c transplanted as 9159dada197d
262 $ hg transplant 1:3
269 $ hg transplant 1:3
263 skipping already applied revision 1:a1e30dd1b8e7
270 skipping already applied revision 1:46ae92138f3c
264 applying 1739ac5f6139
271 applying 9d6d6b5a8275
265 1739ac5f6139 transplanted to d649c221319f
272 9d6d6b5a8275 transplanted to 2d17a10c922f
266 applying 0282d5fbbe02
273 applying 1dab759070cf
267 0282d5fbbe02 transplanted to 77418277ccb3
274 1dab759070cf transplanted to e06a69927eb0
268 $ hg locate
275 $ hg locate
269 added
276 added
270 bar
277 bar
278 baz
271 foo
279 foo
280
281 test multiple revisions and --continue
282
283 $ hg up -qC 0
284 $ echo bazbaz > baz
285 $ hg ci -Am anotherbaz baz
286 created new head
287 $ hg transplant 1:3
288 applying 46ae92138f3c
289 46ae92138f3c transplanted to 1024233ea0ba
290 applying 9d6d6b5a8275
291 patching file baz
292 Hunk #1 FAILED at 0
293 1 out of 1 hunks FAILED -- saving rejects to file baz.rej
294 patch failed to apply
295 abort: fix up the merge and run hg transplant --continue
296 [255]
297 $ echo fixed > baz
298 $ hg transplant --continue
299 9d6d6b5a8275 transplanted as d80c49962290
300 applying 1dab759070cf
301 1dab759070cf transplanted to aa0ffe6bd5ae
302
272 $ cd ..
303 $ cd ..
273
304
274 Issue1111: Test transplant --merge
305 Issue1111: Test transplant --merge
275
306
276 $ hg init t1111
307 $ hg init t1111
277 $ cd t1111
308 $ cd t1111
278 $ echo a > a
309 $ echo a > a
279 $ hg ci -Am adda
310 $ hg ci -Am adda
280 adding a
311 adding a
281 $ echo b >> a
312 $ echo b >> a
282 $ hg ci -m appendb
313 $ hg ci -m appendb
283 $ echo c >> a
314 $ echo c >> a
284 $ hg ci -m appendc
315 $ hg ci -m appendc
285 $ hg up -C 0
316 $ hg up -C 0
286 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
317 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 $ echo d >> a
318 $ echo d >> a
288 $ hg ci -m appendd
319 $ hg ci -m appendd
289 created new head
320 created new head
290
321
291 tranplant
322 tranplant
292
323
293 $ hg transplant -m 1
324 $ hg transplant -m 1
294 applying 42dc4432fd35
325 applying 42dc4432fd35
295 1:42dc4432fd35 merged at a9f4acbac129
326 1:42dc4432fd35 merged at a9f4acbac129
296 $ cd ..
327 $ cd ..
297
328
298 test transplant into empty repository
329 test transplant into empty repository
299
330
300 $ hg init empty
331 $ hg init empty
301 $ cd empty
332 $ cd empty
302 $ hg transplant -s ../t -b tip -a
333 $ hg transplant -s ../t -b tip -a
303 adding changesets
334 adding changesets
304 adding manifests
335 adding manifests
305 adding file changes
336 adding file changes
306 added 4 changesets with 4 changes to 4 files
337 added 4 changesets with 4 changes to 4 files
307 $ cd ..
338 $ cd ..
308
339
309
340
310 test filter
341 test filter
311
342
312 $ hg init filter
343 $ hg init filter
313 $ cd filter
344 $ cd filter
314 $ cat <<'EOF' >test-filter
345 $ cat <<'EOF' >test-filter
315 > #!/bin/sh
346 > #!/bin/sh
316 > sed 's/r1/r2/' $1 > $1.new
347 > sed 's/r1/r2/' $1 > $1.new
317 > mv $1.new $1
348 > mv $1.new $1
318 > EOF
349 > EOF
319 $ chmod +x test-filter
350 $ chmod +x test-filter
320 $ hg transplant -s ../t -b tip -a --filter ./test-filter
351 $ hg transplant -s ../t -b tip -a --filter ./test-filter
321 filtering * (glob)
352 filtering * (glob)
322 applying 17ab29e464c6
353 applying 17ab29e464c6
323 17ab29e464c6 transplanted to e9ffc54ea104
354 17ab29e464c6 transplanted to e9ffc54ea104
324 filtering * (glob)
355 filtering * (glob)
325 applying 37a1297eb21b
356 applying 37a1297eb21b
326 37a1297eb21b transplanted to 348b36d0b6a5
357 37a1297eb21b transplanted to 348b36d0b6a5
327 filtering * (glob)
358 filtering * (glob)
328 applying 722f4667af76
359 applying 722f4667af76
329 722f4667af76 transplanted to 0aa6979afb95
360 722f4667af76 transplanted to 0aa6979afb95
330 filtering * (glob)
361 filtering * (glob)
331 applying a53251cdf717
362 applying a53251cdf717
332 a53251cdf717 transplanted to 14f8512272b5
363 a53251cdf717 transplanted to 14f8512272b5
333 $ hg log --template '{rev} {parents} {desc}\n'
364 $ hg log --template '{rev} {parents} {desc}\n'
334 3 b3
365 3 b3
335 2 b2
366 2 b2
336 1 b1
367 1 b1
337 0 r2
368 0 r2
338 $ cd ..
369 $ cd ..
339
370
340
371
341 test filter with failed patch
372 test filter with failed patch
342
373
343 $ cd filter
374 $ cd filter
344 $ hg up 0
375 $ hg up 0
345 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
376 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
346 $ echo foo > b1
377 $ echo foo > b1
347 $ hg ci -Am foo
378 $ hg ci -Am foo
348 adding b1
379 adding b1
349 adding test-filter
380 adding test-filter
350 created new head
381 created new head
351 $ hg transplant 1 --filter ./test-filter
382 $ hg transplant 1 --filter ./test-filter
352 filtering * (glob)
383 filtering * (glob)
353 applying 348b36d0b6a5
384 applying 348b36d0b6a5
354 file b1 already exists
385 file b1 already exists
355 1 out of 1 hunks FAILED -- saving rejects to file b1.rej
386 1 out of 1 hunks FAILED -- saving rejects to file b1.rej
356 patch failed to apply
387 patch failed to apply
357 abort: fix up the merge and run hg transplant --continue
388 abort: fix up the merge and run hg transplant --continue
358 [255]
389 [255]
359 $ cd ..
390 $ cd ..
360
391
361 test environment passed to filter
392 test environment passed to filter
362
393
363 $ hg init filter-environment
394 $ hg init filter-environment
364 $ cd filter-environment
395 $ cd filter-environment
365 $ cat <<'EOF' >test-filter-environment
396 $ cat <<'EOF' >test-filter-environment
366 > #!/bin/sh
397 > #!/bin/sh
367 > echo "Transplant by $HGUSER" >> $1
398 > echo "Transplant by $HGUSER" >> $1
368 > echo "Transplant from rev $HGREVISION" >> $1
399 > echo "Transplant from rev $HGREVISION" >> $1
369 > EOF
400 > EOF
370 $ chmod +x test-filter-environment
401 $ chmod +x test-filter-environment
371 $ hg transplant -s ../t --filter ./test-filter-environment 0
402 $ hg transplant -s ../t --filter ./test-filter-environment 0
372 filtering * (glob)
403 filtering * (glob)
373 applying 17ab29e464c6
404 applying 17ab29e464c6
374 17ab29e464c6 transplanted to 5190e68026a0
405 17ab29e464c6 transplanted to 5190e68026a0
375
406
376 $ hg log --template '{rev} {parents} {desc}\n'
407 $ hg log --template '{rev} {parents} {desc}\n'
377 0 r1
408 0 r1
378 Transplant by test
409 Transplant by test
379 Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f
410 Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f
380 $ cd ..
411 $ cd ..
381
412
382 test transplant with filter handles invalid changelog
413 test transplant with filter handles invalid changelog
383
414
384 $ hg init filter-invalid-log
415 $ hg init filter-invalid-log
385 $ cd filter-invalid-log
416 $ cd filter-invalid-log
386 $ cat <<'EOF' >test-filter-invalid-log
417 $ cat <<'EOF' >test-filter-invalid-log
387 > #!/bin/sh
418 > #!/bin/sh
388 > echo "" > $1
419 > echo "" > $1
389 > EOF
420 > EOF
390 $ chmod +x test-filter-invalid-log
421 $ chmod +x test-filter-invalid-log
391 $ hg transplant -s ../t --filter ./test-filter-invalid-log 0
422 $ hg transplant -s ../t --filter ./test-filter-invalid-log 0
392 filtering * (glob)
423 filtering * (glob)
393 abort: filter corrupted changeset (no user or date)
424 abort: filter corrupted changeset (no user or date)
394 [255]
425 [255]
395
426
396 test with a win32ext like setup (differing EOLs)
427 test with a win32ext like setup (differing EOLs)
397
428
398 $ hg init twin1
429 $ hg init twin1
399 $ cd twin1
430 $ cd twin1
400 $ echo a > a
431 $ echo a > a
401 $ echo b > b
432 $ echo b > b
402 $ echo b >> b
433 $ echo b >> b
403 $ hg ci -Am t
434 $ hg ci -Am t
404 adding a
435 adding a
405 adding b
436 adding b
406 $ echo a > b
437 $ echo a > b
407 $ echo b >> b
438 $ echo b >> b
408 $ hg ci -m changeb
439 $ hg ci -m changeb
409 $ cd ..
440 $ cd ..
410
441
411 $ hg init twin2
442 $ hg init twin2
412 $ cd twin2
443 $ cd twin2
413 $ echo '[patch]' >> .hg/hgrc
444 $ echo '[patch]' >> .hg/hgrc
414 $ echo 'eol = crlf' >> .hg/hgrc
445 $ echo 'eol = crlf' >> .hg/hgrc
415 $ python -c "file('b', 'wb').write('b\r\nb\r\n')"
446 $ python -c "file('b', 'wb').write('b\r\nb\r\n')"
416 $ hg ci -Am addb
447 $ hg ci -Am addb
417 adding b
448 adding b
418 $ hg transplant -s ../twin1 tip
449 $ hg transplant -s ../twin1 tip
419 searching for changes
450 searching for changes
420 warning: repository is unrelated
451 warning: repository is unrelated
421 applying 2e849d776c17
452 applying 2e849d776c17
422 2e849d776c17 transplanted to 8e65bebc063e
453 2e849d776c17 transplanted to 8e65bebc063e
423 $ cat b
454 $ cat b
424 a\r (esc)
455 a\r (esc)
425 b\r (esc)
456 b\r (esc)
426 $ cd ..
457 $ cd ..
427
458
428 test transplant with merge changeset is skipped
459 test transplant with merge changeset is skipped
429
460
430 $ hg init merge1a
461 $ hg init merge1a
431 $ cd merge1a
462 $ cd merge1a
432 $ echo a > a
463 $ echo a > a
433 $ hg ci -Am a
464 $ hg ci -Am a
434 adding a
465 adding a
435 $ hg branch b
466 $ hg branch b
436 marked working directory as branch b
467 marked working directory as branch b
437 (branches are permanent and global, did you want a bookmark?)
468 (branches are permanent and global, did you want a bookmark?)
438 $ hg ci -m branchb
469 $ hg ci -m branchb
439 $ echo b > b
470 $ echo b > b
440 $ hg ci -Am b
471 $ hg ci -Am b
441 adding b
472 adding b
442 $ hg update default
473 $ hg update default
443 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
474 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
444 $ hg merge b
475 $ hg merge b
445 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
476 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
446 (branch merge, don't forget to commit)
477 (branch merge, don't forget to commit)
447 $ hg ci -m mergeb
478 $ hg ci -m mergeb
448 $ cd ..
479 $ cd ..
449
480
450 $ hg init merge1b
481 $ hg init merge1b
451 $ cd merge1b
482 $ cd merge1b
452 $ hg transplant -s ../merge1a tip
483 $ hg transplant -s ../merge1a tip
453
484
454 test transplant with merge changeset accepts --parent
485 test transplant with merge changeset accepts --parent
455
486
456 $ hg init merge2a
487 $ hg init merge2a
457 $ cd merge2a
488 $ cd merge2a
458 $ echo a > a
489 $ echo a > a
459 $ hg ci -Am a
490 $ hg ci -Am a
460 adding a
491 adding a
461 $ hg branch b
492 $ hg branch b
462 marked working directory as branch b
493 marked working directory as branch b
463 (branches are permanent and global, did you want a bookmark?)
494 (branches are permanent and global, did you want a bookmark?)
464 $ hg ci -m branchb
495 $ hg ci -m branchb
465 $ echo b > b
496 $ echo b > b
466 $ hg ci -Am b
497 $ hg ci -Am b
467 adding b
498 adding b
468 $ hg update default
499 $ hg update default
469 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
500 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
470 $ hg merge b
501 $ hg merge b
471 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
502 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
472 (branch merge, don't forget to commit)
503 (branch merge, don't forget to commit)
473 $ hg ci -m mergeb
504 $ hg ci -m mergeb
474 $ cd ..
505 $ cd ..
475
506
476 $ hg init merge2b
507 $ hg init merge2b
477 $ cd merge2b
508 $ cd merge2b
478 $ hg transplant -s ../merge2a --parent 0 tip
509 $ hg transplant -s ../merge2a --parent 0 tip
479 applying be9f9b39483f
510 applying be9f9b39483f
480 be9f9b39483f transplanted to 9959e51f94d1
511 be9f9b39483f transplanted to 9959e51f94d1
General Comments 0
You need to be logged in to leave comments. Login now