##// END OF EJS Templates
transplant: log source node when recovering too.
Brendan Cully -
r3758:889f7e74 default
parent child Browse files
Show More
@@ -1,568 +1,570
1 # Patch transplanting extension for Mercurial
1 # Patch transplanting extension for Mercurial
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from mercurial.demandload import *
8 from mercurial.demandload import *
9 from mercurial.i18n import gettext as _
9 from mercurial.i18n import gettext as _
10 demandload(globals(), 'os tempfile')
10 demandload(globals(), 'os tempfile')
11 demandload(globals(), 'mercurial:bundlerepo,cmdutil,commands,hg,merge,patch')
11 demandload(globals(), 'mercurial:bundlerepo,cmdutil,commands,hg,merge,patch')
12 demandload(globals(), 'mercurial:revlog,util')
12 demandload(globals(), 'mercurial:revlog,util')
13
13
14 '''patch transplanting tool
14 '''patch transplanting tool
15
15
16 This extension allows you to transplant patches from another branch.
16 This extension allows you to transplant patches from another branch.
17
17
18 Transplanted patches are recorded in .hg/transplant/transplants, as a map
18 Transplanted patches are recorded in .hg/transplant/transplants, as a map
19 from a changeset hash to its hash in the source repository.
19 from a changeset hash to its hash in the source repository.
20 '''
20 '''
21
21
22 class transplantentry:
22 class transplantentry:
23 def __init__(self, lnode, rnode):
23 def __init__(self, lnode, rnode):
24 self.lnode = lnode
24 self.lnode = lnode
25 self.rnode = rnode
25 self.rnode = rnode
26
26
27 class transplants:
27 class transplants:
28 def __init__(self, path=None, transplantfile=None, opener=None):
28 def __init__(self, path=None, transplantfile=None, opener=None):
29 self.path = path
29 self.path = path
30 self.transplantfile = transplantfile
30 self.transplantfile = transplantfile
31 self.opener = opener
31 self.opener = opener
32
32
33 if not opener:
33 if not opener:
34 self.opener = util.opener(self.path)
34 self.opener = util.opener(self.path)
35 self.transplants = []
35 self.transplants = []
36 self.dirty = False
36 self.dirty = False
37 self.read()
37 self.read()
38
38
39 def read(self):
39 def read(self):
40 abspath = os.path.join(self.path, self.transplantfile)
40 abspath = os.path.join(self.path, self.transplantfile)
41 if self.transplantfile and os.path.exists(abspath):
41 if self.transplantfile and os.path.exists(abspath):
42 for line in self.opener(self.transplantfile).read().splitlines():
42 for line in self.opener(self.transplantfile).read().splitlines():
43 lnode, rnode = map(revlog.bin, line.split(':'))
43 lnode, rnode = map(revlog.bin, line.split(':'))
44 self.transplants.append(transplantentry(lnode, rnode))
44 self.transplants.append(transplantentry(lnode, rnode))
45
45
46 def write(self):
46 def write(self):
47 if self.dirty and self.transplantfile:
47 if self.dirty and self.transplantfile:
48 if not os.path.isdir(self.path):
48 if not os.path.isdir(self.path):
49 os.mkdir(self.path)
49 os.mkdir(self.path)
50 fp = self.opener(self.transplantfile, 'w')
50 fp = self.opener(self.transplantfile, 'w')
51 for c in self.transplants:
51 for c in self.transplants:
52 l, r = map(revlog.hex, (c.lnode, c.rnode))
52 l, r = map(revlog.hex, (c.lnode, c.rnode))
53 fp.write(l + ':' + r + '\n')
53 fp.write(l + ':' + r + '\n')
54 fp.close()
54 fp.close()
55 self.dirty = False
55 self.dirty = False
56
56
57 def get(self, rnode):
57 def get(self, rnode):
58 return [t for t in self.transplants if t.rnode == rnode]
58 return [t for t in self.transplants if t.rnode == rnode]
59
59
60 def set(self, lnode, rnode):
60 def set(self, lnode, rnode):
61 self.transplants.append(transplantentry(lnode, rnode))
61 self.transplants.append(transplantentry(lnode, rnode))
62 self.dirty = True
62 self.dirty = True
63
63
64 def remove(self, transplant):
64 def remove(self, transplant):
65 del self.transplants[self.transplants.index(transplant)]
65 del self.transplants[self.transplants.index(transplant)]
66 self.dirty = True
66 self.dirty = True
67
67
68 class transplanter:
68 class transplanter:
69 def __init__(self, ui, repo):
69 def __init__(self, ui, repo):
70 self.ui = ui
70 self.ui = ui
71 self.path = repo.join('transplant')
71 self.path = repo.join('transplant')
72 self.opener = util.opener(self.path)
72 self.opener = util.opener(self.path)
73 self.transplants = transplants(self.path, 'transplants', opener=self.opener)
73 self.transplants = transplants(self.path, 'transplants', opener=self.opener)
74
74
75 def applied(self, repo, node, parent):
75 def applied(self, repo, node, parent):
76 '''returns True if a node is already an ancestor of parent
76 '''returns True if a node is already an ancestor of parent
77 or has already been transplanted'''
77 or has already been transplanted'''
78 if hasnode(repo, node):
78 if hasnode(repo, node):
79 if node in repo.changelog.reachable(parent, stop=node):
79 if node in repo.changelog.reachable(parent, stop=node):
80 return True
80 return True
81 for t in self.transplants.get(node):
81 for t in self.transplants.get(node):
82 # it might have been stripped
82 # it might have been stripped
83 if not hasnode(repo, t.lnode):
83 if not hasnode(repo, t.lnode):
84 self.transplants.remove(t)
84 self.transplants.remove(t)
85 return False
85 return False
86 if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
86 if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
87 return True
87 return True
88 return False
88 return False
89
89
90 def apply(self, repo, source, revmap, merges, opts={}):
90 def apply(self, repo, source, revmap, merges, opts={}):
91 '''apply the revisions in revmap one by one in revision order'''
91 '''apply the revisions in revmap one by one in revision order'''
92 revs = revmap.keys()
92 revs = revmap.keys()
93 revs.sort()
93 revs.sort()
94
94
95 p1, p2 = repo.dirstate.parents()
95 p1, p2 = repo.dirstate.parents()
96 pulls = []
96 pulls = []
97 diffopts = patch.diffopts(self.ui, opts)
97 diffopts = patch.diffopts(self.ui, opts)
98 diffopts.git = True
98 diffopts.git = True
99
99
100 lock = repo.lock()
100 lock = repo.lock()
101 wlock = repo.wlock()
101 wlock = repo.wlock()
102 try:
102 try:
103 for rev in revs:
103 for rev in revs:
104 node = revmap[rev]
104 node = revmap[rev]
105 revstr = '%s:%s' % (rev, revlog.short(node))
105 revstr = '%s:%s' % (rev, revlog.short(node))
106
106
107 if self.applied(repo, node, p1):
107 if self.applied(repo, node, p1):
108 self.ui.warn(_('skipping already applied revision %s\n') %
108 self.ui.warn(_('skipping already applied revision %s\n') %
109 revstr)
109 revstr)
110 continue
110 continue
111
111
112 parents = source.changelog.parents(node)
112 parents = source.changelog.parents(node)
113 if not opts.get('filter'):
113 if not opts.get('filter'):
114 # If the changeset parent is the same as the wdir's parent,
114 # If the changeset parent is the same as the wdir's parent,
115 # just pull it.
115 # just pull it.
116 if parents[0] == p1:
116 if parents[0] == p1:
117 pulls.append(node)
117 pulls.append(node)
118 p1 = node
118 p1 = node
119 continue
119 continue
120 if pulls:
120 if pulls:
121 if source != repo:
121 if source != repo:
122 repo.pull(source, heads=pulls, lock=lock)
122 repo.pull(source, heads=pulls, lock=lock)
123 merge.update(repo, pulls[-1], wlock=wlock)
123 merge.update(repo, pulls[-1], wlock=wlock)
124 p1, p2 = repo.dirstate.parents()
124 p1, p2 = repo.dirstate.parents()
125 pulls = []
125 pulls = []
126
126
127 domerge = False
127 domerge = False
128 if node in merges:
128 if node in merges:
129 # pulling all the merge revs at once would mean we couldn't
129 # pulling all the merge revs at once would mean we couldn't
130 # transplant after the latest even if transplants before them
130 # transplant after the latest even if transplants before them
131 # fail.
131 # fail.
132 domerge = True
132 domerge = True
133 if not hasnode(repo, node):
133 if not hasnode(repo, node):
134 repo.pull(source, heads=[node], lock=lock)
134 repo.pull(source, heads=[node], lock=lock)
135
135
136 if parents[1] != revlog.nullid:
136 if parents[1] != revlog.nullid:
137 self.ui.note(_('skipping merge changeset %s:%s\n')
137 self.ui.note(_('skipping merge changeset %s:%s\n')
138 % (rev, revlog.short(node)))
138 % (rev, revlog.short(node)))
139 patchfile = None
139 patchfile = None
140 else:
140 else:
141 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
141 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
142 fp = os.fdopen(fd, 'w')
142 fp = os.fdopen(fd, 'w')
143 patch.export(source, [node], fp=fp, opts=diffopts)
143 patch.export(source, [node], fp=fp, opts=diffopts)
144 fp.close()
144 fp.close()
145
145
146 del revmap[rev]
146 del revmap[rev]
147 if patchfile or domerge:
147 if patchfile or domerge:
148 try:
148 try:
149 n = self.applyone(repo, node, source.changelog.read(node),
149 n = self.applyone(repo, node, source.changelog.read(node),
150 patchfile, merge=domerge,
150 patchfile, merge=domerge,
151 log=opts.get('log'),
151 log=opts.get('log'),
152 filter=opts.get('filter'),
152 filter=opts.get('filter'),
153 lock=lock, wlock=wlock)
153 lock=lock, wlock=wlock)
154 if domerge:
154 if domerge:
155 self.ui.status(_('%s merged at %s\n') % (revstr,
155 self.ui.status(_('%s merged at %s\n') % (revstr,
156 revlog.short(n)))
156 revlog.short(n)))
157 else:
157 else:
158 self.ui.status(_('%s transplanted to %s\n') % (revlog.short(node),
158 self.ui.status(_('%s transplanted to %s\n') % (revlog.short(node),
159 revlog.short(n)))
159 revlog.short(n)))
160 finally:
160 finally:
161 if patchfile:
161 if patchfile:
162 os.unlink(patchfile)
162 os.unlink(patchfile)
163 if pulls:
163 if pulls:
164 repo.pull(source, heads=pulls, lock=lock)
164 repo.pull(source, heads=pulls, lock=lock)
165 merge.update(repo, pulls[-1], wlock=wlock)
165 merge.update(repo, pulls[-1], wlock=wlock)
166 finally:
166 finally:
167 self.saveseries(revmap, merges)
167 self.saveseries(revmap, merges)
168 self.transplants.write()
168 self.transplants.write()
169
169
170 def filter(self, filter, changelog, patchfile):
170 def filter(self, filter, changelog, patchfile):
171 '''arbitrarily rewrite changeset before applying it'''
171 '''arbitrarily rewrite changeset before applying it'''
172
172
173 self.ui.status('filtering %s\n' % patchfile)
173 self.ui.status('filtering %s\n' % patchfile)
174 util.system('%s %s' % (filter, util.shellquote(patchfile)),
174 util.system('%s %s' % (filter, util.shellquote(patchfile)),
175 environ={'HGUSER': changelog[1]},
175 environ={'HGUSER': changelog[1]},
176 onerr=util.Abort, errprefix=_('filter failed'))
176 onerr=util.Abort, errprefix=_('filter failed'))
177
177
178 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
178 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
179 filter=None, lock=None, wlock=None):
179 filter=None, lock=None, wlock=None):
180 '''apply the patch in patchfile to the repository as a transplant'''
180 '''apply the patch in patchfile to the repository as a transplant'''
181 (manifest, user, (time, timezone), files, message) = cl[:5]
181 (manifest, user, (time, timezone), files, message) = cl[:5]
182 date = "%d %d" % (time, timezone)
182 date = "%d %d" % (time, timezone)
183 extra = {'transplant_source': node}
183 extra = {'transplant_source': node}
184 if filter:
184 if filter:
185 self.filter(filter, cl, patchfile)
185 self.filter(filter, cl, patchfile)
186 patchfile, message, user, date = patch.extract(self.ui, file(patchfile))
186 patchfile, message, user, date = patch.extract(self.ui, file(patchfile))
187
187
188 if log:
188 if log:
189 message += '\n(transplanted from %s)' % revlog.hex(node)
189 message += '\n(transplanted from %s)' % revlog.hex(node)
190
190
191 self.ui.status(_('applying %s\n') % revlog.short(node))
191 self.ui.status(_('applying %s\n') % revlog.short(node))
192 self.ui.note('%s %s\n%s\n' % (user, date, message))
192 self.ui.note('%s %s\n%s\n' % (user, date, message))
193
193
194 if not patchfile and not merge:
194 if not patchfile and not merge:
195 raise util.Abort(_('can only omit patchfile if merging'))
195 raise util.Abort(_('can only omit patchfile if merging'))
196 if patchfile:
196 if patchfile:
197 try:
197 try:
198 files = {}
198 files = {}
199 try:
199 try:
200 fuzz = patch.patch(patchfile, self.ui, cwd=repo.root,
200 fuzz = patch.patch(patchfile, self.ui, cwd=repo.root,
201 files=files)
201 files=files)
202 if not files:
202 if not files:
203 self.ui.warn(_('%s: empty changeset') % revlog.hex(node))
203 self.ui.warn(_('%s: empty changeset') % revlog.hex(node))
204 return
204 return
205 finally:
205 finally:
206 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
206 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
207 if filter:
207 if filter:
208 os.unlink(patchfile)
208 os.unlink(patchfile)
209 except Exception, inst:
209 except Exception, inst:
210 if filter:
210 if filter:
211 os.unlink(patchfile)
211 os.unlink(patchfile)
212 seriespath = os.path.join(self.path, 'series')
212 seriespath = os.path.join(self.path, 'series')
213 if os.path.exists(seriespath):
213 if os.path.exists(seriespath):
214 os.unlink(seriespath)
214 os.unlink(seriespath)
215 p1 = repo.dirstate.parents()[0]
215 p1 = repo.dirstate.parents()[0]
216 p2 = node
216 p2 = node
217 self.log(user, date, message, p1, p2, merge=merge)
217 self.log(user, date, message, p1, p2, merge=merge)
218 self.ui.write(str(inst) + '\n')
218 self.ui.write(str(inst) + '\n')
219 raise util.Abort(_('Fix up the merge and run hg transplant --continue'))
219 raise util.Abort(_('Fix up the merge and run hg transplant --continue'))
220 else:
220 else:
221 files = None
221 files = None
222 if merge:
222 if merge:
223 p1, p2 = repo.dirstate.parents()
223 p1, p2 = repo.dirstate.parents()
224 repo.dirstate.setparents(p1, node)
224 repo.dirstate.setparents(p1, node)
225
225
226 n = repo.commit(files, message, user, date, lock=lock, wlock=wlock,
226 n = repo.commit(files, message, user, date, lock=lock, wlock=wlock,
227 extra=extra)
227 extra=extra)
228 if not merge:
228 if not merge:
229 self.transplants.set(n, node)
229 self.transplants.set(n, node)
230
230
231 return n
231 return n
232
232
233 def resume(self, repo, source, opts=None):
233 def resume(self, repo, source, opts=None):
234 '''recover last transaction and apply remaining changesets'''
234 '''recover last transaction and apply remaining changesets'''
235 if os.path.exists(os.path.join(self.path, 'journal')):
235 if os.path.exists(os.path.join(self.path, 'journal')):
236 n, node = self.recover(repo)
236 n, node = self.recover(repo)
237 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
237 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
238 revlog.short(n)))
238 revlog.short(n)))
239 seriespath = os.path.join(self.path, 'series')
239 seriespath = os.path.join(self.path, 'series')
240 if not os.path.exists(seriespath):
240 if not os.path.exists(seriespath):
241 self.transplants.write()
241 return
242 return
242 nodes, merges = self.readseries()
243 nodes, merges = self.readseries()
243 revmap = {}
244 revmap = {}
244 for n in nodes:
245 for n in nodes:
245 revmap[source.changelog.rev(n)] = n
246 revmap[source.changelog.rev(n)] = n
246 os.unlink(seriespath)
247 os.unlink(seriespath)
247
248
248 self.apply(repo, source, revmap, merges, opts)
249 self.apply(repo, source, revmap, merges, opts)
249
250
250 def recover(self, repo):
251 def recover(self, repo):
251 '''commit working directory using journal metadata'''
252 '''commit working directory using journal metadata'''
252 node, user, date, message, parents = self.readlog()
253 node, user, date, message, parents = self.readlog()
253 merge = len(parents) == 2
254 merge = len(parents) == 2
254
255
255 if not user or not date or not message or not parents[0]:
256 if not user or not date or not message or not parents[0]:
256 raise util.Abort(_('transplant log file is corrupt'))
257 raise util.Abort(_('transplant log file is corrupt'))
257
258
259 extra = {'transplant_source': node}
258 wlock = repo.wlock()
260 wlock = repo.wlock()
259 p1, p2 = repo.dirstate.parents()
261 p1, p2 = repo.dirstate.parents()
260 if p1 != parents[0]:
262 if p1 != parents[0]:
261 raise util.Abort(_('working dir not at transplant parent %s') %
263 raise util.Abort(_('working dir not at transplant parent %s') %
262 revlog.hex(parents[0]))
264 revlog.hex(parents[0]))
263 if merge:
265 if merge:
264 repo.dirstate.setparents(p1, parents[1])
266 repo.dirstate.setparents(p1, parents[1])
265 n = repo.commit(None, message, user, date, wlock=wlock)
267 n = repo.commit(None, message, user, date, wlock=wlock, extra=extra)
266 if not n:
268 if not n:
267 raise util.Abort(_('commit failed'))
269 raise util.Abort(_('commit failed'))
268 if not merge:
270 if not merge:
269 self.transplants.set(n, node)
271 self.transplants.set(n, node)
270 self.unlog()
272 self.unlog()
271
273
272 return n, node
274 return n, node
273
275
274 def readseries(self):
276 def readseries(self):
275 nodes = []
277 nodes = []
276 merges = []
278 merges = []
277 cur = nodes
279 cur = nodes
278 for line in self.opener('series').read().splitlines():
280 for line in self.opener('series').read().splitlines():
279 if line.startswith('# Merges'):
281 if line.startswith('# Merges'):
280 cur = merges
282 cur = merges
281 continue
283 continue
282 cur.append(revlog.bin(line))
284 cur.append(revlog.bin(line))
283
285
284 return (nodes, merges)
286 return (nodes, merges)
285
287
286 def saveseries(self, revmap, merges):
288 def saveseries(self, revmap, merges):
287 if not revmap:
289 if not revmap:
288 return
290 return
289
291
290 if not os.path.isdir(self.path):
292 if not os.path.isdir(self.path):
291 os.mkdir(self.path)
293 os.mkdir(self.path)
292 series = self.opener('series', 'w')
294 series = self.opener('series', 'w')
293 revs = revmap.keys()
295 revs = revmap.keys()
294 revs.sort()
296 revs.sort()
295 for rev in revs:
297 for rev in revs:
296 series.write(revlog.hex(revmap[rev]) + '\n')
298 series.write(revlog.hex(revmap[rev]) + '\n')
297 if merges:
299 if merges:
298 series.write('# Merges\n')
300 series.write('# Merges\n')
299 for m in merges:
301 for m in merges:
300 series.write(revlog.hex(m) + '\n')
302 series.write(revlog.hex(m) + '\n')
301 series.close()
303 series.close()
302
304
303 def log(self, user, date, message, p1, p2, merge=False):
305 def log(self, user, date, message, p1, p2, merge=False):
304 '''journal changelog metadata for later recover'''
306 '''journal changelog metadata for later recover'''
305
307
306 if not os.path.isdir(self.path):
308 if not os.path.isdir(self.path):
307 os.mkdir(self.path)
309 os.mkdir(self.path)
308 fp = self.opener('journal', 'w')
310 fp = self.opener('journal', 'w')
309 fp.write('# User %s\n' % user)
311 fp.write('# User %s\n' % user)
310 fp.write('# Date %s\n' % date)
312 fp.write('# Date %s\n' % date)
311 fp.write('# Node ID %s\n' % revlog.hex(p2))
313 fp.write('# Node ID %s\n' % revlog.hex(p2))
312 fp.write('# Parent ' + revlog.hex(p1) + '\n')
314 fp.write('# Parent ' + revlog.hex(p1) + '\n')
313 if merge:
315 if merge:
314 fp.write('# Parent ' + revlog.hex(p2) + '\n')
316 fp.write('# Parent ' + revlog.hex(p2) + '\n')
315 fp.write(message.rstrip() + '\n')
317 fp.write(message.rstrip() + '\n')
316 fp.close()
318 fp.close()
317
319
318 def readlog(self):
320 def readlog(self):
319 parents = []
321 parents = []
320 message = []
322 message = []
321 for line in self.opener('journal').read().splitlines():
323 for line in self.opener('journal').read().splitlines():
322 if line.startswith('# User '):
324 if line.startswith('# User '):
323 user = line[7:]
325 user = line[7:]
324 elif line.startswith('# Date '):
326 elif line.startswith('# Date '):
325 date = line[7:]
327 date = line[7:]
326 elif line.startswith('# Node ID '):
328 elif line.startswith('# Node ID '):
327 node = revlog.bin(line[10:])
329 node = revlog.bin(line[10:])
328 elif line.startswith('# Parent '):
330 elif line.startswith('# Parent '):
329 parents.append(revlog.bin(line[9:]))
331 parents.append(revlog.bin(line[9:]))
330 else:
332 else:
331 message.append(line)
333 message.append(line)
332 return (node, user, date, '\n'.join(message), parents)
334 return (node, user, date, '\n'.join(message), parents)
333
335
334 def unlog(self):
336 def unlog(self):
335 '''remove changelog journal'''
337 '''remove changelog journal'''
336 absdst = os.path.join(self.path, 'journal')
338 absdst = os.path.join(self.path, 'journal')
337 if os.path.exists(absdst):
339 if os.path.exists(absdst):
338 os.unlink(absdst)
340 os.unlink(absdst)
339
341
340 def transplantfilter(self, repo, source, root):
342 def transplantfilter(self, repo, source, root):
341 def matchfn(node):
343 def matchfn(node):
342 if self.applied(repo, node, root):
344 if self.applied(repo, node, root):
343 return False
345 return False
344 if source.changelog.parents(node)[1] != revlog.nullid:
346 if source.changelog.parents(node)[1] != revlog.nullid:
345 return False
347 return False
346 extra = source.changelog.read(node)[5]
348 extra = source.changelog.read(node)[5]
347 cnode = extra.get('transplant_source')
349 cnode = extra.get('transplant_source')
348 if cnode and self.applied(repo, cnode, root):
350 if cnode and self.applied(repo, cnode, root):
349 return False
351 return False
350 return True
352 return True
351
353
352 return matchfn
354 return matchfn
353
355
354 def hasnode(repo, node):
356 def hasnode(repo, node):
355 try:
357 try:
356 return repo.changelog.rev(node) != None
358 return repo.changelog.rev(node) != None
357 except revlog.RevlogError:
359 except revlog.RevlogError:
358 return False
360 return False
359
361
360 def browserevs(ui, repo, nodes, opts):
362 def browserevs(ui, repo, nodes, opts):
361 '''interactively transplant changesets'''
363 '''interactively transplant changesets'''
362 def browsehelp(ui):
364 def browsehelp(ui):
363 ui.write('y: transplant this changeset\n'
365 ui.write('y: transplant this changeset\n'
364 'n: skip this changeset\n'
366 'n: skip this changeset\n'
365 'm: merge at this changeset\n'
367 'm: merge at this changeset\n'
366 'p: show patch\n'
368 'p: show patch\n'
367 'c: commit selected changesets\n'
369 'c: commit selected changesets\n'
368 'q: cancel transplant\n'
370 'q: cancel transplant\n'
369 '?: show this help\n')
371 '?: show this help\n')
370
372
371 displayer = cmdutil.show_changeset(ui, repo, opts)
373 displayer = cmdutil.show_changeset(ui, repo, opts)
372 transplants = []
374 transplants = []
373 merges = []
375 merges = []
374 for node in nodes:
376 for node in nodes:
375 displayer.show(changenode=node)
377 displayer.show(changenode=node)
376 action = None
378 action = None
377 while not action:
379 while not action:
378 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
380 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
379 if action == '?':
381 if action == '?':
380 browsehelp(ui)
382 browsehelp(ui)
381 action = None
383 action = None
382 elif action == 'p':
384 elif action == 'p':
383 parent = repo.changelog.parents(node)[0]
385 parent = repo.changelog.parents(node)[0]
384 patch.diff(repo, parent, node)
386 patch.diff(repo, parent, node)
385 action = None
387 action = None
386 elif action not in ('y', 'n', 'm', 'c', 'q'):
388 elif action not in ('y', 'n', 'm', 'c', 'q'):
387 ui.write('no such option\n')
389 ui.write('no such option\n')
388 action = None
390 action = None
389 if action == 'y':
391 if action == 'y':
390 transplants.append(node)
392 transplants.append(node)
391 elif action == 'm':
393 elif action == 'm':
392 merges.append(node)
394 merges.append(node)
393 elif action == 'c':
395 elif action == 'c':
394 break
396 break
395 elif action == 'q':
397 elif action == 'q':
396 transplants = ()
398 transplants = ()
397 merges = ()
399 merges = ()
398 break
400 break
399 return (transplants, merges)
401 return (transplants, merges)
400
402
401 def transplant(ui, repo, *revs, **opts):
403 def transplant(ui, repo, *revs, **opts):
402 '''transplant changesets from another branch
404 '''transplant changesets from another branch
403
405
404 Selected changesets will be applied on top of the current working
406 Selected changesets will be applied on top of the current working
405 directory with the log of the original changeset. If --log is
407 directory with the log of the original changeset. If --log is
406 specified, log messages will have a comment appended of the form:
408 specified, log messages will have a comment appended of the form:
407
409
408 (transplanted from CHANGESETHASH)
410 (transplanted from CHANGESETHASH)
409
411
410 You can rewrite the changelog message with the --filter option.
412 You can rewrite the changelog message with the --filter option.
411 Its argument will be invoked with the current changelog message
413 Its argument will be invoked with the current changelog message
412 as $1 and the patch as $2.
414 as $1 and the patch as $2.
413
415
414 If --source is specified, selects changesets from the named
416 If --source is specified, selects changesets from the named
415 repository. If --branch is specified, selects changesets from the
417 repository. If --branch is specified, selects changesets from the
416 branch holding the named revision, up to that revision. If --all
418 branch holding the named revision, up to that revision. If --all
417 is specified, all changesets on the branch will be transplanted,
419 is specified, all changesets on the branch will be transplanted,
418 otherwise you will be prompted to select the changesets you want.
420 otherwise you will be prompted to select the changesets you want.
419
421
420 hg transplant --branch REVISION --all will rebase the selected branch
422 hg transplant --branch REVISION --all will rebase the selected branch
421 (up to the named revision) onto your current working directory.
423 (up to the named revision) onto your current working directory.
422
424
423 You can optionally mark selected transplanted changesets as
425 You can optionally mark selected transplanted changesets as
424 merge changesets. You will not be prompted to transplant any
426 merge changesets. You will not be prompted to transplant any
425 ancestors of a merged transplant, and you can merge descendants
427 ancestors of a merged transplant, and you can merge descendants
426 of them normally instead of transplanting them.
428 of them normally instead of transplanting them.
427
429
428 If no merges or revisions are provided, hg transplant will start
430 If no merges or revisions are provided, hg transplant will start
429 an interactive changeset browser.
431 an interactive changeset browser.
430
432
431 If a changeset application fails, you can fix the merge by hand and
433 If a changeset application fails, you can fix the merge by hand and
432 then resume where you left off by calling hg transplant --continue.
434 then resume where you left off by calling hg transplant --continue.
433 '''
435 '''
434 def getoneitem(opts, item, errmsg):
436 def getoneitem(opts, item, errmsg):
435 val = opts.get(item)
437 val = opts.get(item)
436 if val:
438 if val:
437 if len(val) > 1:
439 if len(val) > 1:
438 raise util.Abort(errmsg)
440 raise util.Abort(errmsg)
439 else:
441 else:
440 return val[0]
442 return val[0]
441
443
442 def getremotechanges(repo, url):
444 def getremotechanges(repo, url):
443 sourcerepo = ui.expandpath(url)
445 sourcerepo = ui.expandpath(url)
444 source = hg.repository(ui, sourcerepo)
446 source = hg.repository(ui, sourcerepo)
445 incoming = repo.findincoming(source, force=True)
447 incoming = repo.findincoming(source, force=True)
446 if not incoming:
448 if not incoming:
447 return (source, None, None)
449 return (source, None, None)
448
450
449 bundle = None
451 bundle = None
450 if not source.local():
452 if not source.local():
451 cg = source.changegroup(incoming, 'incoming')
453 cg = source.changegroup(incoming, 'incoming')
452 bundle = commands.write_bundle(cg, compress=False)
454 bundle = commands.write_bundle(cg, compress=False)
453 source = bundlerepo.bundlerepository(ui, repo.root, bundle)
455 source = bundlerepo.bundlerepository(ui, repo.root, bundle)
454
456
455 return (source, incoming, bundle)
457 return (source, incoming, bundle)
456
458
457 def incwalk(repo, incoming, branches, match=util.always):
459 def incwalk(repo, incoming, branches, match=util.always):
458 if not branches:
460 if not branches:
459 branches=None
461 branches=None
460 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
462 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
461 if match(node):
463 if match(node):
462 yield node
464 yield node
463
465
464 def transplantwalk(repo, root, branches, match=util.always):
466 def transplantwalk(repo, root, branches, match=util.always):
465 if not branches:
467 if not branches:
466 branches = repo.heads()
468 branches = repo.heads()
467 ancestors = []
469 ancestors = []
468 for branch in branches:
470 for branch in branches:
469 ancestors.append(repo.changelog.ancestor(root, branch))
471 ancestors.append(repo.changelog.ancestor(root, branch))
470 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
472 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
471 if match(node):
473 if match(node):
472 yield node
474 yield node
473
475
474 def checkopts(opts, revs):
476 def checkopts(opts, revs):
475 if opts.get('continue'):
477 if opts.get('continue'):
476 if filter(lambda opt: opts.get(opt), ('branch', 'all', 'merge')):
478 if filter(lambda opt: opts.get(opt), ('branch', 'all', 'merge')):
477 raise util.Abort(_('--continue is incompatible with branch, all or merge'))
479 raise util.Abort(_('--continue is incompatible with branch, all or merge'))
478 return
480 return
479 if not (opts.get('source') or revs or
481 if not (opts.get('source') or revs or
480 opts.get('merge') or opts.get('branch')):
482 opts.get('merge') or opts.get('branch')):
481 raise util.Abort(_('no source URL, branch tag or revision list provided'))
483 raise util.Abort(_('no source URL, branch tag or revision list provided'))
482 if opts.get('all'):
484 if opts.get('all'):
483 if not opts.get('branch'):
485 if not opts.get('branch'):
484 raise util.Abort(_('--all requires a branch revision'))
486 raise util.Abort(_('--all requires a branch revision'))
485 if revs:
487 if revs:
486 raise util.Abort(_('--all is incompatible with a revision list'))
488 raise util.Abort(_('--all is incompatible with a revision list'))
487
489
488 checkopts(opts, revs)
490 checkopts(opts, revs)
489
491
490 if not opts.get('log'):
492 if not opts.get('log'):
491 opts['log'] = ui.config('transplant', 'log')
493 opts['log'] = ui.config('transplant', 'log')
492 if not opts.get('filter'):
494 if not opts.get('filter'):
493 opts['filter'] = ui.config('transplant', 'filter')
495 opts['filter'] = ui.config('transplant', 'filter')
494
496
495 tp = transplanter(ui, repo)
497 tp = transplanter(ui, repo)
496
498
497 p1, p2 = repo.dirstate.parents()
499 p1, p2 = repo.dirstate.parents()
498 if p1 == revlog.nullid:
500 if p1 == revlog.nullid:
499 raise util.Abort(_('no revision checked out'))
501 raise util.Abort(_('no revision checked out'))
500 if not opts.get('continue'):
502 if not opts.get('continue'):
501 if p2 != revlog.nullid:
503 if p2 != revlog.nullid:
502 raise util.Abort(_('outstanding uncommitted merges'))
504 raise util.Abort(_('outstanding uncommitted merges'))
503 m, a, r, d = repo.status()[:4]
505 m, a, r, d = repo.status()[:4]
504 if m or a or r or d:
506 if m or a or r or d:
505 raise util.Abort(_('outstanding local changes'))
507 raise util.Abort(_('outstanding local changes'))
506
508
507 bundle = None
509 bundle = None
508 source = opts.get('source')
510 source = opts.get('source')
509 if source:
511 if source:
510 (source, incoming, bundle) = getremotechanges(repo, source)
512 (source, incoming, bundle) = getremotechanges(repo, source)
511 else:
513 else:
512 source = repo
514 source = repo
513
515
514 try:
516 try:
515 if opts.get('continue'):
517 if opts.get('continue'):
516 tp.resume(repo, source, opts)
518 tp.resume(repo, source, opts)
517 return
519 return
518
520
519 tf=tp.transplantfilter(repo, source, p1)
521 tf=tp.transplantfilter(repo, source, p1)
520 if opts.get('prune'):
522 if opts.get('prune'):
521 prune = [source.lookup(r)
523 prune = [source.lookup(r)
522 for r in cmdutil.revrange(source, opts.get('prune'))]
524 for r in cmdutil.revrange(source, opts.get('prune'))]
523 matchfn = lambda x: tf(x) and x not in prune
525 matchfn = lambda x: tf(x) and x not in prune
524 else:
526 else:
525 matchfn = tf
527 matchfn = tf
526 branches = map(source.lookup, opts.get('branch', ()))
528 branches = map(source.lookup, opts.get('branch', ()))
527 merges = map(source.lookup, opts.get('merge', ()))
529 merges = map(source.lookup, opts.get('merge', ()))
528 revmap = {}
530 revmap = {}
529 if revs:
531 if revs:
530 for r in cmdutil.revrange(source, revs):
532 for r in cmdutil.revrange(source, revs):
531 revmap[int(r)] = source.lookup(r)
533 revmap[int(r)] = source.lookup(r)
532 elif opts.get('all') or not merges:
534 elif opts.get('all') or not merges:
533 if source != repo:
535 if source != repo:
534 alltransplants = incwalk(source, incoming, branches, match=matchfn)
536 alltransplants = incwalk(source, incoming, branches, match=matchfn)
535 else:
537 else:
536 alltransplants = transplantwalk(source, p1, branches, match=matchfn)
538 alltransplants = transplantwalk(source, p1, branches, match=matchfn)
537 if opts.get('all'):
539 if opts.get('all'):
538 revs = alltransplants
540 revs = alltransplants
539 else:
541 else:
540 revs, newmerges = browserevs(ui, source, alltransplants, opts)
542 revs, newmerges = browserevs(ui, source, alltransplants, opts)
541 merges.extend(newmerges)
543 merges.extend(newmerges)
542 for r in revs:
544 for r in revs:
543 revmap[source.changelog.rev(r)] = r
545 revmap[source.changelog.rev(r)] = r
544 for r in merges:
546 for r in merges:
545 revmap[source.changelog.rev(r)] = r
547 revmap[source.changelog.rev(r)] = r
546
548
547 revs = revmap.keys()
549 revs = revmap.keys()
548 revs.sort()
550 revs.sort()
549 pulls = []
551 pulls = []
550
552
551 tp.apply(repo, source, revmap, merges, opts)
553 tp.apply(repo, source, revmap, merges, opts)
552 finally:
554 finally:
553 if bundle:
555 if bundle:
554 os.unlink(bundle)
556 os.unlink(bundle)
555
557
556 cmdtable = {
558 cmdtable = {
557 "transplant":
559 "transplant":
558 (transplant,
560 (transplant,
559 [('s', 'source', '', _('pull patches from REPOSITORY')),
561 [('s', 'source', '', _('pull patches from REPOSITORY')),
560 ('b', 'branch', [], _('pull patches from branch BRANCH')),
562 ('b', 'branch', [], _('pull patches from branch BRANCH')),
561 ('a', 'all', None, _('pull all changesets up to BRANCH')),
563 ('a', 'all', None, _('pull all changesets up to BRANCH')),
562 ('p', 'prune', [], _('skip over REV')),
564 ('p', 'prune', [], _('skip over REV')),
563 ('m', 'merge', [], _('merge at REV')),
565 ('m', 'merge', [], _('merge at REV')),
564 ('', 'log', None, _('append transplant info to log message')),
566 ('', 'log', None, _('append transplant info to log message')),
565 ('c', 'continue', None, _('continue last transplant session after repair')),
567 ('c', 'continue', None, _('continue last transplant session after repair')),
566 ('', 'filter', '', _('filter changesets through FILTER'))],
568 ('', 'filter', '', _('filter changesets through FILTER'))],
567 _('hg transplant [-s REPOSITORY] [-b BRANCH] [-p REV] [-m REV] [-n] REV...'))
569 _('hg transplant [-s REPOSITORY] [-b BRANCH] [-p REV] [-m REV] [-n] REV...'))
568 }
570 }
@@ -1,90 +1,93
1 #!/bin/sh
1 #!/bin/sh
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 echo r2 > r2
12 echo r2 > r2
13 hg ci -Amr2 -d'1 0'
13 hg ci -Amr2 -d'1 0'
14 hg up 0
14 hg up 0
15
15
16 echo b1 > b1
16 echo b1 > b1
17 hg ci -Amb1 -d '0 0'
17 hg ci -Amb1 -d '0 0'
18 echo b2 > b2
18 echo b2 > b2
19 hg ci -Amb2 -d '1 0'
19 hg ci -Amb2 -d '1 0'
20 echo b3 > b3
20 echo b3 > b3
21 hg ci -Amb3 -d '2 0'
21 hg ci -Amb3 -d '2 0'
22
22
23 hg log --template '{rev} {parents} {desc}\n'
23 hg log --template '{rev} {parents} {desc}\n'
24
24
25 cd ..
25 cd ..
26 hg clone t rebase
26 hg clone t rebase
27 cd rebase
27 cd rebase
28
28
29 hg up -C 1
29 hg up -C 1
30 echo '% rebase b onto r1'
30 echo '% rebase b onto r1'
31 hg transplant -a -b tip
31 hg transplant -a -b tip
32 hg log --template '{rev} {parents} {desc}\n'
32 hg log --template '{rev} {parents} {desc}\n'
33
33
34 cd ..
34 cd ..
35 hg clone t prune
35 hg clone t prune
36 cd prune
36 cd prune
37
37
38 hg up -C 1
38 hg up -C 1
39 echo '% rebase b onto r1, skipping b2'
39 echo '% rebase b onto r1, skipping b2'
40 hg transplant -a -b tip -p 3
40 hg transplant -a -b tip -p 3
41 hg log --template '{rev} {parents} {desc}\n'
41 hg log --template '{rev} {parents} {desc}\n'
42
42
43 cd ..
43 cd ..
44 echo '% remote transplant'
44 echo '% remote transplant'
45 hg clone -r 1 t remote
45 hg clone -r 1 t remote
46 cd remote
46 cd remote
47 hg transplant --log -s ../t 2 4
47 hg transplant --log -s ../t 2 4
48 hg log --template '{rev} {parents} {desc}\n'
48 hg log --template '{rev} {parents} {desc}\n'
49
49
50 echo '% skip previous transplants'
50 echo '% skip previous transplants'
51 hg transplant -s ../t -a -b 4
51 hg transplant -s ../t -a -b 4
52 hg log --template '{rev} {parents} {desc}\n'
52 hg log --template '{rev} {parents} {desc}\n'
53
53
54 echo '% skip local changes transplanted to the source'
54 echo '% skip local changes transplanted to the source'
55 echo b4 > b4
55 echo b4 > b4
56 hg ci -Amb4 -d '3 0'
56 hg ci -Amb4 -d '3 0'
57 cd ..
57 cd ..
58 hg clone t pullback
58 hg clone t pullback
59 cd pullback
59 cd pullback
60 hg transplant -s ../remote -a -b tip
60 hg transplant -s ../remote -a -b tip
61
61
62 echo '% transplant --continue'
62 echo '% transplant --continue'
63 hg init ../tc
63 hg init ../tc
64 cd ../tc
64 cd ../tc
65 cat <<EOF > foo
65 cat <<EOF > foo
66 foo
66 foo
67 bar
67 bar
68 baz
68 baz
69 EOF
69 EOF
70 echo toremove > toremove
70 echo toremove > toremove
71 hg ci -Amfoo -d '0 0'
71 hg ci -Amfoo -d '0 0'
72 cat <<EOF > foo
72 cat <<EOF > foo
73 foo2
73 foo2
74 bar2
74 bar2
75 baz2
75 baz2
76 EOF
76 EOF
77 rm toremove
77 rm toremove
78 echo added > added
78 echo added > added
79 hg ci -Amfoo2 -d '0 0'
79 hg ci -Amfoo2 -d '0 0'
80 echo bar > bar
80 echo bar > bar
81 hg ci -Ambar -d '0 0'
81 hg ci -Ambar -d '0 0'
82 echo bar2 >> bar
82 echo bar2 >> bar
83 hg ci -mbar2 -d '0 0'
83 hg ci -mbar2 -d '0 0'
84 hg up 0
84 hg up 0
85 echo foobar > foo
85 echo foobar > foo
86 hg ci -mfoobar -d '0 0'
86 hg ci -mfoobar -d '0 0'
87 hg transplant 1:3
87 hg transplant 1:3
88 echo merge > foo
88 # transplant -c shouldn't use an old changeset
89 hg up -C
90 hg transplant 1
89 hg transplant --continue
91 hg transplant --continue
92 hg transplant 1:3
90 hg locate
93 hg locate
@@ -1,98 +1,106
1 adding r1
1 adding r1
2 adding r2
2 adding r2
3 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
3 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
4 adding b1
4 adding b1
5 adding b2
5 adding b2
6 adding b3
6 adding b3
7 4 b3
7 4 b3
8 3 b2
8 3 b2
9 2 0:17ab29e464c6 b1
9 2 0:17ab29e464c6 b1
10 1 r2
10 1 r2
11 0 r1
11 0 r1
12 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
13 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
14 % rebase b onto r1
14 % rebase b onto r1
15 applying 37a1297eb21b
15 applying 37a1297eb21b
16 37a1297eb21b transplanted to e234d668f844
16 37a1297eb21b transplanted to e234d668f844
17 applying 722f4667af76
17 applying 722f4667af76
18 722f4667af76 transplanted to 539f377d78df
18 722f4667af76 transplanted to 539f377d78df
19 applying a53251cdf717
19 applying a53251cdf717
20 a53251cdf717 transplanted to ffd6818a3975
20 a53251cdf717 transplanted to ffd6818a3975
21 7 b3
21 7 b3
22 6 b2
22 6 b2
23 5 1:d11e3596cc1a b1
23 5 1:d11e3596cc1a b1
24 4 b3
24 4 b3
25 3 b2
25 3 b2
26 2 0:17ab29e464c6 b1
26 2 0:17ab29e464c6 b1
27 1 r2
27 1 r2
28 0 r1
28 0 r1
29 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
30 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
31 % rebase b onto r1, skipping b2
31 % rebase b onto r1, skipping b2
32 applying 37a1297eb21b
32 applying 37a1297eb21b
33 37a1297eb21b transplanted to e234d668f844
33 37a1297eb21b transplanted to e234d668f844
34 applying a53251cdf717
34 applying a53251cdf717
35 a53251cdf717 transplanted to 7275fda4d04f
35 a53251cdf717 transplanted to 7275fda4d04f
36 6 b3
36 6 b3
37 5 1:d11e3596cc1a b1
37 5 1:d11e3596cc1a b1
38 4 b3
38 4 b3
39 3 b2
39 3 b2
40 2 0:17ab29e464c6 b1
40 2 0:17ab29e464c6 b1
41 1 r2
41 1 r2
42 0 r1
42 0 r1
43 % remote transplant
43 % remote transplant
44 requesting all changes
44 requesting all changes
45 adding changesets
45 adding changesets
46 adding manifests
46 adding manifests
47 adding file changes
47 adding file changes
48 added 2 changesets with 2 changes to 2 files
48 added 2 changesets with 2 changes to 2 files
49 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 searching for changes
50 searching for changes
51 applying 37a1297eb21b
51 applying 37a1297eb21b
52 37a1297eb21b transplanted to c19cf0ccb069
52 37a1297eb21b transplanted to c19cf0ccb069
53 applying a53251cdf717
53 applying a53251cdf717
54 a53251cdf717 transplanted to f7fe5bf98525
54 a53251cdf717 transplanted to f7fe5bf98525
55 3 b3
55 3 b3
56 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
56 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
57 2 b1
57 2 b1
58 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
58 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
59 1 r2
59 1 r2
60 0 r1
60 0 r1
61 % skip previous transplants
61 % skip previous transplants
62 searching for changes
62 searching for changes
63 applying 722f4667af76
63 applying 722f4667af76
64 722f4667af76 transplanted to 47156cd86c0b
64 722f4667af76 transplanted to 47156cd86c0b
65 4 b2
65 4 b2
66 3 b3
66 3 b3
67 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
67 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
68 2 b1
68 2 b1
69 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
69 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
70 1 r2
70 1 r2
71 0 r1
71 0 r1
72 % skip local changes transplanted to the source
72 % skip local changes transplanted to the source
73 adding b4
73 adding b4
74 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
74 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
75 searching for changes
75 searching for changes
76 applying 4333daefcb15
76 applying 4333daefcb15
77 4333daefcb15 transplanted to 5f42c04e07cc
77 4333daefcb15 transplanted to 5f42c04e07cc
78 % transplant --continue
78 % transplant --continue
79 adding foo
79 adding foo
80 adding toremove
80 adding toremove
81 adding added
81 adding added
82 removing toremove
82 removing toremove
83 adding bar
83 adding bar
84 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
84 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
85 applying a1e30dd1b8e7
85 applying a1e30dd1b8e7
86 foo
86 foo
87 Hunk #1 FAILED at 1.
87 Hunk #1 FAILED at 1.
88 1 out of 1 hunk FAILED -- saving rejects to file foo.rej
88 1 out of 1 hunk FAILED -- saving rejects to file foo.rej
89 patch command failed: exited with status 1
89 patch command failed: exited with status 1
90 abort: Fix up the merge and run hg transplant --continue
90 abort: Fix up the merge and run hg transplant --continue
91 a1e30dd1b8e7 transplanted as e6d0b5145568
91 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
92 applying a1e30dd1b8e7
93 foo
94 Hunk #1 FAILED at 1.
95 1 out of 1 hunk FAILED -- saving rejects to file foo.rej
96 patch command failed: exited with status 1
97 abort: Fix up the merge and run hg transplant --continue
98 a1e30dd1b8e7 transplanted as f1563cf27039
99 skipping already applied revision 1:a1e30dd1b8e7
92 applying 1739ac5f6139
100 applying 1739ac5f6139
93 1739ac5f6139 transplanted to 48f780141a79
101 1739ac5f6139 transplanted to d649c221319f
94 applying 0282d5fbbe02
102 applying 0282d5fbbe02
95 0282d5fbbe02 transplanted to 821d17b1a3ed
103 0282d5fbbe02 transplanted to 77418277ccb3
96 added
104 added
97 bar
105 bar
98 foo
106 foo
General Comments 0
You need to be logged in to leave comments. Login now