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