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