##// END OF EJS Templates
transplant: use absolute_import
timeless -
r28481:ec75c942 default
parent child Browse files
Show More
@@ -1,727 +1,742 b''
1 # Patch transplanting extension for Mercurial
1 # Patch transplanting extension for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''command to transplant changesets from another branch
8 '''command to transplant changesets from another branch
9
9
10 This extension allows you to transplant changes to another parent revision,
10 This extension allows you to transplant changes to another parent revision,
11 possibly in another repository. The transplant is done using 'diff' patches.
11 possibly in another repository. The transplant is done using 'diff' patches.
12
12
13 Transplanted patches are recorded in .hg/transplant/transplants, as a
13 Transplanted patches are recorded in .hg/transplant/transplants, as a
14 map from a changeset hash to its hash in the source repository.
14 map from a changeset hash to its hash in the source repository.
15 '''
15 '''
16 from __future__ import absolute_import
16
17
18 import os
19 import tempfile
17 from mercurial.i18n import _
20 from mercurial.i18n import _
18 import os, tempfile
21 from mercurial import (
19 from mercurial import node as nodemod
22 bundlerepo,
20 from mercurial import bundlerepo, hg, merge, match
23 cmdutil,
21 from mercurial import patch, revlog, scmutil, util, error, cmdutil
24 error,
22 from mercurial import registrar, revset, templatekw, exchange
25 exchange,
26 hg,
27 match,
28 merge,
29 node as nodemod,
30 patch,
31 registrar,
32 revlog,
33 revset,
34 scmutil,
35 templatekw,
36 util,
37 )
23
38
24 class TransplantError(error.Abort):
39 class TransplantError(error.Abort):
25 pass
40 pass
26
41
27 cmdtable = {}
42 cmdtable = {}
28 command = cmdutil.command(cmdtable)
43 command = cmdutil.command(cmdtable)
29 # Note for extension authors: ONLY specify testedwith = 'internal' for
44 # Note for extension authors: ONLY specify testedwith = 'internal' for
30 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
45 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
31 # be specifying the version(s) of Mercurial they are tested with, or
46 # be specifying the version(s) of Mercurial they are tested with, or
32 # leave the attribute unspecified.
47 # leave the attribute unspecified.
33 testedwith = 'internal'
48 testedwith = 'internal'
34
49
35 class transplantentry(object):
50 class transplantentry(object):
36 def __init__(self, lnode, rnode):
51 def __init__(self, lnode, rnode):
37 self.lnode = lnode
52 self.lnode = lnode
38 self.rnode = rnode
53 self.rnode = rnode
39
54
40 class transplants(object):
55 class transplants(object):
41 def __init__(self, path=None, transplantfile=None, opener=None):
56 def __init__(self, path=None, transplantfile=None, opener=None):
42 self.path = path
57 self.path = path
43 self.transplantfile = transplantfile
58 self.transplantfile = transplantfile
44 self.opener = opener
59 self.opener = opener
45
60
46 if not opener:
61 if not opener:
47 self.opener = scmutil.opener(self.path)
62 self.opener = scmutil.opener(self.path)
48 self.transplants = {}
63 self.transplants = {}
49 self.dirty = False
64 self.dirty = False
50 self.read()
65 self.read()
51
66
52 def read(self):
67 def read(self):
53 abspath = os.path.join(self.path, self.transplantfile)
68 abspath = os.path.join(self.path, self.transplantfile)
54 if self.transplantfile and os.path.exists(abspath):
69 if self.transplantfile and os.path.exists(abspath):
55 for line in self.opener.read(self.transplantfile).splitlines():
70 for line in self.opener.read(self.transplantfile).splitlines():
56 lnode, rnode = map(revlog.bin, line.split(':'))
71 lnode, rnode = map(revlog.bin, line.split(':'))
57 list = self.transplants.setdefault(rnode, [])
72 list = self.transplants.setdefault(rnode, [])
58 list.append(transplantentry(lnode, rnode))
73 list.append(transplantentry(lnode, rnode))
59
74
60 def write(self):
75 def write(self):
61 if self.dirty and self.transplantfile:
76 if self.dirty and self.transplantfile:
62 if not os.path.isdir(self.path):
77 if not os.path.isdir(self.path):
63 os.mkdir(self.path)
78 os.mkdir(self.path)
64 fp = self.opener(self.transplantfile, 'w')
79 fp = self.opener(self.transplantfile, 'w')
65 for list in self.transplants.itervalues():
80 for list in self.transplants.itervalues():
66 for t in list:
81 for t in list:
67 l, r = map(nodemod.hex, (t.lnode, t.rnode))
82 l, r = map(nodemod.hex, (t.lnode, t.rnode))
68 fp.write(l + ':' + r + '\n')
83 fp.write(l + ':' + r + '\n')
69 fp.close()
84 fp.close()
70 self.dirty = False
85 self.dirty = False
71
86
72 def get(self, rnode):
87 def get(self, rnode):
73 return self.transplants.get(rnode) or []
88 return self.transplants.get(rnode) or []
74
89
75 def set(self, lnode, rnode):
90 def set(self, lnode, rnode):
76 list = self.transplants.setdefault(rnode, [])
91 list = self.transplants.setdefault(rnode, [])
77 list.append(transplantentry(lnode, rnode))
92 list.append(transplantentry(lnode, rnode))
78 self.dirty = True
93 self.dirty = True
79
94
80 def remove(self, transplant):
95 def remove(self, transplant):
81 list = self.transplants.get(transplant.rnode)
96 list = self.transplants.get(transplant.rnode)
82 if list:
97 if list:
83 del list[list.index(transplant)]
98 del list[list.index(transplant)]
84 self.dirty = True
99 self.dirty = True
85
100
86 class transplanter(object):
101 class transplanter(object):
87 def __init__(self, ui, repo, opts):
102 def __init__(self, ui, repo, opts):
88 self.ui = ui
103 self.ui = ui
89 self.path = repo.join('transplant')
104 self.path = repo.join('transplant')
90 self.opener = scmutil.opener(self.path)
105 self.opener = scmutil.opener(self.path)
91 self.transplants = transplants(self.path, 'transplants',
106 self.transplants = transplants(self.path, 'transplants',
92 opener=self.opener)
107 opener=self.opener)
93 def getcommiteditor():
108 def getcommiteditor():
94 editform = cmdutil.mergeeditform(repo[None], 'transplant')
109 editform = cmdutil.mergeeditform(repo[None], 'transplant')
95 return cmdutil.getcommiteditor(editform=editform, **opts)
110 return cmdutil.getcommiteditor(editform=editform, **opts)
96 self.getcommiteditor = getcommiteditor
111 self.getcommiteditor = getcommiteditor
97
112
98 def applied(self, repo, node, parent):
113 def applied(self, repo, node, parent):
99 '''returns True if a node is already an ancestor of parent
114 '''returns True if a node is already an ancestor of parent
100 or is parent or has already been transplanted'''
115 or is parent or has already been transplanted'''
101 if hasnode(repo, parent):
116 if hasnode(repo, parent):
102 parentrev = repo.changelog.rev(parent)
117 parentrev = repo.changelog.rev(parent)
103 if hasnode(repo, node):
118 if hasnode(repo, node):
104 rev = repo.changelog.rev(node)
119 rev = repo.changelog.rev(node)
105 reachable = repo.changelog.ancestors([parentrev], rev,
120 reachable = repo.changelog.ancestors([parentrev], rev,
106 inclusive=True)
121 inclusive=True)
107 if rev in reachable:
122 if rev in reachable:
108 return True
123 return True
109 for t in self.transplants.get(node):
124 for t in self.transplants.get(node):
110 # it might have been stripped
125 # it might have been stripped
111 if not hasnode(repo, t.lnode):
126 if not hasnode(repo, t.lnode):
112 self.transplants.remove(t)
127 self.transplants.remove(t)
113 return False
128 return False
114 lnoderev = repo.changelog.rev(t.lnode)
129 lnoderev = repo.changelog.rev(t.lnode)
115 if lnoderev in repo.changelog.ancestors([parentrev], lnoderev,
130 if lnoderev in repo.changelog.ancestors([parentrev], lnoderev,
116 inclusive=True):
131 inclusive=True):
117 return True
132 return True
118 return False
133 return False
119
134
120 def apply(self, repo, source, revmap, merges, opts=None):
135 def apply(self, repo, source, revmap, merges, opts=None):
121 '''apply the revisions in revmap one by one in revision order'''
136 '''apply the revisions in revmap one by one in revision order'''
122 if opts is None:
137 if opts is None:
123 opts = {}
138 opts = {}
124 revs = sorted(revmap)
139 revs = sorted(revmap)
125 p1, p2 = repo.dirstate.parents()
140 p1, p2 = repo.dirstate.parents()
126 pulls = []
141 pulls = []
127 diffopts = patch.difffeatureopts(self.ui, opts)
142 diffopts = patch.difffeatureopts(self.ui, opts)
128 diffopts.git = True
143 diffopts.git = True
129
144
130 lock = tr = None
145 lock = tr = None
131 try:
146 try:
132 lock = repo.lock()
147 lock = repo.lock()
133 tr = repo.transaction('transplant')
148 tr = repo.transaction('transplant')
134 for rev in revs:
149 for rev in revs:
135 node = revmap[rev]
150 node = revmap[rev]
136 revstr = '%s:%s' % (rev, nodemod.short(node))
151 revstr = '%s:%s' % (rev, nodemod.short(node))
137
152
138 if self.applied(repo, node, p1):
153 if self.applied(repo, node, p1):
139 self.ui.warn(_('skipping already applied revision %s\n') %
154 self.ui.warn(_('skipping already applied revision %s\n') %
140 revstr)
155 revstr)
141 continue
156 continue
142
157
143 parents = source.changelog.parents(node)
158 parents = source.changelog.parents(node)
144 if not (opts.get('filter') or opts.get('log')):
159 if not (opts.get('filter') or opts.get('log')):
145 # If the changeset parent is the same as the
160 # If the changeset parent is the same as the
146 # wdir's parent, just pull it.
161 # wdir's parent, just pull it.
147 if parents[0] == p1:
162 if parents[0] == p1:
148 pulls.append(node)
163 pulls.append(node)
149 p1 = node
164 p1 = node
150 continue
165 continue
151 if pulls:
166 if pulls:
152 if source != repo:
167 if source != repo:
153 exchange.pull(repo, source.peer(), heads=pulls)
168 exchange.pull(repo, source.peer(), heads=pulls)
154 merge.update(repo, pulls[-1], False, False)
169 merge.update(repo, pulls[-1], False, False)
155 p1, p2 = repo.dirstate.parents()
170 p1, p2 = repo.dirstate.parents()
156 pulls = []
171 pulls = []
157
172
158 domerge = False
173 domerge = False
159 if node in merges:
174 if node in merges:
160 # pulling all the merge revs at once would mean we
175 # pulling all the merge revs at once would mean we
161 # couldn't transplant after the latest even if
176 # couldn't transplant after the latest even if
162 # transplants before them fail.
177 # transplants before them fail.
163 domerge = True
178 domerge = True
164 if not hasnode(repo, node):
179 if not hasnode(repo, node):
165 exchange.pull(repo, source.peer(), heads=[node])
180 exchange.pull(repo, source.peer(), heads=[node])
166
181
167 skipmerge = False
182 skipmerge = False
168 if parents[1] != revlog.nullid:
183 if parents[1] != revlog.nullid:
169 if not opts.get('parent'):
184 if not opts.get('parent'):
170 self.ui.note(_('skipping merge changeset %s:%s\n')
185 self.ui.note(_('skipping merge changeset %s:%s\n')
171 % (rev, nodemod.short(node)))
186 % (rev, nodemod.short(node)))
172 skipmerge = True
187 skipmerge = True
173 else:
188 else:
174 parent = source.lookup(opts['parent'])
189 parent = source.lookup(opts['parent'])
175 if parent not in parents:
190 if parent not in parents:
176 raise error.Abort(_('%s is not a parent of %s') %
191 raise error.Abort(_('%s is not a parent of %s') %
177 (nodemod.short(parent),
192 (nodemod.short(parent),
178 nodemod.short(node)))
193 nodemod.short(node)))
179 else:
194 else:
180 parent = parents[0]
195 parent = parents[0]
181
196
182 if skipmerge:
197 if skipmerge:
183 patchfile = None
198 patchfile = None
184 else:
199 else:
185 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
200 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
186 fp = os.fdopen(fd, 'w')
201 fp = os.fdopen(fd, 'w')
187 gen = patch.diff(source, parent, node, opts=diffopts)
202 gen = patch.diff(source, parent, node, opts=diffopts)
188 for chunk in gen:
203 for chunk in gen:
189 fp.write(chunk)
204 fp.write(chunk)
190 fp.close()
205 fp.close()
191
206
192 del revmap[rev]
207 del revmap[rev]
193 if patchfile or domerge:
208 if patchfile or domerge:
194 try:
209 try:
195 try:
210 try:
196 n = self.applyone(repo, node,
211 n = self.applyone(repo, node,
197 source.changelog.read(node),
212 source.changelog.read(node),
198 patchfile, merge=domerge,
213 patchfile, merge=domerge,
199 log=opts.get('log'),
214 log=opts.get('log'),
200 filter=opts.get('filter'))
215 filter=opts.get('filter'))
201 except TransplantError:
216 except TransplantError:
202 # Do not rollback, it is up to the user to
217 # Do not rollback, it is up to the user to
203 # fix the merge or cancel everything
218 # fix the merge or cancel everything
204 tr.close()
219 tr.close()
205 raise
220 raise
206 if n and domerge:
221 if n and domerge:
207 self.ui.status(_('%s merged at %s\n') % (revstr,
222 self.ui.status(_('%s merged at %s\n') % (revstr,
208 nodemod.short(n)))
223 nodemod.short(n)))
209 elif n:
224 elif n:
210 self.ui.status(_('%s transplanted to %s\n')
225 self.ui.status(_('%s transplanted to %s\n')
211 % (nodemod.short(node),
226 % (nodemod.short(node),
212 nodemod.short(n)))
227 nodemod.short(n)))
213 finally:
228 finally:
214 if patchfile:
229 if patchfile:
215 os.unlink(patchfile)
230 os.unlink(patchfile)
216 tr.close()
231 tr.close()
217 if pulls:
232 if pulls:
218 exchange.pull(repo, source.peer(), heads=pulls)
233 exchange.pull(repo, source.peer(), heads=pulls)
219 merge.update(repo, pulls[-1], False, False)
234 merge.update(repo, pulls[-1], False, False)
220 finally:
235 finally:
221 self.saveseries(revmap, merges)
236 self.saveseries(revmap, merges)
222 self.transplants.write()
237 self.transplants.write()
223 if tr:
238 if tr:
224 tr.release()
239 tr.release()
225 if lock:
240 if lock:
226 lock.release()
241 lock.release()
227
242
228 def filter(self, filter, node, changelog, patchfile):
243 def filter(self, filter, node, changelog, patchfile):
229 '''arbitrarily rewrite changeset before applying it'''
244 '''arbitrarily rewrite changeset before applying it'''
230
245
231 self.ui.status(_('filtering %s\n') % patchfile)
246 self.ui.status(_('filtering %s\n') % patchfile)
232 user, date, msg = (changelog[1], changelog[2], changelog[4])
247 user, date, msg = (changelog[1], changelog[2], changelog[4])
233 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
248 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
234 fp = os.fdopen(fd, 'w')
249 fp = os.fdopen(fd, 'w')
235 fp.write("# HG changeset patch\n")
250 fp.write("# HG changeset patch\n")
236 fp.write("# User %s\n" % user)
251 fp.write("# User %s\n" % user)
237 fp.write("# Date %d %d\n" % date)
252 fp.write("# Date %d %d\n" % date)
238 fp.write(msg + '\n')
253 fp.write(msg + '\n')
239 fp.close()
254 fp.close()
240
255
241 try:
256 try:
242 self.ui.system('%s %s %s' % (filter, util.shellquote(headerfile),
257 self.ui.system('%s %s %s' % (filter, util.shellquote(headerfile),
243 util.shellquote(patchfile)),
258 util.shellquote(patchfile)),
244 environ={'HGUSER': changelog[1],
259 environ={'HGUSER': changelog[1],
245 'HGREVISION': nodemod.hex(node),
260 'HGREVISION': nodemod.hex(node),
246 },
261 },
247 onerr=error.Abort, errprefix=_('filter failed'))
262 onerr=error.Abort, errprefix=_('filter failed'))
248 user, date, msg = self.parselog(file(headerfile))[1:4]
263 user, date, msg = self.parselog(file(headerfile))[1:4]
249 finally:
264 finally:
250 os.unlink(headerfile)
265 os.unlink(headerfile)
251
266
252 return (user, date, msg)
267 return (user, date, msg)
253
268
254 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
269 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
255 filter=None):
270 filter=None):
256 '''apply the patch in patchfile to the repository as a transplant'''
271 '''apply the patch in patchfile to the repository as a transplant'''
257 (manifest, user, (time, timezone), files, message) = cl[:5]
272 (manifest, user, (time, timezone), files, message) = cl[:5]
258 date = "%d %d" % (time, timezone)
273 date = "%d %d" % (time, timezone)
259 extra = {'transplant_source': node}
274 extra = {'transplant_source': node}
260 if filter:
275 if filter:
261 (user, date, message) = self.filter(filter, node, cl, patchfile)
276 (user, date, message) = self.filter(filter, node, cl, patchfile)
262
277
263 if log:
278 if log:
264 # we don't translate messages inserted into commits
279 # we don't translate messages inserted into commits
265 message += '\n(transplanted from %s)' % nodemod.hex(node)
280 message += '\n(transplanted from %s)' % nodemod.hex(node)
266
281
267 self.ui.status(_('applying %s\n') % nodemod.short(node))
282 self.ui.status(_('applying %s\n') % nodemod.short(node))
268 self.ui.note('%s %s\n%s\n' % (user, date, message))
283 self.ui.note('%s %s\n%s\n' % (user, date, message))
269
284
270 if not patchfile and not merge:
285 if not patchfile and not merge:
271 raise error.Abort(_('can only omit patchfile if merging'))
286 raise error.Abort(_('can only omit patchfile if merging'))
272 if patchfile:
287 if patchfile:
273 try:
288 try:
274 files = set()
289 files = set()
275 patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
290 patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
276 files = list(files)
291 files = list(files)
277 except Exception as inst:
292 except Exception as inst:
278 seriespath = os.path.join(self.path, 'series')
293 seriespath = os.path.join(self.path, 'series')
279 if os.path.exists(seriespath):
294 if os.path.exists(seriespath):
280 os.unlink(seriespath)
295 os.unlink(seriespath)
281 p1 = repo.dirstate.p1()
296 p1 = repo.dirstate.p1()
282 p2 = node
297 p2 = node
283 self.log(user, date, message, p1, p2, merge=merge)
298 self.log(user, date, message, p1, p2, merge=merge)
284 self.ui.write(str(inst) + '\n')
299 self.ui.write(str(inst) + '\n')
285 raise TransplantError(_('fix up the working directory and run '
300 raise TransplantError(_('fix up the working directory and run '
286 'hg transplant --continue'))
301 'hg transplant --continue'))
287 else:
302 else:
288 files = None
303 files = None
289 if merge:
304 if merge:
290 p1, p2 = repo.dirstate.parents()
305 p1, p2 = repo.dirstate.parents()
291 repo.setparents(p1, node)
306 repo.setparents(p1, node)
292 m = match.always(repo.root, '')
307 m = match.always(repo.root, '')
293 else:
308 else:
294 m = match.exact(repo.root, '', files)
309 m = match.exact(repo.root, '', files)
295
310
296 n = repo.commit(message, user, date, extra=extra, match=m,
311 n = repo.commit(message, user, date, extra=extra, match=m,
297 editor=self.getcommiteditor())
312 editor=self.getcommiteditor())
298 if not n:
313 if not n:
299 self.ui.warn(_('skipping emptied changeset %s\n') %
314 self.ui.warn(_('skipping emptied changeset %s\n') %
300 nodemod.short(node))
315 nodemod.short(node))
301 return None
316 return None
302 if not merge:
317 if not merge:
303 self.transplants.set(n, node)
318 self.transplants.set(n, node)
304
319
305 return n
320 return n
306
321
307 def canresume(self):
322 def canresume(self):
308 return os.path.exists(os.path.join(self.path, 'journal'))
323 return os.path.exists(os.path.join(self.path, 'journal'))
309
324
310 def resume(self, repo, source, opts):
325 def resume(self, repo, source, opts):
311 '''recover last transaction and apply remaining changesets'''
326 '''recover last transaction and apply remaining changesets'''
312 if os.path.exists(os.path.join(self.path, 'journal')):
327 if os.path.exists(os.path.join(self.path, 'journal')):
313 n, node = self.recover(repo, source, opts)
328 n, node = self.recover(repo, source, opts)
314 if n:
329 if n:
315 self.ui.status(_('%s transplanted as %s\n') %
330 self.ui.status(_('%s transplanted as %s\n') %
316 (nodemod.short(node),
331 (nodemod.short(node),
317 nodemod.short(n)))
332 nodemod.short(n)))
318 else:
333 else:
319 self.ui.status(_('%s skipped due to empty diff\n')
334 self.ui.status(_('%s skipped due to empty diff\n')
320 % (nodemod.short(node),))
335 % (nodemod.short(node),))
321 seriespath = os.path.join(self.path, 'series')
336 seriespath = os.path.join(self.path, 'series')
322 if not os.path.exists(seriespath):
337 if not os.path.exists(seriespath):
323 self.transplants.write()
338 self.transplants.write()
324 return
339 return
325 nodes, merges = self.readseries()
340 nodes, merges = self.readseries()
326 revmap = {}
341 revmap = {}
327 for n in nodes:
342 for n in nodes:
328 revmap[source.changelog.rev(n)] = n
343 revmap[source.changelog.rev(n)] = n
329 os.unlink(seriespath)
344 os.unlink(seriespath)
330
345
331 self.apply(repo, source, revmap, merges, opts)
346 self.apply(repo, source, revmap, merges, opts)
332
347
333 def recover(self, repo, source, opts):
348 def recover(self, repo, source, opts):
334 '''commit working directory using journal metadata'''
349 '''commit working directory using journal metadata'''
335 node, user, date, message, parents = self.readlog()
350 node, user, date, message, parents = self.readlog()
336 merge = False
351 merge = False
337
352
338 if not user or not date or not message or not parents[0]:
353 if not user or not date or not message or not parents[0]:
339 raise error.Abort(_('transplant log file is corrupt'))
354 raise error.Abort(_('transplant log file is corrupt'))
340
355
341 parent = parents[0]
356 parent = parents[0]
342 if len(parents) > 1:
357 if len(parents) > 1:
343 if opts.get('parent'):
358 if opts.get('parent'):
344 parent = source.lookup(opts['parent'])
359 parent = source.lookup(opts['parent'])
345 if parent not in parents:
360 if parent not in parents:
346 raise error.Abort(_('%s is not a parent of %s') %
361 raise error.Abort(_('%s is not a parent of %s') %
347 (nodemod.short(parent),
362 (nodemod.short(parent),
348 nodemod.short(node)))
363 nodemod.short(node)))
349 else:
364 else:
350 merge = True
365 merge = True
351
366
352 extra = {'transplant_source': node}
367 extra = {'transplant_source': node}
353 try:
368 try:
354 p1, p2 = repo.dirstate.parents()
369 p1, p2 = repo.dirstate.parents()
355 if p1 != parent:
370 if p1 != parent:
356 raise error.Abort(_('working directory not at transplant '
371 raise error.Abort(_('working directory not at transplant '
357 'parent %s') % nodemod.hex(parent))
372 'parent %s') % nodemod.hex(parent))
358 if merge:
373 if merge:
359 repo.setparents(p1, parents[1])
374 repo.setparents(p1, parents[1])
360 modified, added, removed, deleted = repo.status()[:4]
375 modified, added, removed, deleted = repo.status()[:4]
361 if merge or modified or added or removed or deleted:
376 if merge or modified or added or removed or deleted:
362 n = repo.commit(message, user, date, extra=extra,
377 n = repo.commit(message, user, date, extra=extra,
363 editor=self.getcommiteditor())
378 editor=self.getcommiteditor())
364 if not n:
379 if not n:
365 raise error.Abort(_('commit failed'))
380 raise error.Abort(_('commit failed'))
366 if not merge:
381 if not merge:
367 self.transplants.set(n, node)
382 self.transplants.set(n, node)
368 else:
383 else:
369 n = None
384 n = None
370 self.unlog()
385 self.unlog()
371
386
372 return n, node
387 return n, node
373 finally:
388 finally:
374 # TODO: get rid of this meaningless try/finally enclosing.
389 # TODO: get rid of this meaningless try/finally enclosing.
375 # this is kept only to reduce changes in a patch.
390 # this is kept only to reduce changes in a patch.
376 pass
391 pass
377
392
378 def readseries(self):
393 def readseries(self):
379 nodes = []
394 nodes = []
380 merges = []
395 merges = []
381 cur = nodes
396 cur = nodes
382 for line in self.opener.read('series').splitlines():
397 for line in self.opener.read('series').splitlines():
383 if line.startswith('# Merges'):
398 if line.startswith('# Merges'):
384 cur = merges
399 cur = merges
385 continue
400 continue
386 cur.append(revlog.bin(line))
401 cur.append(revlog.bin(line))
387
402
388 return (nodes, merges)
403 return (nodes, merges)
389
404
390 def saveseries(self, revmap, merges):
405 def saveseries(self, revmap, merges):
391 if not revmap:
406 if not revmap:
392 return
407 return
393
408
394 if not os.path.isdir(self.path):
409 if not os.path.isdir(self.path):
395 os.mkdir(self.path)
410 os.mkdir(self.path)
396 series = self.opener('series', 'w')
411 series = self.opener('series', 'w')
397 for rev in sorted(revmap):
412 for rev in sorted(revmap):
398 series.write(nodemod.hex(revmap[rev]) + '\n')
413 series.write(nodemod.hex(revmap[rev]) + '\n')
399 if merges:
414 if merges:
400 series.write('# Merges\n')
415 series.write('# Merges\n')
401 for m in merges:
416 for m in merges:
402 series.write(nodemod.hex(m) + '\n')
417 series.write(nodemod.hex(m) + '\n')
403 series.close()
418 series.close()
404
419
405 def parselog(self, fp):
420 def parselog(self, fp):
406 parents = []
421 parents = []
407 message = []
422 message = []
408 node = revlog.nullid
423 node = revlog.nullid
409 inmsg = False
424 inmsg = False
410 user = None
425 user = None
411 date = None
426 date = None
412 for line in fp.read().splitlines():
427 for line in fp.read().splitlines():
413 if inmsg:
428 if inmsg:
414 message.append(line)
429 message.append(line)
415 elif line.startswith('# User '):
430 elif line.startswith('# User '):
416 user = line[7:]
431 user = line[7:]
417 elif line.startswith('# Date '):
432 elif line.startswith('# Date '):
418 date = line[7:]
433 date = line[7:]
419 elif line.startswith('# Node ID '):
434 elif line.startswith('# Node ID '):
420 node = revlog.bin(line[10:])
435 node = revlog.bin(line[10:])
421 elif line.startswith('# Parent '):
436 elif line.startswith('# Parent '):
422 parents.append(revlog.bin(line[9:]))
437 parents.append(revlog.bin(line[9:]))
423 elif not line.startswith('# '):
438 elif not line.startswith('# '):
424 inmsg = True
439 inmsg = True
425 message.append(line)
440 message.append(line)
426 if None in (user, date):
441 if None in (user, date):
427 raise error.Abort(_("filter corrupted changeset (no user or date)"))
442 raise error.Abort(_("filter corrupted changeset (no user or date)"))
428 return (node, user, date, '\n'.join(message), parents)
443 return (node, user, date, '\n'.join(message), parents)
429
444
430 def log(self, user, date, message, p1, p2, merge=False):
445 def log(self, user, date, message, p1, p2, merge=False):
431 '''journal changelog metadata for later recover'''
446 '''journal changelog metadata for later recover'''
432
447
433 if not os.path.isdir(self.path):
448 if not os.path.isdir(self.path):
434 os.mkdir(self.path)
449 os.mkdir(self.path)
435 fp = self.opener('journal', 'w')
450 fp = self.opener('journal', 'w')
436 fp.write('# User %s\n' % user)
451 fp.write('# User %s\n' % user)
437 fp.write('# Date %s\n' % date)
452 fp.write('# Date %s\n' % date)
438 fp.write('# Node ID %s\n' % nodemod.hex(p2))
453 fp.write('# Node ID %s\n' % nodemod.hex(p2))
439 fp.write('# Parent ' + nodemod.hex(p1) + '\n')
454 fp.write('# Parent ' + nodemod.hex(p1) + '\n')
440 if merge:
455 if merge:
441 fp.write('# Parent ' + nodemod.hex(p2) + '\n')
456 fp.write('# Parent ' + nodemod.hex(p2) + '\n')
442 fp.write(message.rstrip() + '\n')
457 fp.write(message.rstrip() + '\n')
443 fp.close()
458 fp.close()
444
459
445 def readlog(self):
460 def readlog(self):
446 return self.parselog(self.opener('journal'))
461 return self.parselog(self.opener('journal'))
447
462
448 def unlog(self):
463 def unlog(self):
449 '''remove changelog journal'''
464 '''remove changelog journal'''
450 absdst = os.path.join(self.path, 'journal')
465 absdst = os.path.join(self.path, 'journal')
451 if os.path.exists(absdst):
466 if os.path.exists(absdst):
452 os.unlink(absdst)
467 os.unlink(absdst)
453
468
454 def transplantfilter(self, repo, source, root):
469 def transplantfilter(self, repo, source, root):
455 def matchfn(node):
470 def matchfn(node):
456 if self.applied(repo, node, root):
471 if self.applied(repo, node, root):
457 return False
472 return False
458 if source.changelog.parents(node)[1] != revlog.nullid:
473 if source.changelog.parents(node)[1] != revlog.nullid:
459 return False
474 return False
460 extra = source.changelog.read(node)[5]
475 extra = source.changelog.read(node)[5]
461 cnode = extra.get('transplant_source')
476 cnode = extra.get('transplant_source')
462 if cnode and self.applied(repo, cnode, root):
477 if cnode and self.applied(repo, cnode, root):
463 return False
478 return False
464 return True
479 return True
465
480
466 return matchfn
481 return matchfn
467
482
468 def hasnode(repo, node):
483 def hasnode(repo, node):
469 try:
484 try:
470 return repo.changelog.rev(node) is not None
485 return repo.changelog.rev(node) is not None
471 except error.RevlogError:
486 except error.RevlogError:
472 return False
487 return False
473
488
474 def browserevs(ui, repo, nodes, opts):
489 def browserevs(ui, repo, nodes, opts):
475 '''interactively transplant changesets'''
490 '''interactively transplant changesets'''
476 displayer = cmdutil.show_changeset(ui, repo, opts)
491 displayer = cmdutil.show_changeset(ui, repo, opts)
477 transplants = []
492 transplants = []
478 merges = []
493 merges = []
479 prompt = _('apply changeset? [ynmpcq?]:'
494 prompt = _('apply changeset? [ynmpcq?]:'
480 '$$ &yes, transplant this changeset'
495 '$$ &yes, transplant this changeset'
481 '$$ &no, skip this changeset'
496 '$$ &no, skip this changeset'
482 '$$ &merge at this changeset'
497 '$$ &merge at this changeset'
483 '$$ show &patch'
498 '$$ show &patch'
484 '$$ &commit selected changesets'
499 '$$ &commit selected changesets'
485 '$$ &quit and cancel transplant'
500 '$$ &quit and cancel transplant'
486 '$$ &? (show this help)')
501 '$$ &? (show this help)')
487 for node in nodes:
502 for node in nodes:
488 displayer.show(repo[node])
503 displayer.show(repo[node])
489 action = None
504 action = None
490 while not action:
505 while not action:
491 action = 'ynmpcq?'[ui.promptchoice(prompt)]
506 action = 'ynmpcq?'[ui.promptchoice(prompt)]
492 if action == '?':
507 if action == '?':
493 for c, t in ui.extractchoices(prompt)[1]:
508 for c, t in ui.extractchoices(prompt)[1]:
494 ui.write('%s: %s\n' % (c, t))
509 ui.write('%s: %s\n' % (c, t))
495 action = None
510 action = None
496 elif action == 'p':
511 elif action == 'p':
497 parent = repo.changelog.parents(node)[0]
512 parent = repo.changelog.parents(node)[0]
498 for chunk in patch.diff(repo, parent, node):
513 for chunk in patch.diff(repo, parent, node):
499 ui.write(chunk)
514 ui.write(chunk)
500 action = None
515 action = None
501 if action == 'y':
516 if action == 'y':
502 transplants.append(node)
517 transplants.append(node)
503 elif action == 'm':
518 elif action == 'm':
504 merges.append(node)
519 merges.append(node)
505 elif action == 'c':
520 elif action == 'c':
506 break
521 break
507 elif action == 'q':
522 elif action == 'q':
508 transplants = ()
523 transplants = ()
509 merges = ()
524 merges = ()
510 break
525 break
511 displayer.close()
526 displayer.close()
512 return (transplants, merges)
527 return (transplants, merges)
513
528
514 @command('transplant',
529 @command('transplant',
515 [('s', 'source', '', _('transplant changesets from REPO'), _('REPO')),
530 [('s', 'source', '', _('transplant changesets from REPO'), _('REPO')),
516 ('b', 'branch', [], _('use this source changeset as head'), _('REV')),
531 ('b', 'branch', [], _('use this source changeset as head'), _('REV')),
517 ('a', 'all', None, _('pull all changesets up to the --branch revisions')),
532 ('a', 'all', None, _('pull all changesets up to the --branch revisions')),
518 ('p', 'prune', [], _('skip over REV'), _('REV')),
533 ('p', 'prune', [], _('skip over REV'), _('REV')),
519 ('m', 'merge', [], _('merge at REV'), _('REV')),
534 ('m', 'merge', [], _('merge at REV'), _('REV')),
520 ('', 'parent', '',
535 ('', 'parent', '',
521 _('parent to choose when transplanting merge'), _('REV')),
536 _('parent to choose when transplanting merge'), _('REV')),
522 ('e', 'edit', False, _('invoke editor on commit messages')),
537 ('e', 'edit', False, _('invoke editor on commit messages')),
523 ('', 'log', None, _('append transplant info to log message')),
538 ('', 'log', None, _('append transplant info to log message')),
524 ('c', 'continue', None, _('continue last transplant session '
539 ('c', 'continue', None, _('continue last transplant session '
525 'after fixing conflicts')),
540 'after fixing conflicts')),
526 ('', 'filter', '',
541 ('', 'filter', '',
527 _('filter changesets through command'), _('CMD'))],
542 _('filter changesets through command'), _('CMD'))],
528 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
543 _('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
529 '[-m REV] [REV]...'))
544 '[-m REV] [REV]...'))
530 def transplant(ui, repo, *revs, **opts):
545 def transplant(ui, repo, *revs, **opts):
531 '''transplant changesets from another branch
546 '''transplant changesets from another branch
532
547
533 Selected changesets will be applied on top of the current working
548 Selected changesets will be applied on top of the current working
534 directory with the log of the original changeset. The changesets
549 directory with the log of the original changeset. The changesets
535 are copied and will thus appear twice in the history with different
550 are copied and will thus appear twice in the history with different
536 identities.
551 identities.
537
552
538 Consider using the graft command if everything is inside the same
553 Consider using the graft command if everything is inside the same
539 repository - it will use merges and will usually give a better result.
554 repository - it will use merges and will usually give a better result.
540 Use the rebase extension if the changesets are unpublished and you want
555 Use the rebase extension if the changesets are unpublished and you want
541 to move them instead of copying them.
556 to move them instead of copying them.
542
557
543 If --log is specified, log messages will have a comment appended
558 If --log is specified, log messages will have a comment appended
544 of the form::
559 of the form::
545
560
546 (transplanted from CHANGESETHASH)
561 (transplanted from CHANGESETHASH)
547
562
548 You can rewrite the changelog message with the --filter option.
563 You can rewrite the changelog message with the --filter option.
549 Its argument will be invoked with the current changelog message as
564 Its argument will be invoked with the current changelog message as
550 $1 and the patch as $2.
565 $1 and the patch as $2.
551
566
552 --source/-s specifies another repository to use for selecting changesets,
567 --source/-s specifies another repository to use for selecting changesets,
553 just as if it temporarily had been pulled.
568 just as if it temporarily had been pulled.
554 If --branch/-b is specified, these revisions will be used as
569 If --branch/-b is specified, these revisions will be used as
555 heads when deciding which changesets to transplant, just as if only
570 heads when deciding which changesets to transplant, just as if only
556 these revisions had been pulled.
571 these revisions had been pulled.
557 If --all/-a is specified, all the revisions up to the heads specified
572 If --all/-a is specified, all the revisions up to the heads specified
558 with --branch will be transplanted.
573 with --branch will be transplanted.
559
574
560 Example:
575 Example:
561
576
562 - transplant all changes up to REV on top of your current revision::
577 - transplant all changes up to REV on top of your current revision::
563
578
564 hg transplant --branch REV --all
579 hg transplant --branch REV --all
565
580
566 You can optionally mark selected transplanted changesets as merge
581 You can optionally mark selected transplanted changesets as merge
567 changesets. You will not be prompted to transplant any ancestors
582 changesets. You will not be prompted to transplant any ancestors
568 of a merged transplant, and you can merge descendants of them
583 of a merged transplant, and you can merge descendants of them
569 normally instead of transplanting them.
584 normally instead of transplanting them.
570
585
571 Merge changesets may be transplanted directly by specifying the
586 Merge changesets may be transplanted directly by specifying the
572 proper parent changeset by calling :hg:`transplant --parent`.
587 proper parent changeset by calling :hg:`transplant --parent`.
573
588
574 If no merges or revisions are provided, :hg:`transplant` will
589 If no merges or revisions are provided, :hg:`transplant` will
575 start an interactive changeset browser.
590 start an interactive changeset browser.
576
591
577 If a changeset application fails, you can fix the merge by hand
592 If a changeset application fails, you can fix the merge by hand
578 and then resume where you left off by calling :hg:`transplant
593 and then resume where you left off by calling :hg:`transplant
579 --continue/-c`.
594 --continue/-c`.
580 '''
595 '''
581 with repo.wlock():
596 with repo.wlock():
582 return _dotransplant(ui, repo, *revs, **opts)
597 return _dotransplant(ui, repo, *revs, **opts)
583
598
584 def _dotransplant(ui, repo, *revs, **opts):
599 def _dotransplant(ui, repo, *revs, **opts):
585 def incwalk(repo, csets, match=util.always):
600 def incwalk(repo, csets, match=util.always):
586 for node in csets:
601 for node in csets:
587 if match(node):
602 if match(node):
588 yield node
603 yield node
589
604
590 def transplantwalk(repo, dest, heads, match=util.always):
605 def transplantwalk(repo, dest, heads, match=util.always):
591 '''Yield all nodes that are ancestors of a head but not ancestors
606 '''Yield all nodes that are ancestors of a head but not ancestors
592 of dest.
607 of dest.
593 If no heads are specified, the heads of repo will be used.'''
608 If no heads are specified, the heads of repo will be used.'''
594 if not heads:
609 if not heads:
595 heads = repo.heads()
610 heads = repo.heads()
596 ancestors = []
611 ancestors = []
597 ctx = repo[dest]
612 ctx = repo[dest]
598 for head in heads:
613 for head in heads:
599 ancestors.append(ctx.ancestor(repo[head]).node())
614 ancestors.append(ctx.ancestor(repo[head]).node())
600 for node in repo.changelog.nodesbetween(ancestors, heads)[0]:
615 for node in repo.changelog.nodesbetween(ancestors, heads)[0]:
601 if match(node):
616 if match(node):
602 yield node
617 yield node
603
618
604 def checkopts(opts, revs):
619 def checkopts(opts, revs):
605 if opts.get('continue'):
620 if opts.get('continue'):
606 if opts.get('branch') or opts.get('all') or opts.get('merge'):
621 if opts.get('branch') or opts.get('all') or opts.get('merge'):
607 raise error.Abort(_('--continue is incompatible with '
622 raise error.Abort(_('--continue is incompatible with '
608 '--branch, --all and --merge'))
623 '--branch, --all and --merge'))
609 return
624 return
610 if not (opts.get('source') or revs or
625 if not (opts.get('source') or revs or
611 opts.get('merge') or opts.get('branch')):
626 opts.get('merge') or opts.get('branch')):
612 raise error.Abort(_('no source URL, branch revision, or revision '
627 raise error.Abort(_('no source URL, branch revision, or revision '
613 'list provided'))
628 'list provided'))
614 if opts.get('all'):
629 if opts.get('all'):
615 if not opts.get('branch'):
630 if not opts.get('branch'):
616 raise error.Abort(_('--all requires a branch revision'))
631 raise error.Abort(_('--all requires a branch revision'))
617 if revs:
632 if revs:
618 raise error.Abort(_('--all is incompatible with a '
633 raise error.Abort(_('--all is incompatible with a '
619 'revision list'))
634 'revision list'))
620
635
621 checkopts(opts, revs)
636 checkopts(opts, revs)
622
637
623 if not opts.get('log'):
638 if not opts.get('log'):
624 # deprecated config: transplant.log
639 # deprecated config: transplant.log
625 opts['log'] = ui.config('transplant', 'log')
640 opts['log'] = ui.config('transplant', 'log')
626 if not opts.get('filter'):
641 if not opts.get('filter'):
627 # deprecated config: transplant.filter
642 # deprecated config: transplant.filter
628 opts['filter'] = ui.config('transplant', 'filter')
643 opts['filter'] = ui.config('transplant', 'filter')
629
644
630 tp = transplanter(ui, repo, opts)
645 tp = transplanter(ui, repo, opts)
631
646
632 p1, p2 = repo.dirstate.parents()
647 p1, p2 = repo.dirstate.parents()
633 if len(repo) > 0 and p1 == revlog.nullid:
648 if len(repo) > 0 and p1 == revlog.nullid:
634 raise error.Abort(_('no revision checked out'))
649 raise error.Abort(_('no revision checked out'))
635 if opts.get('continue'):
650 if opts.get('continue'):
636 if not tp.canresume():
651 if not tp.canresume():
637 raise error.Abort(_('no transplant to continue'))
652 raise error.Abort(_('no transplant to continue'))
638 else:
653 else:
639 cmdutil.checkunfinished(repo)
654 cmdutil.checkunfinished(repo)
640 if p2 != revlog.nullid:
655 if p2 != revlog.nullid:
641 raise error.Abort(_('outstanding uncommitted merges'))
656 raise error.Abort(_('outstanding uncommitted merges'))
642 m, a, r, d = repo.status()[:4]
657 m, a, r, d = repo.status()[:4]
643 if m or a or r or d:
658 if m or a or r or d:
644 raise error.Abort(_('outstanding local changes'))
659 raise error.Abort(_('outstanding local changes'))
645
660
646 sourcerepo = opts.get('source')
661 sourcerepo = opts.get('source')
647 if sourcerepo:
662 if sourcerepo:
648 peer = hg.peer(repo, opts, ui.expandpath(sourcerepo))
663 peer = hg.peer(repo, opts, ui.expandpath(sourcerepo))
649 heads = map(peer.lookup, opts.get('branch', ()))
664 heads = map(peer.lookup, opts.get('branch', ()))
650 target = set(heads)
665 target = set(heads)
651 for r in revs:
666 for r in revs:
652 try:
667 try:
653 target.add(peer.lookup(r))
668 target.add(peer.lookup(r))
654 except error.RepoError:
669 except error.RepoError:
655 pass
670 pass
656 source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer,
671 source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer,
657 onlyheads=sorted(target), force=True)
672 onlyheads=sorted(target), force=True)
658 else:
673 else:
659 source = repo
674 source = repo
660 heads = map(source.lookup, opts.get('branch', ()))
675 heads = map(source.lookup, opts.get('branch', ()))
661 cleanupfn = None
676 cleanupfn = None
662
677
663 try:
678 try:
664 if opts.get('continue'):
679 if opts.get('continue'):
665 tp.resume(repo, source, opts)
680 tp.resume(repo, source, opts)
666 return
681 return
667
682
668 tf = tp.transplantfilter(repo, source, p1)
683 tf = tp.transplantfilter(repo, source, p1)
669 if opts.get('prune'):
684 if opts.get('prune'):
670 prune = set(source.lookup(r)
685 prune = set(source.lookup(r)
671 for r in scmutil.revrange(source, opts.get('prune')))
686 for r in scmutil.revrange(source, opts.get('prune')))
672 matchfn = lambda x: tf(x) and x not in prune
687 matchfn = lambda x: tf(x) and x not in prune
673 else:
688 else:
674 matchfn = tf
689 matchfn = tf
675 merges = map(source.lookup, opts.get('merge', ()))
690 merges = map(source.lookup, opts.get('merge', ()))
676 revmap = {}
691 revmap = {}
677 if revs:
692 if revs:
678 for r in scmutil.revrange(source, revs):
693 for r in scmutil.revrange(source, revs):
679 revmap[int(r)] = source.lookup(r)
694 revmap[int(r)] = source.lookup(r)
680 elif opts.get('all') or not merges:
695 elif opts.get('all') or not merges:
681 if source != repo:
696 if source != repo:
682 alltransplants = incwalk(source, csets, match=matchfn)
697 alltransplants = incwalk(source, csets, match=matchfn)
683 else:
698 else:
684 alltransplants = transplantwalk(source, p1, heads,
699 alltransplants = transplantwalk(source, p1, heads,
685 match=matchfn)
700 match=matchfn)
686 if opts.get('all'):
701 if opts.get('all'):
687 revs = alltransplants
702 revs = alltransplants
688 else:
703 else:
689 revs, newmerges = browserevs(ui, source, alltransplants, opts)
704 revs, newmerges = browserevs(ui, source, alltransplants, opts)
690 merges.extend(newmerges)
705 merges.extend(newmerges)
691 for r in revs:
706 for r in revs:
692 revmap[source.changelog.rev(r)] = r
707 revmap[source.changelog.rev(r)] = r
693 for r in merges:
708 for r in merges:
694 revmap[source.changelog.rev(r)] = r
709 revmap[source.changelog.rev(r)] = r
695
710
696 tp.apply(repo, source, revmap, merges, opts)
711 tp.apply(repo, source, revmap, merges, opts)
697 finally:
712 finally:
698 if cleanupfn:
713 if cleanupfn:
699 cleanupfn()
714 cleanupfn()
700
715
701 revsetpredicate = registrar.revsetpredicate()
716 revsetpredicate = registrar.revsetpredicate()
702
717
703 @revsetpredicate('transplanted([set])')
718 @revsetpredicate('transplanted([set])')
704 def revsettransplanted(repo, subset, x):
719 def revsettransplanted(repo, subset, x):
705 """Transplanted changesets in set, or all transplanted changesets.
720 """Transplanted changesets in set, or all transplanted changesets.
706 """
721 """
707 if x:
722 if x:
708 s = revset.getset(repo, subset, x)
723 s = revset.getset(repo, subset, x)
709 else:
724 else:
710 s = subset
725 s = subset
711 return revset.baseset([r for r in s if
726 return revset.baseset([r for r in s if
712 repo[r].extra().get('transplant_source')])
727 repo[r].extra().get('transplant_source')])
713
728
714 def kwtransplanted(repo, ctx, **args):
729 def kwtransplanted(repo, ctx, **args):
715 """:transplanted: String. The node identifier of the transplanted
730 """:transplanted: String. The node identifier of the transplanted
716 changeset if any."""
731 changeset if any."""
717 n = ctx.extra().get('transplant_source')
732 n = ctx.extra().get('transplant_source')
718 return n and nodemod.hex(n) or ''
733 return n and nodemod.hex(n) or ''
719
734
720 def extsetup(ui):
735 def extsetup(ui):
721 templatekw.keywords['transplanted'] = kwtransplanted
736 templatekw.keywords['transplanted'] = kwtransplanted
722 cmdutil.unfinishedstates.append(
737 cmdutil.unfinishedstates.append(
723 ['transplant/journal', True, False, _('transplant in progress'),
738 ['transplant/journal', True, False, _('transplant in progress'),
724 _("use 'hg transplant --continue' or 'hg update' to abort")])
739 _("use 'hg transplant --continue' or 'hg update' to abort")])
725
740
726 # tell hggettext to extract docstrings from these functions:
741 # tell hggettext to extract docstrings from these functions:
727 i18nfunctions = [revsettransplanted, kwtransplanted]
742 i18nfunctions = [revsettransplanted, kwtransplanted]
@@ -1,131 +1,130 b''
1 #require test-repo
1 #require test-repo
2
2
3 $ cd "$TESTDIR"/..
3 $ cd "$TESTDIR"/..
4
4
5 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
5 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
6 contrib/check-code.py not using absolute_import
6 contrib/check-code.py not using absolute_import
7 contrib/check-code.py requires print_function
7 contrib/check-code.py requires print_function
8 contrib/import-checker.py not using absolute_import
8 contrib/import-checker.py not using absolute_import
9 contrib/import-checker.py requires print_function
9 contrib/import-checker.py requires print_function
10 contrib/memory.py not using absolute_import
10 contrib/memory.py not using absolute_import
11 contrib/perf.py not using absolute_import
11 contrib/perf.py not using absolute_import
12 contrib/python-hook-examples.py not using absolute_import
12 contrib/python-hook-examples.py not using absolute_import
13 contrib/revsetbenchmarks.py not using absolute_import
13 contrib/revsetbenchmarks.py not using absolute_import
14 contrib/revsetbenchmarks.py requires print_function
14 contrib/revsetbenchmarks.py requires print_function
15 contrib/showstack.py not using absolute_import
15 contrib/showstack.py not using absolute_import
16 contrib/synthrepo.py not using absolute_import
16 contrib/synthrepo.py not using absolute_import
17 contrib/win32/hgwebdir_wsgi.py not using absolute_import
17 contrib/win32/hgwebdir_wsgi.py not using absolute_import
18 doc/check-seclevel.py not using absolute_import
18 doc/check-seclevel.py not using absolute_import
19 doc/gendoc.py not using absolute_import
19 doc/gendoc.py not using absolute_import
20 doc/hgmanpage.py not using absolute_import
20 doc/hgmanpage.py not using absolute_import
21 hgext/color.py not using absolute_import
21 hgext/color.py not using absolute_import
22 hgext/eol.py not using absolute_import
22 hgext/eol.py not using absolute_import
23 hgext/extdiff.py not using absolute_import
23 hgext/extdiff.py not using absolute_import
24 hgext/factotum.py not using absolute_import
24 hgext/factotum.py not using absolute_import
25 hgext/fetch.py not using absolute_import
25 hgext/fetch.py not using absolute_import
26 hgext/fsmonitor/pywatchman/__init__.py not using absolute_import
26 hgext/fsmonitor/pywatchman/__init__.py not using absolute_import
27 hgext/fsmonitor/pywatchman/__init__.py requires print_function
27 hgext/fsmonitor/pywatchman/__init__.py requires print_function
28 hgext/fsmonitor/pywatchman/capabilities.py not using absolute_import
28 hgext/fsmonitor/pywatchman/capabilities.py not using absolute_import
29 hgext/fsmonitor/pywatchman/pybser.py not using absolute_import
29 hgext/fsmonitor/pywatchman/pybser.py not using absolute_import
30 hgext/gpg.py not using absolute_import
30 hgext/gpg.py not using absolute_import
31 hgext/graphlog.py not using absolute_import
31 hgext/graphlog.py not using absolute_import
32 hgext/hgcia.py not using absolute_import
32 hgext/hgcia.py not using absolute_import
33 hgext/hgk.py not using absolute_import
33 hgext/hgk.py not using absolute_import
34 hgext/highlight/__init__.py not using absolute_import
34 hgext/highlight/__init__.py not using absolute_import
35 hgext/highlight/highlight.py not using absolute_import
35 hgext/highlight/highlight.py not using absolute_import
36 hgext/histedit.py not using absolute_import
36 hgext/histedit.py not using absolute_import
37 hgext/largefiles/__init__.py not using absolute_import
37 hgext/largefiles/__init__.py not using absolute_import
38 hgext/largefiles/basestore.py not using absolute_import
38 hgext/largefiles/basestore.py not using absolute_import
39 hgext/largefiles/lfcommands.py not using absolute_import
39 hgext/largefiles/lfcommands.py not using absolute_import
40 hgext/largefiles/lfutil.py not using absolute_import
40 hgext/largefiles/lfutil.py not using absolute_import
41 hgext/largefiles/localstore.py not using absolute_import
41 hgext/largefiles/localstore.py not using absolute_import
42 hgext/largefiles/overrides.py not using absolute_import
42 hgext/largefiles/overrides.py not using absolute_import
43 hgext/largefiles/proto.py not using absolute_import
43 hgext/largefiles/proto.py not using absolute_import
44 hgext/largefiles/remotestore.py not using absolute_import
44 hgext/largefiles/remotestore.py not using absolute_import
45 hgext/largefiles/reposetup.py not using absolute_import
45 hgext/largefiles/reposetup.py not using absolute_import
46 hgext/largefiles/uisetup.py not using absolute_import
46 hgext/largefiles/uisetup.py not using absolute_import
47 hgext/largefiles/wirestore.py not using absolute_import
47 hgext/largefiles/wirestore.py not using absolute_import
48 hgext/mq.py not using absolute_import
48 hgext/mq.py not using absolute_import
49 hgext/rebase.py not using absolute_import
49 hgext/rebase.py not using absolute_import
50 hgext/share.py not using absolute_import
50 hgext/share.py not using absolute_import
51 hgext/transplant.py not using absolute_import
52 hgext/win32text.py not using absolute_import
51 hgext/win32text.py not using absolute_import
53 i18n/check-translation.py not using absolute_import
52 i18n/check-translation.py not using absolute_import
54 i18n/polib.py not using absolute_import
53 i18n/polib.py not using absolute_import
55 setup.py not using absolute_import
54 setup.py not using absolute_import
56 tests/filterpyflakes.py requires print_function
55 tests/filterpyflakes.py requires print_function
57 tests/generate-working-copy-states.py requires print_function
56 tests/generate-working-copy-states.py requires print_function
58 tests/get-with-headers.py requires print_function
57 tests/get-with-headers.py requires print_function
59 tests/heredoctest.py requires print_function
58 tests/heredoctest.py requires print_function
60 tests/hypothesishelpers.py not using absolute_import
59 tests/hypothesishelpers.py not using absolute_import
61 tests/hypothesishelpers.py requires print_function
60 tests/hypothesishelpers.py requires print_function
62 tests/killdaemons.py not using absolute_import
61 tests/killdaemons.py not using absolute_import
63 tests/md5sum.py not using absolute_import
62 tests/md5sum.py not using absolute_import
64 tests/mockblackbox.py not using absolute_import
63 tests/mockblackbox.py not using absolute_import
65 tests/printenv.py not using absolute_import
64 tests/printenv.py not using absolute_import
66 tests/readlink.py not using absolute_import
65 tests/readlink.py not using absolute_import
67 tests/readlink.py requires print_function
66 tests/readlink.py requires print_function
68 tests/revlog-formatv0.py not using absolute_import
67 tests/revlog-formatv0.py not using absolute_import
69 tests/run-tests.py not using absolute_import
68 tests/run-tests.py not using absolute_import
70 tests/seq.py not using absolute_import
69 tests/seq.py not using absolute_import
71 tests/seq.py requires print_function
70 tests/seq.py requires print_function
72 tests/silenttestrunner.py not using absolute_import
71 tests/silenttestrunner.py not using absolute_import
73 tests/silenttestrunner.py requires print_function
72 tests/silenttestrunner.py requires print_function
74 tests/sitecustomize.py not using absolute_import
73 tests/sitecustomize.py not using absolute_import
75 tests/svn-safe-append.py not using absolute_import
74 tests/svn-safe-append.py not using absolute_import
76 tests/svnxml.py not using absolute_import
75 tests/svnxml.py not using absolute_import
77 tests/test-ancestor.py requires print_function
76 tests/test-ancestor.py requires print_function
78 tests/test-atomictempfile.py not using absolute_import
77 tests/test-atomictempfile.py not using absolute_import
79 tests/test-batching.py not using absolute_import
78 tests/test-batching.py not using absolute_import
80 tests/test-batching.py requires print_function
79 tests/test-batching.py requires print_function
81 tests/test-bdiff.py not using absolute_import
80 tests/test-bdiff.py not using absolute_import
82 tests/test-bdiff.py requires print_function
81 tests/test-bdiff.py requires print_function
83 tests/test-context.py not using absolute_import
82 tests/test-context.py not using absolute_import
84 tests/test-context.py requires print_function
83 tests/test-context.py requires print_function
85 tests/test-demandimport.py not using absolute_import
84 tests/test-demandimport.py not using absolute_import
86 tests/test-demandimport.py requires print_function
85 tests/test-demandimport.py requires print_function
87 tests/test-doctest.py not using absolute_import
86 tests/test-doctest.py not using absolute_import
88 tests/test-duplicateoptions.py not using absolute_import
87 tests/test-duplicateoptions.py not using absolute_import
89 tests/test-duplicateoptions.py requires print_function
88 tests/test-duplicateoptions.py requires print_function
90 tests/test-filecache.py not using absolute_import
89 tests/test-filecache.py not using absolute_import
91 tests/test-filecache.py requires print_function
90 tests/test-filecache.py requires print_function
92 tests/test-filelog.py not using absolute_import
91 tests/test-filelog.py not using absolute_import
93 tests/test-filelog.py requires print_function
92 tests/test-filelog.py requires print_function
94 tests/test-hg-parseurl.py not using absolute_import
93 tests/test-hg-parseurl.py not using absolute_import
95 tests/test-hg-parseurl.py requires print_function
94 tests/test-hg-parseurl.py requires print_function
96 tests/test-hgweb-auth.py not using absolute_import
95 tests/test-hgweb-auth.py not using absolute_import
97 tests/test-hgweb-auth.py requires print_function
96 tests/test-hgweb-auth.py requires print_function
98 tests/test-hgwebdir-paths.py not using absolute_import
97 tests/test-hgwebdir-paths.py not using absolute_import
99 tests/test-hybridencode.py not using absolute_import
98 tests/test-hybridencode.py not using absolute_import
100 tests/test-hybridencode.py requires print_function
99 tests/test-hybridencode.py requires print_function
101 tests/test-lrucachedict.py not using absolute_import
100 tests/test-lrucachedict.py not using absolute_import
102 tests/test-lrucachedict.py requires print_function
101 tests/test-lrucachedict.py requires print_function
103 tests/test-manifest.py not using absolute_import
102 tests/test-manifest.py not using absolute_import
104 tests/test-minirst.py not using absolute_import
103 tests/test-minirst.py not using absolute_import
105 tests/test-minirst.py requires print_function
104 tests/test-minirst.py requires print_function
106 tests/test-parseindex2.py not using absolute_import
105 tests/test-parseindex2.py not using absolute_import
107 tests/test-parseindex2.py requires print_function
106 tests/test-parseindex2.py requires print_function
108 tests/test-pathencode.py not using absolute_import
107 tests/test-pathencode.py not using absolute_import
109 tests/test-pathencode.py requires print_function
108 tests/test-pathencode.py requires print_function
110 tests/test-propertycache.py not using absolute_import
109 tests/test-propertycache.py not using absolute_import
111 tests/test-propertycache.py requires print_function
110 tests/test-propertycache.py requires print_function
112 tests/test-revlog-ancestry.py not using absolute_import
111 tests/test-revlog-ancestry.py not using absolute_import
113 tests/test-revlog-ancestry.py requires print_function
112 tests/test-revlog-ancestry.py requires print_function
114 tests/test-run-tests.py not using absolute_import
113 tests/test-run-tests.py not using absolute_import
115 tests/test-simplemerge.py not using absolute_import
114 tests/test-simplemerge.py not using absolute_import
116 tests/test-status-inprocess.py not using absolute_import
115 tests/test-status-inprocess.py not using absolute_import
117 tests/test-status-inprocess.py requires print_function
116 tests/test-status-inprocess.py requires print_function
118 tests/test-symlink-os-yes-fs-no.py not using absolute_import
117 tests/test-symlink-os-yes-fs-no.py not using absolute_import
119 tests/test-trusted.py not using absolute_import
118 tests/test-trusted.py not using absolute_import
120 tests/test-trusted.py requires print_function
119 tests/test-trusted.py requires print_function
121 tests/test-ui-color.py not using absolute_import
120 tests/test-ui-color.py not using absolute_import
122 tests/test-ui-color.py requires print_function
121 tests/test-ui-color.py requires print_function
123 tests/test-ui-config.py not using absolute_import
122 tests/test-ui-config.py not using absolute_import
124 tests/test-ui-config.py requires print_function
123 tests/test-ui-config.py requires print_function
125 tests/test-ui-verbosity.py not using absolute_import
124 tests/test-ui-verbosity.py not using absolute_import
126 tests/test-ui-verbosity.py requires print_function
125 tests/test-ui-verbosity.py requires print_function
127 tests/test-url.py not using absolute_import
126 tests/test-url.py not using absolute_import
128 tests/test-url.py requires print_function
127 tests/test-url.py requires print_function
129 tests/test-walkrepo.py requires print_function
128 tests/test-walkrepo.py requires print_function
130 tests/test-wireproto.py requires print_function
129 tests/test-wireproto.py requires print_function
131 tests/tinyproxy.py requires print_function
130 tests/tinyproxy.py requires print_function
General Comments 0
You need to be logged in to leave comments. Login now