##// END OF EJS Templates
transplant: properly skip empty changeset (issue4423)...
Pierre-Yves David -
r23781:49caef45 default
parent child Browse files
Show More
@@ -1,691 +1,699
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 self.ui.status(_('%s transplanted as %s\n') % (short(node),
304 if n:
305 short(n)))
305 self.ui.status(_('%s transplanted as %s\n') % (short(node),
306 short(n)))
307 else:
308 self.ui.status(_('%s skipped due to empty diff\n')
309 % (short(node),))
306 seriespath = os.path.join(self.path, 'series')
310 seriespath = os.path.join(self.path, 'series')
307 if not os.path.exists(seriespath):
311 if not os.path.exists(seriespath):
308 self.transplants.write()
312 self.transplants.write()
309 return
313 return
310 nodes, merges = self.readseries()
314 nodes, merges = self.readseries()
311 revmap = {}
315 revmap = {}
312 for n in nodes:
316 for n in nodes:
313 revmap[source.changelog.rev(n)] = n
317 revmap[source.changelog.rev(n)] = n
314 os.unlink(seriespath)
318 os.unlink(seriespath)
315
319
316 self.apply(repo, source, revmap, merges, opts)
320 self.apply(repo, source, revmap, merges, opts)
317
321
318 def recover(self, repo, source, opts):
322 def recover(self, repo, source, opts):
319 '''commit working directory using journal metadata'''
323 '''commit working directory using journal metadata'''
320 node, user, date, message, parents = self.readlog()
324 node, user, date, message, parents = self.readlog()
321 merge = False
325 merge = False
322
326
323 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]:
324 raise util.Abort(_('transplant log file is corrupt'))
328 raise util.Abort(_('transplant log file is corrupt'))
325
329
326 parent = parents[0]
330 parent = parents[0]
327 if len(parents) > 1:
331 if len(parents) > 1:
328 if opts.get('parent'):
332 if opts.get('parent'):
329 parent = source.lookup(opts['parent'])
333 parent = source.lookup(opts['parent'])
330 if parent not in parents:
334 if parent not in parents:
331 raise util.Abort(_('%s is not a parent of %s') %
335 raise util.Abort(_('%s is not a parent of %s') %
332 (short(parent), short(node)))
336 (short(parent), short(node)))
333 else:
337 else:
334 merge = True
338 merge = True
335
339
336 extra = {'transplant_source': node}
340 extra = {'transplant_source': node}
337 wlock = repo.wlock()
341 wlock = repo.wlock()
338 try:
342 try:
339 p1, p2 = repo.dirstate.parents()
343 p1, p2 = repo.dirstate.parents()
340 if p1 != parent:
344 if p1 != parent:
341 raise util.Abort(
345 raise util.Abort(
342 _('working dir not at transplant parent %s') %
346 _('working dir not at transplant parent %s') %
343 revlog.hex(parent))
347 revlog.hex(parent))
344 if merge:
348 if merge:
345 repo.setparents(p1, parents[1])
349 repo.setparents(p1, parents[1])
346 n = repo.commit(message, user, date, extra=extra,
350 modified, added, removed, deleted = repo.status()[:4]
347 editor=self.getcommiteditor())
351 if merge or modified or added or removed or deleted:
348 if not n:
352 n = repo.commit(message, user, date, extra=extra,
349 raise util.Abort(_('commit failed'))
353 editor=self.getcommiteditor())
350 if not merge:
354 if not n:
351 self.transplants.set(n, node)
355 raise util.Abort(_('commit failed'))
356 if not merge:
357 self.transplants.set(n, node)
358 else:
359 n = None
352 self.unlog()
360 self.unlog()
353
361
354 return n, node
362 return n, node
355 finally:
363 finally:
356 wlock.release()
364 wlock.release()
357
365
358 def readseries(self):
366 def readseries(self):
359 nodes = []
367 nodes = []
360 merges = []
368 merges = []
361 cur = nodes
369 cur = nodes
362 for line in self.opener.read('series').splitlines():
370 for line in self.opener.read('series').splitlines():
363 if line.startswith('# Merges'):
371 if line.startswith('# Merges'):
364 cur = merges
372 cur = merges
365 continue
373 continue
366 cur.append(revlog.bin(line))
374 cur.append(revlog.bin(line))
367
375
368 return (nodes, merges)
376 return (nodes, merges)
369
377
370 def saveseries(self, revmap, merges):
378 def saveseries(self, revmap, merges):
371 if not revmap:
379 if not revmap:
372 return
380 return
373
381
374 if not os.path.isdir(self.path):
382 if not os.path.isdir(self.path):
375 os.mkdir(self.path)
383 os.mkdir(self.path)
376 series = self.opener('series', 'w')
384 series = self.opener('series', 'w')
377 for rev in sorted(revmap):
385 for rev in sorted(revmap):
378 series.write(revlog.hex(revmap[rev]) + '\n')
386 series.write(revlog.hex(revmap[rev]) + '\n')
379 if merges:
387 if merges:
380 series.write('# Merges\n')
388 series.write('# Merges\n')
381 for m in merges:
389 for m in merges:
382 series.write(revlog.hex(m) + '\n')
390 series.write(revlog.hex(m) + '\n')
383 series.close()
391 series.close()
384
392
385 def parselog(self, fp):
393 def parselog(self, fp):
386 parents = []
394 parents = []
387 message = []
395 message = []
388 node = revlog.nullid
396 node = revlog.nullid
389 inmsg = False
397 inmsg = False
390 user = None
398 user = None
391 date = None
399 date = None
392 for line in fp.read().splitlines():
400 for line in fp.read().splitlines():
393 if inmsg:
401 if inmsg:
394 message.append(line)
402 message.append(line)
395 elif line.startswith('# User '):
403 elif line.startswith('# User '):
396 user = line[7:]
404 user = line[7:]
397 elif line.startswith('# Date '):
405 elif line.startswith('# Date '):
398 date = line[7:]
406 date = line[7:]
399 elif line.startswith('# Node ID '):
407 elif line.startswith('# Node ID '):
400 node = revlog.bin(line[10:])
408 node = revlog.bin(line[10:])
401 elif line.startswith('# Parent '):
409 elif line.startswith('# Parent '):
402 parents.append(revlog.bin(line[9:]))
410 parents.append(revlog.bin(line[9:]))
403 elif not line.startswith('# '):
411 elif not line.startswith('# '):
404 inmsg = True
412 inmsg = True
405 message.append(line)
413 message.append(line)
406 if None in (user, date):
414 if None in (user, date):
407 raise util.Abort(_("filter corrupted changeset (no user or date)"))
415 raise util.Abort(_("filter corrupted changeset (no user or date)"))
408 return (node, user, date, '\n'.join(message), parents)
416 return (node, user, date, '\n'.join(message), parents)
409
417
410 def log(self, user, date, message, p1, p2, merge=False):
418 def log(self, user, date, message, p1, p2, merge=False):
411 '''journal changelog metadata for later recover'''
419 '''journal changelog metadata for later recover'''
412
420
413 if not os.path.isdir(self.path):
421 if not os.path.isdir(self.path):
414 os.mkdir(self.path)
422 os.mkdir(self.path)
415 fp = self.opener('journal', 'w')
423 fp = self.opener('journal', 'w')
416 fp.write('# User %s\n' % user)
424 fp.write('# User %s\n' % user)
417 fp.write('# Date %s\n' % date)
425 fp.write('# Date %s\n' % date)
418 fp.write('# Node ID %s\n' % revlog.hex(p2))
426 fp.write('# Node ID %s\n' % revlog.hex(p2))
419 fp.write('# Parent ' + revlog.hex(p1) + '\n')
427 fp.write('# Parent ' + revlog.hex(p1) + '\n')
420 if merge:
428 if merge:
421 fp.write('# Parent ' + revlog.hex(p2) + '\n')
429 fp.write('# Parent ' + revlog.hex(p2) + '\n')
422 fp.write(message.rstrip() + '\n')
430 fp.write(message.rstrip() + '\n')
423 fp.close()
431 fp.close()
424
432
425 def readlog(self):
433 def readlog(self):
426 return self.parselog(self.opener('journal'))
434 return self.parselog(self.opener('journal'))
427
435
428 def unlog(self):
436 def unlog(self):
429 '''remove changelog journal'''
437 '''remove changelog journal'''
430 absdst = os.path.join(self.path, 'journal')
438 absdst = os.path.join(self.path, 'journal')
431 if os.path.exists(absdst):
439 if os.path.exists(absdst):
432 os.unlink(absdst)
440 os.unlink(absdst)
433
441
434 def transplantfilter(self, repo, source, root):
442 def transplantfilter(self, repo, source, root):
435 def matchfn(node):
443 def matchfn(node):
436 if self.applied(repo, node, root):
444 if self.applied(repo, node, root):
437 return False
445 return False
438 if source.changelog.parents(node)[1] != revlog.nullid:
446 if source.changelog.parents(node)[1] != revlog.nullid:
439 return False
447 return False
440 extra = source.changelog.read(node)[5]
448 extra = source.changelog.read(node)[5]
441 cnode = extra.get('transplant_source')
449 cnode = extra.get('transplant_source')
442 if cnode and self.applied(repo, cnode, root):
450 if cnode and self.applied(repo, cnode, root):
443 return False
451 return False
444 return True
452 return True
445
453
446 return matchfn
454 return matchfn
447
455
448 def hasnode(repo, node):
456 def hasnode(repo, node):
449 try:
457 try:
450 return repo.changelog.rev(node) is not None
458 return repo.changelog.rev(node) is not None
451 except error.RevlogError:
459 except error.RevlogError:
452 return False
460 return False
453
461
454 def browserevs(ui, repo, nodes, opts):
462 def browserevs(ui, repo, nodes, opts):
455 '''interactively transplant changesets'''
463 '''interactively transplant changesets'''
456 displayer = cmdutil.show_changeset(ui, repo, opts)
464 displayer = cmdutil.show_changeset(ui, repo, opts)
457 transplants = []
465 transplants = []
458 merges = []
466 merges = []
459 prompt = _('apply changeset? [ynmpcq?]:'
467 prompt = _('apply changeset? [ynmpcq?]:'
460 '$$ &yes, transplant this changeset'
468 '$$ &yes, transplant this changeset'
461 '$$ &no, skip this changeset'
469 '$$ &no, skip this changeset'
462 '$$ &merge at this changeset'
470 '$$ &merge at this changeset'
463 '$$ show &patch'
471 '$$ show &patch'
464 '$$ &commit selected changesets'
472 '$$ &commit selected changesets'
465 '$$ &quit and cancel transplant'
473 '$$ &quit and cancel transplant'
466 '$$ &? (show this help)')
474 '$$ &? (show this help)')
467 for node in nodes:
475 for node in nodes:
468 displayer.show(repo[node])
476 displayer.show(repo[node])
469 action = None
477 action = None
470 while not action:
478 while not action:
471 action = 'ynmpcq?'[ui.promptchoice(prompt)]
479 action = 'ynmpcq?'[ui.promptchoice(prompt)]
472 if action == '?':
480 if action == '?':
473 for c, t in ui.extractchoices(prompt)[1]:
481 for c, t in ui.extractchoices(prompt)[1]:
474 ui.write('%s: %s\n' % (c, t))
482 ui.write('%s: %s\n' % (c, t))
475 action = None
483 action = None
476 elif action == 'p':
484 elif action == 'p':
477 parent = repo.changelog.parents(node)[0]
485 parent = repo.changelog.parents(node)[0]
478 for chunk in patch.diff(repo, parent, node):
486 for chunk in patch.diff(repo, parent, node):
479 ui.write(chunk)
487 ui.write(chunk)
480 action = None
488 action = None
481 if action == 'y':
489 if action == 'y':
482 transplants.append(node)
490 transplants.append(node)
483 elif action == 'm':
491 elif action == 'm':
484 merges.append(node)
492 merges.append(node)
485 elif action == 'c':
493 elif action == 'c':
486 break
494 break
487 elif action == 'q':
495 elif action == 'q':
488 transplants = ()
496 transplants = ()
489 merges = ()
497 merges = ()
490 break
498 break
491 displayer.close()
499 displayer.close()
492 return (transplants, merges)
500 return (transplants, merges)
493
501
494 @command('transplant',
502 @command('transplant',
495 [('s', 'source', '', _('transplant changesets from REPO'), _('REPO')),
503 [('s', 'source', '', _('transplant changesets from REPO'), _('REPO')),
496 ('b', 'branch', [], _('use this source changeset as head'), _('REV')),
504 ('b', 'branch', [], _('use this source changeset as head'), _('REV')),
497 ('a', 'all', None, _('pull all changesets up to the --branch revisions')),
505 ('a', 'all', None, _('pull all changesets up to the --branch revisions')),
498 ('p', 'prune', [], _('skip over REV'), _('REV')),
506 ('p', 'prune', [], _('skip over REV'), _('REV')),
499 ('m', 'merge', [], _('merge at REV'), _('REV')),
507 ('m', 'merge', [], _('merge at REV'), _('REV')),
500 ('', 'parent', '',
508 ('', 'parent', '',
501 _('parent to choose when transplanting merge'), _('REV')),
509 _('parent to choose when transplanting merge'), _('REV')),
502 ('e', 'edit', False, _('invoke editor on commit messages')),
510 ('e', 'edit', False, _('invoke editor on commit messages')),
503 ('', 'log', None, _('append transplant info to log message')),
511 ('', 'log', None, _('append transplant info to log message')),
504 ('c', 'continue', None, _('continue last transplant session '
512 ('c', 'continue', None, _('continue last transplant session '
505 'after fixing conflicts')),
513 'after fixing conflicts')),
506 ('', 'filter', '',
514 ('', 'filter', '',
507 _('filter changesets through command'), _('CMD'))],
515 _('filter changesets through command'), _('CMD'))],
508 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
516 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
509 '[-m REV] [REV]...'))
517 '[-m REV] [REV]...'))
510 def transplant(ui, repo, *revs, **opts):
518 def transplant(ui, repo, *revs, **opts):
511 '''transplant changesets from another branch
519 '''transplant changesets from another branch
512
520
513 Selected changesets will be applied on top of the current working
521 Selected changesets will be applied on top of the current working
514 directory with the log of the original changeset. The changesets
522 directory with the log of the original changeset. The changesets
515 are copied and will thus appear twice in the history with different
523 are copied and will thus appear twice in the history with different
516 identities.
524 identities.
517
525
518 Consider using the graft command if everything is inside the same
526 Consider using the graft command if everything is inside the same
519 repository - it will use merges and will usually give a better result.
527 repository - it will use merges and will usually give a better result.
520 Use the rebase extension if the changesets are unpublished and you want
528 Use the rebase extension if the changesets are unpublished and you want
521 to move them instead of copying them.
529 to move them instead of copying them.
522
530
523 If --log is specified, log messages will have a comment appended
531 If --log is specified, log messages will have a comment appended
524 of the form::
532 of the form::
525
533
526 (transplanted from CHANGESETHASH)
534 (transplanted from CHANGESETHASH)
527
535
528 You can rewrite the changelog message with the --filter option.
536 You can rewrite the changelog message with the --filter option.
529 Its argument will be invoked with the current changelog message as
537 Its argument will be invoked with the current changelog message as
530 $1 and the patch as $2.
538 $1 and the patch as $2.
531
539
532 --source/-s specifies another repository to use for selecting changesets,
540 --source/-s specifies another repository to use for selecting changesets,
533 just as if it temporarily had been pulled.
541 just as if it temporarily had been pulled.
534 If --branch/-b is specified, these revisions will be used as
542 If --branch/-b is specified, these revisions will be used as
535 heads when deciding which changesets to transplant, just as if only
543 heads when deciding which changesets to transplant, just as if only
536 these revisions had been pulled.
544 these revisions had been pulled.
537 If --all/-a is specified, all the revisions up to the heads specified
545 If --all/-a is specified, all the revisions up to the heads specified
538 with --branch will be transplanted.
546 with --branch will be transplanted.
539
547
540 Example:
548 Example:
541
549
542 - transplant all changes up to REV on top of your current revision::
550 - transplant all changes up to REV on top of your current revision::
543
551
544 hg transplant --branch REV --all
552 hg transplant --branch REV --all
545
553
546 You can optionally mark selected transplanted changesets as merge
554 You can optionally mark selected transplanted changesets as merge
547 changesets. You will not be prompted to transplant any ancestors
555 changesets. You will not be prompted to transplant any ancestors
548 of a merged transplant, and you can merge descendants of them
556 of a merged transplant, and you can merge descendants of them
549 normally instead of transplanting them.
557 normally instead of transplanting them.
550
558
551 Merge changesets may be transplanted directly by specifying the
559 Merge changesets may be transplanted directly by specifying the
552 proper parent changeset by calling :hg:`transplant --parent`.
560 proper parent changeset by calling :hg:`transplant --parent`.
553
561
554 If no merges or revisions are provided, :hg:`transplant` will
562 If no merges or revisions are provided, :hg:`transplant` will
555 start an interactive changeset browser.
563 start an interactive changeset browser.
556
564
557 If a changeset application fails, you can fix the merge by hand
565 If a changeset application fails, you can fix the merge by hand
558 and then resume where you left off by calling :hg:`transplant
566 and then resume where you left off by calling :hg:`transplant
559 --continue/-c`.
567 --continue/-c`.
560 '''
568 '''
561 def incwalk(repo, csets, match=util.always):
569 def incwalk(repo, csets, match=util.always):
562 for node in csets:
570 for node in csets:
563 if match(node):
571 if match(node):
564 yield node
572 yield node
565
573
566 def transplantwalk(repo, dest, heads, match=util.always):
574 def transplantwalk(repo, dest, heads, match=util.always):
567 '''Yield all nodes that are ancestors of a head but not ancestors
575 '''Yield all nodes that are ancestors of a head but not ancestors
568 of dest.
576 of dest.
569 If no heads are specified, the heads of repo will be used.'''
577 If no heads are specified, the heads of repo will be used.'''
570 if not heads:
578 if not heads:
571 heads = repo.heads()
579 heads = repo.heads()
572 ancestors = []
580 ancestors = []
573 ctx = repo[dest]
581 ctx = repo[dest]
574 for head in heads:
582 for head in heads:
575 ancestors.append(ctx.ancestor(repo[head]).node())
583 ancestors.append(ctx.ancestor(repo[head]).node())
576 for node in repo.changelog.nodesbetween(ancestors, heads)[0]:
584 for node in repo.changelog.nodesbetween(ancestors, heads)[0]:
577 if match(node):
585 if match(node):
578 yield node
586 yield node
579
587
580 def checkopts(opts, revs):
588 def checkopts(opts, revs):
581 if opts.get('continue'):
589 if opts.get('continue'):
582 if opts.get('branch') or opts.get('all') or opts.get('merge'):
590 if opts.get('branch') or opts.get('all') or opts.get('merge'):
583 raise util.Abort(_('--continue is incompatible with '
591 raise util.Abort(_('--continue is incompatible with '
584 '--branch, --all and --merge'))
592 '--branch, --all and --merge'))
585 return
593 return
586 if not (opts.get('source') or revs or
594 if not (opts.get('source') or revs or
587 opts.get('merge') or opts.get('branch')):
595 opts.get('merge') or opts.get('branch')):
588 raise util.Abort(_('no source URL, branch revision or revision '
596 raise util.Abort(_('no source URL, branch revision or revision '
589 'list provided'))
597 'list provided'))
590 if opts.get('all'):
598 if opts.get('all'):
591 if not opts.get('branch'):
599 if not opts.get('branch'):
592 raise util.Abort(_('--all requires a branch revision'))
600 raise util.Abort(_('--all requires a branch revision'))
593 if revs:
601 if revs:
594 raise util.Abort(_('--all is incompatible with a '
602 raise util.Abort(_('--all is incompatible with a '
595 'revision list'))
603 'revision list'))
596
604
597 checkopts(opts, revs)
605 checkopts(opts, revs)
598
606
599 if not opts.get('log'):
607 if not opts.get('log'):
600 opts['log'] = ui.config('transplant', 'log')
608 opts['log'] = ui.config('transplant', 'log')
601 if not opts.get('filter'):
609 if not opts.get('filter'):
602 opts['filter'] = ui.config('transplant', 'filter')
610 opts['filter'] = ui.config('transplant', 'filter')
603
611
604 tp = transplanter(ui, repo, opts)
612 tp = transplanter(ui, repo, opts)
605
613
606 cmdutil.checkunfinished(repo)
614 cmdutil.checkunfinished(repo)
607 p1, p2 = repo.dirstate.parents()
615 p1, p2 = repo.dirstate.parents()
608 if len(repo) > 0 and p1 == revlog.nullid:
616 if len(repo) > 0 and p1 == revlog.nullid:
609 raise util.Abort(_('no revision checked out'))
617 raise util.Abort(_('no revision checked out'))
610 if not opts.get('continue'):
618 if not opts.get('continue'):
611 if p2 != revlog.nullid:
619 if p2 != revlog.nullid:
612 raise util.Abort(_('outstanding uncommitted merges'))
620 raise util.Abort(_('outstanding uncommitted merges'))
613 m, a, r, d = repo.status()[:4]
621 m, a, r, d = repo.status()[:4]
614 if m or a or r or d:
622 if m or a or r or d:
615 raise util.Abort(_('outstanding local changes'))
623 raise util.Abort(_('outstanding local changes'))
616
624
617 sourcerepo = opts.get('source')
625 sourcerepo = opts.get('source')
618 if sourcerepo:
626 if sourcerepo:
619 peer = hg.peer(repo, opts, ui.expandpath(sourcerepo))
627 peer = hg.peer(repo, opts, ui.expandpath(sourcerepo))
620 heads = map(peer.lookup, opts.get('branch', ()))
628 heads = map(peer.lookup, opts.get('branch', ()))
621 source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer,
629 source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer,
622 onlyheads=heads, force=True)
630 onlyheads=heads, force=True)
623 else:
631 else:
624 source = repo
632 source = repo
625 heads = map(source.lookup, opts.get('branch', ()))
633 heads = map(source.lookup, opts.get('branch', ()))
626 cleanupfn = None
634 cleanupfn = None
627
635
628 try:
636 try:
629 if opts.get('continue'):
637 if opts.get('continue'):
630 tp.resume(repo, source, opts)
638 tp.resume(repo, source, opts)
631 return
639 return
632
640
633 tf = tp.transplantfilter(repo, source, p1)
641 tf = tp.transplantfilter(repo, source, p1)
634 if opts.get('prune'):
642 if opts.get('prune'):
635 prune = set(source.lookup(r)
643 prune = set(source.lookup(r)
636 for r in scmutil.revrange(source, opts.get('prune')))
644 for r in scmutil.revrange(source, opts.get('prune')))
637 matchfn = lambda x: tf(x) and x not in prune
645 matchfn = lambda x: tf(x) and x not in prune
638 else:
646 else:
639 matchfn = tf
647 matchfn = tf
640 merges = map(source.lookup, opts.get('merge', ()))
648 merges = map(source.lookup, opts.get('merge', ()))
641 revmap = {}
649 revmap = {}
642 if revs:
650 if revs:
643 for r in scmutil.revrange(source, revs):
651 for r in scmutil.revrange(source, revs):
644 revmap[int(r)] = source.lookup(r)
652 revmap[int(r)] = source.lookup(r)
645 elif opts.get('all') or not merges:
653 elif opts.get('all') or not merges:
646 if source != repo:
654 if source != repo:
647 alltransplants = incwalk(source, csets, match=matchfn)
655 alltransplants = incwalk(source, csets, match=matchfn)
648 else:
656 else:
649 alltransplants = transplantwalk(source, p1, heads,
657 alltransplants = transplantwalk(source, p1, heads,
650 match=matchfn)
658 match=matchfn)
651 if opts.get('all'):
659 if opts.get('all'):
652 revs = alltransplants
660 revs = alltransplants
653 else:
661 else:
654 revs, newmerges = browserevs(ui, source, alltransplants, opts)
662 revs, newmerges = browserevs(ui, source, alltransplants, opts)
655 merges.extend(newmerges)
663 merges.extend(newmerges)
656 for r in revs:
664 for r in revs:
657 revmap[source.changelog.rev(r)] = r
665 revmap[source.changelog.rev(r)] = r
658 for r in merges:
666 for r in merges:
659 revmap[source.changelog.rev(r)] = r
667 revmap[source.changelog.rev(r)] = r
660
668
661 tp.apply(repo, source, revmap, merges, opts)
669 tp.apply(repo, source, revmap, merges, opts)
662 finally:
670 finally:
663 if cleanupfn:
671 if cleanupfn:
664 cleanupfn()
672 cleanupfn()
665
673
666 def revsettransplanted(repo, subset, x):
674 def revsettransplanted(repo, subset, x):
667 """``transplanted([set])``
675 """``transplanted([set])``
668 Transplanted changesets in set, or all transplanted changesets.
676 Transplanted changesets in set, or all transplanted changesets.
669 """
677 """
670 if x:
678 if x:
671 s = revset.getset(repo, subset, x)
679 s = revset.getset(repo, subset, x)
672 else:
680 else:
673 s = subset
681 s = subset
674 return revset.baseset([r for r in s if
682 return revset.baseset([r for r in s if
675 repo[r].extra().get('transplant_source')])
683 repo[r].extra().get('transplant_source')])
676
684
677 def kwtransplanted(repo, ctx, **args):
685 def kwtransplanted(repo, ctx, **args):
678 """:transplanted: String. The node identifier of the transplanted
686 """:transplanted: String. The node identifier of the transplanted
679 changeset if any."""
687 changeset if any."""
680 n = ctx.extra().get('transplant_source')
688 n = ctx.extra().get('transplant_source')
681 return n and revlog.hex(n) or ''
689 return n and revlog.hex(n) or ''
682
690
683 def extsetup(ui):
691 def extsetup(ui):
684 revset.symbols['transplanted'] = revsettransplanted
692 revset.symbols['transplanted'] = revsettransplanted
685 templatekw.keywords['transplanted'] = kwtransplanted
693 templatekw.keywords['transplanted'] = kwtransplanted
686 cmdutil.unfinishedstates.append(
694 cmdutil.unfinishedstates.append(
687 ['series', True, False, _('transplant in progress'),
695 ['series', True, False, _('transplant in progress'),
688 _("use 'hg transplant --continue' or 'hg update' to abort")])
696 _("use 'hg transplant --continue' or 'hg update' to abort")])
689
697
690 # tell hggettext to extract docstrings from these functions:
698 # tell hggettext to extract docstrings from these functions:
691 i18nfunctions = [revsettransplanted, kwtransplanted]
699 i18nfunctions = [revsettransplanted, kwtransplanted]
@@ -1,776 +1,792
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/ 2 4
301 $ hg transplant -s http://localhost:$HGPORT/ 2 4
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
316
317 $ hg pull -q http://localhost:$HGPORT/
317 $ hg pull -q http://localhost:$HGPORT/
318 $ hg transplant -s http://localhost:$HGPORT/ 2 4
318 $ hg transplant -s http://localhost:$HGPORT/ 2 4
319 searching for changes
319 searching for changes
320 skipping already applied revision 2:8d9279348abb
320 skipping already applied revision 2:8d9279348abb
321 applying 722f4667af76
321 applying 722f4667af76
322 722f4667af76 transplanted to 76e321915884
322 722f4667af76 transplanted to 76e321915884
323
323
324 transplant --continue
324 transplant --continue
325
325
326 $ hg init ../tc
326 $ hg init ../tc
327 $ cd ../tc
327 $ cd ../tc
328 $ cat <<EOF > foo
328 $ cat <<EOF > foo
329 > foo
329 > foo
330 > bar
330 > bar
331 > baz
331 > baz
332 > EOF
332 > EOF
333 $ echo toremove > toremove
333 $ echo toremove > toremove
334 $ echo baz > baz
334 $ echo baz > baz
335 $ hg ci -Amfoo
335 $ hg ci -Amfoo
336 adding baz
336 adding baz
337 adding foo
337 adding foo
338 adding toremove
338 adding toremove
339 $ cat <<EOF > foo
339 $ cat <<EOF > foo
340 > foo2
340 > foo2
341 > bar2
341 > bar2
342 > baz2
342 > baz2
343 > EOF
343 > EOF
344 $ rm toremove
344 $ rm toremove
345 $ echo added > added
345 $ echo added > added
346 $ hg ci -Amfoo2
346 $ hg ci -Amfoo2
347 adding added
347 adding added
348 removing toremove
348 removing toremove
349 $ echo bar > bar
349 $ echo bar > bar
350 $ cat > baz <<EOF
350 $ cat > baz <<EOF
351 > before baz
351 > before baz
352 > baz
352 > baz
353 > after baz
353 > after baz
354 > EOF
354 > EOF
355 $ hg ci -Ambar
355 $ hg ci -Ambar
356 adding bar
356 adding bar
357 $ echo bar2 >> bar
357 $ echo bar2 >> bar
358 $ hg ci -mbar2
358 $ hg ci -mbar2
359 $ hg up 0
359 $ hg up 0
360 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
360 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
361 $ echo foobar > foo
361 $ echo foobar > foo
362 $ hg ci -mfoobar
362 $ hg ci -mfoobar
363 created new head
363 created new head
364 $ hg transplant 1:3
364 $ hg transplant 1:3
365 applying 46ae92138f3c
365 applying 46ae92138f3c
366 patching file foo
366 patching file foo
367 Hunk #1 FAILED at 0
367 Hunk #1 FAILED at 0
368 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
368 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
369 patch failed to apply
369 patch failed to apply
370 abort: fix up the merge and run hg transplant --continue
370 abort: fix up the merge and run hg transplant --continue
371 [255]
371 [255]
372
372
373 transplant -c shouldn't use an old changeset
373 transplant -c shouldn't use an old changeset
374
374
375 $ hg up -C
375 $ hg up -C
376 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
376 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 $ rm added
377 $ rm added
378 $ hg transplant 1
378 $ hg transplant 1
379 applying 46ae92138f3c
379 applying 46ae92138f3c
380 patching file foo
380 patching file foo
381 Hunk #1 FAILED at 0
381 Hunk #1 FAILED at 0
382 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
382 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
383 patch failed to apply
383 patch failed to apply
384 abort: fix up the merge and run hg transplant --continue
384 abort: fix up the merge and run hg transplant --continue
385 [255]
385 [255]
386 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
386 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
387 HGEDITFORM=transplant.normal
387 HGEDITFORM=transplant.normal
388 46ae92138f3c transplanted as 9159dada197d
388 46ae92138f3c transplanted as 9159dada197d
389 $ hg transplant 1:3
389 $ hg transplant 1:3
390 skipping already applied revision 1:46ae92138f3c
390 skipping already applied revision 1:46ae92138f3c
391 applying 9d6d6b5a8275
391 applying 9d6d6b5a8275
392 9d6d6b5a8275 transplanted to 2d17a10c922f
392 9d6d6b5a8275 transplanted to 2d17a10c922f
393 applying 1dab759070cf
393 applying 1dab759070cf
394 1dab759070cf transplanted to e06a69927eb0
394 1dab759070cf transplanted to e06a69927eb0
395 $ hg locate
395 $ hg locate
396 added
396 added
397 bar
397 bar
398 baz
398 baz
399 foo
399 foo
400
400
401 test multiple revisions and --continue
401 test multiple revisions and --continue
402
402
403 $ hg up -qC 0
403 $ hg up -qC 0
404 $ echo bazbaz > baz
404 $ echo bazbaz > baz
405 $ hg ci -Am anotherbaz baz
405 $ hg ci -Am anotherbaz baz
406 created new head
406 created new head
407 $ hg transplant 1:3
407 $ hg transplant 1:3
408 applying 46ae92138f3c
408 applying 46ae92138f3c
409 46ae92138f3c transplanted to 1024233ea0ba
409 46ae92138f3c transplanted to 1024233ea0ba
410 applying 9d6d6b5a8275
410 applying 9d6d6b5a8275
411 patching file baz
411 patching file baz
412 Hunk #1 FAILED at 0
412 Hunk #1 FAILED at 0
413 1 out of 1 hunks FAILED -- saving rejects to file baz.rej
413 1 out of 1 hunks FAILED -- saving rejects to file baz.rej
414 patch failed to apply
414 patch failed to apply
415 abort: fix up the merge and run hg transplant --continue
415 abort: fix up the merge and run hg transplant --continue
416 [255]
416 [255]
417 $ echo fixed > baz
417 $ echo fixed > baz
418 $ hg transplant --continue
418 $ hg transplant --continue
419 9d6d6b5a8275 transplanted as d80c49962290
419 9d6d6b5a8275 transplanted as d80c49962290
420 applying 1dab759070cf
420 applying 1dab759070cf
421 1dab759070cf transplanted to aa0ffe6bd5ae
421 1dab759070cf transplanted to aa0ffe6bd5ae
422
422
423 $ cd ..
423 $ cd ..
424
424
425 Issue1111: Test transplant --merge
425 Issue1111: Test transplant --merge
426
426
427 $ hg init t1111
427 $ hg init t1111
428 $ cd t1111
428 $ cd t1111
429 $ echo a > a
429 $ echo a > a
430 $ hg ci -Am adda
430 $ hg ci -Am adda
431 adding a
431 adding a
432 $ echo b >> a
432 $ echo b >> a
433 $ hg ci -m appendb
433 $ hg ci -m appendb
434 $ echo c >> a
434 $ echo c >> a
435 $ hg ci -m appendc
435 $ hg ci -m appendc
436 $ hg up -C 0
436 $ hg up -C 0
437 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
437 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
438 $ echo d >> a
438 $ echo d >> a
439 $ hg ci -m appendd
439 $ hg ci -m appendd
440 created new head
440 created new head
441
441
442 transplant
442 transplant
443
443
444 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant -m 1 -e
444 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant -m 1 -e
445 applying 42dc4432fd35
445 applying 42dc4432fd35
446 HGEDITFORM=transplant.merge
446 HGEDITFORM=transplant.merge
447 1:42dc4432fd35 merged at a9f4acbac129
447 1:42dc4432fd35 merged at a9f4acbac129
448 $ hg update -q -C 2
448 $ hg update -q -C 2
449 $ cat > a <<EOF
449 $ cat > a <<EOF
450 > x
450 > x
451 > y
451 > y
452 > z
452 > z
453 > EOF
453 > EOF
454 $ hg commit -m replace
454 $ hg commit -m replace
455 $ hg update -q -C 4
455 $ hg update -q -C 4
456 $ hg transplant -m 5
456 $ hg transplant -m 5
457 applying 600a3cdcb41d
457 applying 600a3cdcb41d
458 patching file a
458 patching file a
459 Hunk #1 FAILED at 0
459 Hunk #1 FAILED at 0
460 1 out of 1 hunks FAILED -- saving rejects to file a.rej
460 1 out of 1 hunks FAILED -- saving rejects to file a.rej
461 patch failed to apply
461 patch failed to apply
462 abort: fix up the merge and run hg transplant --continue
462 abort: fix up the merge and run hg transplant --continue
463 [255]
463 [255]
464 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
464 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
465 HGEDITFORM=transplant.merge
465 HGEDITFORM=transplant.merge
466 600a3cdcb41d transplanted as a3f88be652e0
466 600a3cdcb41d transplanted as a3f88be652e0
467
467
468 $ cd ..
468 $ cd ..
469
469
470 test transplant into empty repository
470 test transplant into empty repository
471
471
472 $ hg init empty
472 $ hg init empty
473 $ cd empty
473 $ cd empty
474 $ hg transplant -s ../t -b tip -a
474 $ hg transplant -s ../t -b tip -a
475 adding changesets
475 adding changesets
476 adding manifests
476 adding manifests
477 adding file changes
477 adding file changes
478 added 4 changesets with 4 changes to 4 files
478 added 4 changesets with 4 changes to 4 files
479
479
480 test "--merge" causing pull from source repository on local host
480 test "--merge" causing pull from source repository on local host
481
481
482 $ hg --config extensions.mq= -q strip 2
482 $ hg --config extensions.mq= -q strip 2
483 $ hg transplant -s ../t --merge tip
483 $ hg transplant -s ../t --merge tip
484 searching for changes
484 searching for changes
485 searching for changes
485 searching for changes
486 adding changesets
486 adding changesets
487 adding manifests
487 adding manifests
488 adding file changes
488 adding file changes
489 added 2 changesets with 2 changes to 2 files
489 added 2 changesets with 2 changes to 2 files
490 applying a53251cdf717
490 applying a53251cdf717
491 4:a53251cdf717 merged at 4831f4dc831a
491 4:a53251cdf717 merged at 4831f4dc831a
492
492
493 test interactive transplant
493 test interactive transplant
494
494
495 $ hg --config extensions.strip= -q strip 0
495 $ hg --config extensions.strip= -q strip 0
496 $ hg -R ../t log -G --template "{rev}:{node|short}"
496 $ hg -R ../t log -G --template "{rev}:{node|short}"
497 @ 4:a53251cdf717
497 @ 4:a53251cdf717
498 |
498 |
499 o 3:722f4667af76
499 o 3:722f4667af76
500 |
500 |
501 o 2:37a1297eb21b
501 o 2:37a1297eb21b
502 |
502 |
503 | o 1:d11e3596cc1a
503 | o 1:d11e3596cc1a
504 |/
504 |/
505 o 0:17ab29e464c6
505 o 0:17ab29e464c6
506
506
507 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
507 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
508 > p
508 > p
509 > y
509 > y
510 > n
510 > n
511 > n
511 > n
512 > m
512 > m
513 > c
513 > c
514 > EOF
514 > EOF
515 0:17ab29e464c6
515 0:17ab29e464c6
516 apply changeset? [ynmpcq?]: p
516 apply changeset? [ynmpcq?]: p
517 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
517 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
518 +++ b/r1 Thu Jan 01 00:00:00 1970 +0000
518 +++ b/r1 Thu Jan 01 00:00:00 1970 +0000
519 @@ -0,0 +1,1 @@
519 @@ -0,0 +1,1 @@
520 +r1
520 +r1
521 apply changeset? [ynmpcq?]: y
521 apply changeset? [ynmpcq?]: y
522 1:d11e3596cc1a
522 1:d11e3596cc1a
523 apply changeset? [ynmpcq?]: n
523 apply changeset? [ynmpcq?]: n
524 2:37a1297eb21b
524 2:37a1297eb21b
525 apply changeset? [ynmpcq?]: n
525 apply changeset? [ynmpcq?]: n
526 3:722f4667af76
526 3:722f4667af76
527 apply changeset? [ynmpcq?]: m
527 apply changeset? [ynmpcq?]: m
528 4:a53251cdf717
528 4:a53251cdf717
529 apply changeset? [ynmpcq?]: c
529 apply changeset? [ynmpcq?]: c
530 $ hg log -G --template "{node|short}"
530 $ hg log -G --template "{node|short}"
531 @ 88be5dde5260
531 @ 88be5dde5260
532 |\
532 |\
533 | o 722f4667af76
533 | o 722f4667af76
534 | |
534 | |
535 | o 37a1297eb21b
535 | o 37a1297eb21b
536 |/
536 |/
537 o 17ab29e464c6
537 o 17ab29e464c6
538
538
539 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
539 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
540 > x
540 > x
541 > ?
541 > ?
542 > y
542 > y
543 > q
543 > q
544 > EOF
544 > EOF
545 1:d11e3596cc1a
545 1:d11e3596cc1a
546 apply changeset? [ynmpcq?]: x
546 apply changeset? [ynmpcq?]: x
547 unrecognized response
547 unrecognized response
548 apply changeset? [ynmpcq?]: ?
548 apply changeset? [ynmpcq?]: ?
549 y: yes, transplant this changeset
549 y: yes, transplant this changeset
550 n: no, skip this changeset
550 n: no, skip this changeset
551 m: merge at this changeset
551 m: merge at this changeset
552 p: show patch
552 p: show patch
553 c: commit selected changesets
553 c: commit selected changesets
554 q: quit and cancel transplant
554 q: quit and cancel transplant
555 ?: ? (show this help)
555 ?: ? (show this help)
556 apply changeset? [ynmpcq?]: y
556 apply changeset? [ynmpcq?]: y
557 4:a53251cdf717
557 4:a53251cdf717
558 apply changeset? [ynmpcq?]: q
558 apply changeset? [ynmpcq?]: q
559 $ hg heads --template "{node|short}\n"
559 $ hg heads --template "{node|short}\n"
560 88be5dde5260
560 88be5dde5260
561
561
562 $ cd ..
562 $ cd ..
563
563
564
564
565 #if unix-permissions system-sh
565 #if unix-permissions system-sh
566
566
567 test filter
567 test filter
568
568
569 $ hg init filter
569 $ hg init filter
570 $ cd filter
570 $ cd filter
571 $ cat <<'EOF' >test-filter
571 $ cat <<'EOF' >test-filter
572 > #!/bin/sh
572 > #!/bin/sh
573 > sed 's/r1/r2/' $1 > $1.new
573 > sed 's/r1/r2/' $1 > $1.new
574 > mv $1.new $1
574 > mv $1.new $1
575 > EOF
575 > EOF
576 $ chmod +x test-filter
576 $ chmod +x test-filter
577 $ hg transplant -s ../t -b tip -a --filter ./test-filter
577 $ hg transplant -s ../t -b tip -a --filter ./test-filter
578 filtering * (glob)
578 filtering * (glob)
579 applying 17ab29e464c6
579 applying 17ab29e464c6
580 17ab29e464c6 transplanted to e9ffc54ea104
580 17ab29e464c6 transplanted to e9ffc54ea104
581 filtering * (glob)
581 filtering * (glob)
582 applying 37a1297eb21b
582 applying 37a1297eb21b
583 37a1297eb21b transplanted to 348b36d0b6a5
583 37a1297eb21b transplanted to 348b36d0b6a5
584 filtering * (glob)
584 filtering * (glob)
585 applying 722f4667af76
585 applying 722f4667af76
586 722f4667af76 transplanted to 0aa6979afb95
586 722f4667af76 transplanted to 0aa6979afb95
587 filtering * (glob)
587 filtering * (glob)
588 applying a53251cdf717
588 applying a53251cdf717
589 a53251cdf717 transplanted to 14f8512272b5
589 a53251cdf717 transplanted to 14f8512272b5
590 $ hg log --template '{rev} {parents} {desc}\n'
590 $ hg log --template '{rev} {parents} {desc}\n'
591 3 b3
591 3 b3
592 2 b2
592 2 b2
593 1 b1
593 1 b1
594 0 r2
594 0 r2
595 $ cd ..
595 $ cd ..
596
596
597
597
598 test filter with failed patch
598 test filter with failed patch
599
599
600 $ cd filter
600 $ cd filter
601 $ hg up 0
601 $ hg up 0
602 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
602 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
603 $ echo foo > b1
603 $ echo foo > b1
604 $ hg ci -Am foo
604 $ hg ci -Am foo
605 adding b1
605 adding b1
606 adding test-filter
606 adding test-filter
607 created new head
607 created new head
608 $ hg transplant 1 --filter ./test-filter
608 $ hg transplant 1 --filter ./test-filter
609 filtering * (glob)
609 filtering * (glob)
610 applying 348b36d0b6a5
610 applying 348b36d0b6a5
611 file b1 already exists
611 file b1 already exists
612 1 out of 1 hunks FAILED -- saving rejects to file b1.rej
612 1 out of 1 hunks FAILED -- saving rejects to file b1.rej
613 patch failed to apply
613 patch failed to apply
614 abort: fix up the merge and run hg transplant --continue
614 abort: fix up the merge and run hg transplant --continue
615 [255]
615 [255]
616 $ cd ..
616 $ cd ..
617
617
618 test environment passed to filter
618 test environment passed to filter
619
619
620 $ hg init filter-environment
620 $ hg init filter-environment
621 $ cd filter-environment
621 $ cd filter-environment
622 $ cat <<'EOF' >test-filter-environment
622 $ cat <<'EOF' >test-filter-environment
623 > #!/bin/sh
623 > #!/bin/sh
624 > echo "Transplant by $HGUSER" >> $1
624 > echo "Transplant by $HGUSER" >> $1
625 > echo "Transplant from rev $HGREVISION" >> $1
625 > echo "Transplant from rev $HGREVISION" >> $1
626 > EOF
626 > EOF
627 $ chmod +x test-filter-environment
627 $ chmod +x test-filter-environment
628 $ hg transplant -s ../t --filter ./test-filter-environment 0
628 $ hg transplant -s ../t --filter ./test-filter-environment 0
629 filtering * (glob)
629 filtering * (glob)
630 applying 17ab29e464c6
630 applying 17ab29e464c6
631 17ab29e464c6 transplanted to 5190e68026a0
631 17ab29e464c6 transplanted to 5190e68026a0
632
632
633 $ hg log --template '{rev} {parents} {desc}\n'
633 $ hg log --template '{rev} {parents} {desc}\n'
634 0 r1
634 0 r1
635 Transplant by test
635 Transplant by test
636 Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f
636 Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f
637 $ cd ..
637 $ cd ..
638
638
639 test transplant with filter handles invalid changelog
639 test transplant with filter handles invalid changelog
640
640
641 $ hg init filter-invalid-log
641 $ hg init filter-invalid-log
642 $ cd filter-invalid-log
642 $ cd filter-invalid-log
643 $ cat <<'EOF' >test-filter-invalid-log
643 $ cat <<'EOF' >test-filter-invalid-log
644 > #!/bin/sh
644 > #!/bin/sh
645 > echo "" > $1
645 > echo "" > $1
646 > EOF
646 > EOF
647 $ chmod +x test-filter-invalid-log
647 $ chmod +x test-filter-invalid-log
648 $ hg transplant -s ../t --filter ./test-filter-invalid-log 0
648 $ hg transplant -s ../t --filter ./test-filter-invalid-log 0
649 filtering * (glob)
649 filtering * (glob)
650 abort: filter corrupted changeset (no user or date)
650 abort: filter corrupted changeset (no user or date)
651 [255]
651 [255]
652 $ cd ..
652 $ cd ..
653
653
654 #endif
654 #endif
655
655
656
656
657 test with a win32ext like setup (differing EOLs)
657 test with a win32ext like setup (differing EOLs)
658
658
659 $ hg init twin1
659 $ hg init twin1
660 $ cd twin1
660 $ cd twin1
661 $ echo a > a
661 $ echo a > a
662 $ echo b > b
662 $ echo b > b
663 $ echo b >> b
663 $ echo b >> b
664 $ hg ci -Am t
664 $ hg ci -Am t
665 adding a
665 adding a
666 adding b
666 adding b
667 $ echo a > b
667 $ echo a > b
668 $ echo b >> b
668 $ echo b >> b
669 $ hg ci -m changeb
669 $ hg ci -m changeb
670 $ cd ..
670 $ cd ..
671
671
672 $ hg init twin2
672 $ hg init twin2
673 $ cd twin2
673 $ cd twin2
674 $ echo '[patch]' >> .hg/hgrc
674 $ echo '[patch]' >> .hg/hgrc
675 $ echo 'eol = crlf' >> .hg/hgrc
675 $ echo 'eol = crlf' >> .hg/hgrc
676 $ $PYTHON -c "file('b', 'wb').write('b\r\nb\r\n')"
676 $ $PYTHON -c "file('b', 'wb').write('b\r\nb\r\n')"
677 $ hg ci -Am addb
677 $ hg ci -Am addb
678 adding b
678 adding b
679 $ hg transplant -s ../twin1 tip
679 $ hg transplant -s ../twin1 tip
680 searching for changes
680 searching for changes
681 warning: repository is unrelated
681 warning: repository is unrelated
682 applying 2e849d776c17
682 applying 2e849d776c17
683 2e849d776c17 transplanted to 8e65bebc063e
683 2e849d776c17 transplanted to 8e65bebc063e
684 $ cat b
684 $ cat b
685 a\r (esc)
685 a\r (esc)
686 b\r (esc)
686 b\r (esc)
687 $ cd ..
687 $ cd ..
688
688
689 test transplant with merge changeset is skipped
689 test transplant with merge changeset is skipped
690
690
691 $ hg init merge1a
691 $ hg init merge1a
692 $ cd merge1a
692 $ cd merge1a
693 $ echo a > a
693 $ echo a > a
694 $ hg ci -Am a
694 $ hg ci -Am a
695 adding a
695 adding a
696 $ hg branch b
696 $ hg branch b
697 marked working directory as branch b
697 marked working directory as branch b
698 (branches are permanent and global, did you want a bookmark?)
698 (branches are permanent and global, did you want a bookmark?)
699 $ hg ci -m branchb
699 $ hg ci -m branchb
700 $ echo b > b
700 $ echo b > b
701 $ hg ci -Am b
701 $ hg ci -Am b
702 adding b
702 adding b
703 $ hg update default
703 $ hg update default
704 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
704 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
705 $ hg merge b
705 $ hg merge b
706 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
706 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
707 (branch merge, don't forget to commit)
707 (branch merge, don't forget to commit)
708 $ hg ci -m mergeb
708 $ hg ci -m mergeb
709 $ cd ..
709 $ cd ..
710
710
711 $ hg init merge1b
711 $ hg init merge1b
712 $ cd merge1b
712 $ cd merge1b
713 $ hg transplant -s ../merge1a tip
713 $ hg transplant -s ../merge1a tip
714 $ cd ..
714 $ cd ..
715
715
716 test transplant with merge changeset accepts --parent
716 test transplant with merge changeset accepts --parent
717
717
718 $ hg init merge2a
718 $ hg init merge2a
719 $ cd merge2a
719 $ cd merge2a
720 $ echo a > a
720 $ echo a > a
721 $ hg ci -Am a
721 $ hg ci -Am a
722 adding a
722 adding a
723 $ hg branch b
723 $ hg branch b
724 marked working directory as branch b
724 marked working directory as branch b
725 (branches are permanent and global, did you want a bookmark?)
725 (branches are permanent and global, did you want a bookmark?)
726 $ hg ci -m branchb
726 $ hg ci -m branchb
727 $ echo b > b
727 $ echo b > b
728 $ hg ci -Am b
728 $ hg ci -Am b
729 adding b
729 adding b
730 $ hg update default
730 $ hg update default
731 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
731 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
732 $ hg merge b
732 $ hg merge b
733 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
733 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
734 (branch merge, don't forget to commit)
734 (branch merge, don't forget to commit)
735 $ hg ci -m mergeb
735 $ hg ci -m mergeb
736 $ cd ..
736 $ cd ..
737
737
738 $ hg init merge2b
738 $ hg init merge2b
739 $ cd merge2b
739 $ cd merge2b
740 $ hg transplant -s ../merge2a --parent 0 tip
740 $ hg transplant -s ../merge2a --parent 0 tip
741 applying be9f9b39483f
741 applying be9f9b39483f
742 be9f9b39483f transplanted to 9959e51f94d1
742 be9f9b39483f transplanted to 9959e51f94d1
743 $ cd ..
743 $ cd ..
744
744
745 test transplanting a patch turning into a no-op
745 test transplanting a patch turning into a no-op
746
746
747 $ hg init binarysource
747 $ hg init binarysource
748 $ cd binarysource
748 $ cd binarysource
749 $ echo a > a
749 $ echo a > a
750 $ hg ci -Am adda a
750 $ hg ci -Am adda a
751 >>> file('b', 'wb').write('\0b1')
751 >>> file('b', 'wb').write('\0b1')
752 $ hg ci -Am addb b
752 $ hg ci -Am addb b
753 >>> file('b', 'wb').write('\0b2')
753 >>> file('b', 'wb').write('\0b2')
754 $ hg ci -m changeb b
754 $ hg ci -m changeb b
755 $ cd ..
755 $ cd ..
756
756
757 $ hg clone -r0 binarysource binarydest
757 $ hg clone -r0 binarysource binarydest
758 adding changesets
758 adding changesets
759 adding manifests
759 adding manifests
760 adding file changes
760 adding file changes
761 added 1 changesets with 1 changes to 1 files
761 added 1 changesets with 1 changes to 1 files
762 updating to branch default
762 updating to branch default
763 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
763 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
764 $ cd binarydest
764 $ cd binarydest
765 $ cp ../binarysource/b b
765 $ cp ../binarysource/b b
766 $ hg ci -Am addb2 b
766 $ hg ci -Am addb2 b
767 $ hg transplant -s ../binarysource 2
767 $ hg transplant -s ../binarysource 2
768 searching for changes
768 searching for changes
769 applying 7a7d57e15850
769 applying 7a7d57e15850
770 skipping emptied changeset 7a7d57e15850
770 skipping emptied changeset 7a7d57e15850
771
772 Test empty result in --continue
773
774 $ hg transplant -s ../binarysource 1
775 searching for changes
776 applying 645035761929
777 file b already exists
778 1 out of 1 hunks FAILED -- saving rejects to file b.rej
779 patch failed to apply
780 abort: fix up the merge and run hg transplant --continue
781 [255]
782 $ hg status
783 ? b.rej
784 $ hg transplant --continue
785 645035761929 skipped due to empty diff
786
771 $ cd ..
787 $ cd ..
772
788
773 Explicitly kill daemons to let the test exit on Windows
789 Explicitly kill daemons to let the test exit on Windows
774
790
775 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
791 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
776
792
General Comments 0
You need to be logged in to leave comments. Login now