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