##// END OF EJS Templates
use ui instead of repo.ui when the former is in scope
Martin Geisler -
r8615:94ca38e6 default
parent child Browse files
Show More
@@ -1,359 +1,359 b''
1 # Minimal support for git commands on an hg repository
1 # Minimal support for git commands on an hg repository
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''browsing the repository in a graphical way
8 '''browsing the repository in a graphical way
9
9
10 The hgk extension allows browsing the history of a repository in a
10 The hgk extension allows browsing the history of a repository in a
11 graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is not
11 graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is not
12 distributed with Mercurial.)
12 distributed with Mercurial.)
13
13
14 hgk consists of two parts: a Tcl script that does the displaying and
14 hgk consists of two parts: a Tcl script that does the displaying and
15 querying of information, and an extension to mercurial named hgk.py,
15 querying of information, and an extension to mercurial named hgk.py,
16 which provides hooks for hgk to get information. hgk can be found in
16 which provides hooks for hgk to get information. hgk can be found in
17 the contrib directory, and hgk.py can be found in the hgext directory.
17 the contrib directory, and hgk.py can be found in the hgext directory.
18
18
19 To load the hgext.py extension, add it to your .hgrc file (you have to
19 To load the hgext.py extension, add it to your .hgrc file (you have to
20 use your global $HOME/.hgrc file, not one in a repository). You can
20 use your global $HOME/.hgrc file, not one in a repository). You can
21 specify an absolute path:
21 specify an absolute path:
22
22
23 [extensions]
23 [extensions]
24 hgk=/usr/local/lib/hgk.py
24 hgk=/usr/local/lib/hgk.py
25
25
26 Mercurial can also scan the default python library path for a file
26 Mercurial can also scan the default python library path for a file
27 named 'hgk.py' if you set hgk empty:
27 named 'hgk.py' if you set hgk empty:
28
28
29 [extensions]
29 [extensions]
30 hgk=
30 hgk=
31
31
32 The hg view command will launch the hgk Tcl script. For this command
32 The hg view command will launch the hgk Tcl script. For this command
33 to work, hgk must be in your search path. Alternately, you can specify
33 to work, hgk must be in your search path. Alternately, you can specify
34 the path to hgk in your .hgrc file:
34 the path to hgk in your .hgrc file:
35
35
36 [hgk]
36 [hgk]
37 path=/location/of/hgk
37 path=/location/of/hgk
38
38
39 hgk can make use of the extdiff extension to visualize revisions.
39 hgk can make use of the extdiff extension to visualize revisions.
40 Assuming you had already configured extdiff vdiff command, just add:
40 Assuming you had already configured extdiff vdiff command, just add:
41
41
42 [hgk]
42 [hgk]
43 vdiff=vdiff
43 vdiff=vdiff
44
44
45 Revisions context menu will now display additional entries to fire
45 Revisions context menu will now display additional entries to fire
46 vdiff on hovered and selected revisions.'''
46 vdiff on hovered and selected revisions.'''
47
47
48 import os
48 import os
49 from mercurial import commands, util, patch, revlog, cmdutil
49 from mercurial import commands, util, patch, revlog, cmdutil
50 from mercurial.node import nullid, nullrev, short
50 from mercurial.node import nullid, nullrev, short
51 from mercurial.i18n import _
51 from mercurial.i18n import _
52
52
53 def difftree(ui, repo, node1=None, node2=None, *files, **opts):
53 def difftree(ui, repo, node1=None, node2=None, *files, **opts):
54 """diff trees from two commits"""
54 """diff trees from two commits"""
55 def __difftree(repo, node1, node2, files=[]):
55 def __difftree(repo, node1, node2, files=[]):
56 assert node2 is not None
56 assert node2 is not None
57 mmap = repo[node1].manifest()
57 mmap = repo[node1].manifest()
58 mmap2 = repo[node2].manifest()
58 mmap2 = repo[node2].manifest()
59 m = cmdutil.match(repo, files)
59 m = cmdutil.match(repo, files)
60 modified, added, removed = repo.status(node1, node2, m)[:3]
60 modified, added, removed = repo.status(node1, node2, m)[:3]
61 empty = short(nullid)
61 empty = short(nullid)
62
62
63 for f in modified:
63 for f in modified:
64 # TODO get file permissions
64 # TODO get file permissions
65 ui.write(":100664 100664 %s %s M\t%s\t%s\n" %
65 ui.write(":100664 100664 %s %s M\t%s\t%s\n" %
66 (short(mmap[f]), short(mmap2[f]), f, f))
66 (short(mmap[f]), short(mmap2[f]), f, f))
67 for f in added:
67 for f in added:
68 ui.write(":000000 100664 %s %s N\t%s\t%s\n" %
68 ui.write(":000000 100664 %s %s N\t%s\t%s\n" %
69 (empty, short(mmap2[f]), f, f))
69 (empty, short(mmap2[f]), f, f))
70 for f in removed:
70 for f in removed:
71 ui.write(":100664 000000 %s %s D\t%s\t%s\n" %
71 ui.write(":100664 000000 %s %s D\t%s\t%s\n" %
72 (short(mmap[f]), empty, f, f))
72 (short(mmap[f]), empty, f, f))
73 ##
73 ##
74
74
75 while True:
75 while True:
76 if opts['stdin']:
76 if opts['stdin']:
77 try:
77 try:
78 line = raw_input().split(' ')
78 line = raw_input().split(' ')
79 node1 = line[0]
79 node1 = line[0]
80 if len(line) > 1:
80 if len(line) > 1:
81 node2 = line[1]
81 node2 = line[1]
82 else:
82 else:
83 node2 = None
83 node2 = None
84 except EOFError:
84 except EOFError:
85 break
85 break
86 node1 = repo.lookup(node1)
86 node1 = repo.lookup(node1)
87 if node2:
87 if node2:
88 node2 = repo.lookup(node2)
88 node2 = repo.lookup(node2)
89 else:
89 else:
90 node2 = node1
90 node2 = node1
91 node1 = repo.changelog.parents(node1)[0]
91 node1 = repo.changelog.parents(node1)[0]
92 if opts['patch']:
92 if opts['patch']:
93 if opts['pretty']:
93 if opts['pretty']:
94 catcommit(ui, repo, node2, "")
94 catcommit(ui, repo, node2, "")
95 m = cmdutil.match(repo, files)
95 m = cmdutil.match(repo, files)
96 chunks = patch.diff(repo, node1, node2, match=m,
96 chunks = patch.diff(repo, node1, node2, match=m,
97 opts=patch.diffopts(ui, {'git': True}))
97 opts=patch.diffopts(ui, {'git': True}))
98 for chunk in chunks:
98 for chunk in chunks:
99 repo.ui.write(chunk)
99 ui.write(chunk)
100 else:
100 else:
101 __difftree(repo, node1, node2, files=files)
101 __difftree(repo, node1, node2, files=files)
102 if not opts['stdin']:
102 if not opts['stdin']:
103 break
103 break
104
104
105 def catcommit(ui, repo, n, prefix, ctx=None):
105 def catcommit(ui, repo, n, prefix, ctx=None):
106 nlprefix = '\n' + prefix;
106 nlprefix = '\n' + prefix;
107 if ctx is None:
107 if ctx is None:
108 ctx = repo[n]
108 ctx = repo[n]
109 ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
109 ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
110 for p in ctx.parents():
110 for p in ctx.parents():
111 ui.write("parent %s\n" % p)
111 ui.write("parent %s\n" % p)
112
112
113 date = ctx.date()
113 date = ctx.date()
114 description = ctx.description().replace("\0", "")
114 description = ctx.description().replace("\0", "")
115 lines = description.splitlines()
115 lines = description.splitlines()
116 if lines and lines[-1].startswith('committer:'):
116 if lines and lines[-1].startswith('committer:'):
117 committer = lines[-1].split(': ')[1].rstrip()
117 committer = lines[-1].split(': ')[1].rstrip()
118 else:
118 else:
119 committer = ctx.user()
119 committer = ctx.user()
120
120
121 ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
121 ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
122 ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
122 ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
123 ui.write("revision %d\n" % ctx.rev())
123 ui.write("revision %d\n" % ctx.rev())
124 ui.write("branch %s\n\n" % ctx.branch())
124 ui.write("branch %s\n\n" % ctx.branch())
125
125
126 if prefix != "":
126 if prefix != "":
127 ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
127 ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
128 else:
128 else:
129 ui.write(description + "\n")
129 ui.write(description + "\n")
130 if prefix:
130 if prefix:
131 ui.write('\0')
131 ui.write('\0')
132
132
133 def base(ui, repo, node1, node2):
133 def base(ui, repo, node1, node2):
134 """output common ancestor information"""
134 """output common ancestor information"""
135 node1 = repo.lookup(node1)
135 node1 = repo.lookup(node1)
136 node2 = repo.lookup(node2)
136 node2 = repo.lookup(node2)
137 n = repo.changelog.ancestor(node1, node2)
137 n = repo.changelog.ancestor(node1, node2)
138 ui.write(short(n) + "\n")
138 ui.write(short(n) + "\n")
139
139
140 def catfile(ui, repo, type=None, r=None, **opts):
140 def catfile(ui, repo, type=None, r=None, **opts):
141 """cat a specific revision"""
141 """cat a specific revision"""
142 # in stdin mode, every line except the commit is prefixed with two
142 # in stdin mode, every line except the commit is prefixed with two
143 # spaces. This way the our caller can find the commit without magic
143 # spaces. This way the our caller can find the commit without magic
144 # strings
144 # strings
145 #
145 #
146 prefix = ""
146 prefix = ""
147 if opts['stdin']:
147 if opts['stdin']:
148 try:
148 try:
149 (type, r) = raw_input().split(' ');
149 (type, r) = raw_input().split(' ');
150 prefix = " "
150 prefix = " "
151 except EOFError:
151 except EOFError:
152 return
152 return
153
153
154 else:
154 else:
155 if not type or not r:
155 if not type or not r:
156 ui.warn(_("cat-file: type or revision not supplied\n"))
156 ui.warn(_("cat-file: type or revision not supplied\n"))
157 commands.help_(ui, 'cat-file')
157 commands.help_(ui, 'cat-file')
158
158
159 while r:
159 while r:
160 if type != "commit":
160 if type != "commit":
161 ui.warn(_("aborting hg cat-file only understands commits\n"))
161 ui.warn(_("aborting hg cat-file only understands commits\n"))
162 return 1;
162 return 1;
163 n = repo.lookup(r)
163 n = repo.lookup(r)
164 catcommit(ui, repo, n, prefix)
164 catcommit(ui, repo, n, prefix)
165 if opts['stdin']:
165 if opts['stdin']:
166 try:
166 try:
167 (type, r) = raw_input().split(' ');
167 (type, r) = raw_input().split(' ');
168 except EOFError:
168 except EOFError:
169 break
169 break
170 else:
170 else:
171 break
171 break
172
172
173 # git rev-tree is a confusing thing. You can supply a number of
173 # git rev-tree is a confusing thing. You can supply a number of
174 # commit sha1s on the command line, and it walks the commit history
174 # commit sha1s on the command line, and it walks the commit history
175 # telling you which commits are reachable from the supplied ones via
175 # telling you which commits are reachable from the supplied ones via
176 # a bitmask based on arg position.
176 # a bitmask based on arg position.
177 # you can specify a commit to stop at by starting the sha1 with ^
177 # you can specify a commit to stop at by starting the sha1 with ^
178 def revtree(ui, args, repo, full="tree", maxnr=0, parents=False):
178 def revtree(ui, args, repo, full="tree", maxnr=0, parents=False):
179 def chlogwalk():
179 def chlogwalk():
180 count = len(repo)
180 count = len(repo)
181 i = count
181 i = count
182 l = [0] * 100
182 l = [0] * 100
183 chunk = 100
183 chunk = 100
184 while True:
184 while True:
185 if chunk > i:
185 if chunk > i:
186 chunk = i
186 chunk = i
187 i = 0
187 i = 0
188 else:
188 else:
189 i -= chunk
189 i -= chunk
190
190
191 for x in xrange(0, chunk):
191 for x in xrange(0, chunk):
192 if i + x >= count:
192 if i + x >= count:
193 l[chunk - x:] = [0] * (chunk - x)
193 l[chunk - x:] = [0] * (chunk - x)
194 break
194 break
195 if full != None:
195 if full != None:
196 l[x] = repo[i + x]
196 l[x] = repo[i + x]
197 l[x].changeset() # force reading
197 l[x].changeset() # force reading
198 else:
198 else:
199 l[x] = 1
199 l[x] = 1
200 for x in xrange(chunk-1, -1, -1):
200 for x in xrange(chunk-1, -1, -1):
201 if l[x] != 0:
201 if l[x] != 0:
202 yield (i + x, full != None and l[x] or None)
202 yield (i + x, full != None and l[x] or None)
203 if i == 0:
203 if i == 0:
204 break
204 break
205
205
206 # calculate and return the reachability bitmask for sha
206 # calculate and return the reachability bitmask for sha
207 def is_reachable(ar, reachable, sha):
207 def is_reachable(ar, reachable, sha):
208 if len(ar) == 0:
208 if len(ar) == 0:
209 return 1
209 return 1
210 mask = 0
210 mask = 0
211 for i in xrange(len(ar)):
211 for i in xrange(len(ar)):
212 if sha in reachable[i]:
212 if sha in reachable[i]:
213 mask |= 1 << i
213 mask |= 1 << i
214
214
215 return mask
215 return mask
216
216
217 reachable = []
217 reachable = []
218 stop_sha1 = []
218 stop_sha1 = []
219 want_sha1 = []
219 want_sha1 = []
220 count = 0
220 count = 0
221
221
222 # figure out which commits they are asking for and which ones they
222 # figure out which commits they are asking for and which ones they
223 # want us to stop on
223 # want us to stop on
224 for i in xrange(len(args)):
224 for i in xrange(len(args)):
225 if args[i].startswith('^'):
225 if args[i].startswith('^'):
226 s = repo.lookup(args[i][1:])
226 s = repo.lookup(args[i][1:])
227 stop_sha1.append(s)
227 stop_sha1.append(s)
228 want_sha1.append(s)
228 want_sha1.append(s)
229 elif args[i] != 'HEAD':
229 elif args[i] != 'HEAD':
230 want_sha1.append(repo.lookup(args[i]))
230 want_sha1.append(repo.lookup(args[i]))
231
231
232 # calculate the graph for the supplied commits
232 # calculate the graph for the supplied commits
233 for i in xrange(len(want_sha1)):
233 for i in xrange(len(want_sha1)):
234 reachable.append(set());
234 reachable.append(set());
235 n = want_sha1[i];
235 n = want_sha1[i];
236 visit = [n];
236 visit = [n];
237 reachable[i].add(n)
237 reachable[i].add(n)
238 while visit:
238 while visit:
239 n = visit.pop(0)
239 n = visit.pop(0)
240 if n in stop_sha1:
240 if n in stop_sha1:
241 continue
241 continue
242 for p in repo.changelog.parents(n):
242 for p in repo.changelog.parents(n):
243 if p not in reachable[i]:
243 if p not in reachable[i]:
244 reachable[i].add(p)
244 reachable[i].add(p)
245 visit.append(p)
245 visit.append(p)
246 if p in stop_sha1:
246 if p in stop_sha1:
247 continue
247 continue
248
248
249 # walk the repository looking for commits that are in our
249 # walk the repository looking for commits that are in our
250 # reachability graph
250 # reachability graph
251 for i, ctx in chlogwalk():
251 for i, ctx in chlogwalk():
252 n = repo.changelog.node(i)
252 n = repo.changelog.node(i)
253 mask = is_reachable(want_sha1, reachable, n)
253 mask = is_reachable(want_sha1, reachable, n)
254 if mask:
254 if mask:
255 parentstr = ""
255 parentstr = ""
256 if parents:
256 if parents:
257 pp = repo.changelog.parents(n)
257 pp = repo.changelog.parents(n)
258 if pp[0] != nullid:
258 if pp[0] != nullid:
259 parentstr += " " + short(pp[0])
259 parentstr += " " + short(pp[0])
260 if pp[1] != nullid:
260 if pp[1] != nullid:
261 parentstr += " " + short(pp[1])
261 parentstr += " " + short(pp[1])
262 if not full:
262 if not full:
263 ui.write("%s%s\n" % (short(n), parentstr))
263 ui.write("%s%s\n" % (short(n), parentstr))
264 elif full == "commit":
264 elif full == "commit":
265 ui.write("%s%s\n" % (short(n), parentstr))
265 ui.write("%s%s\n" % (short(n), parentstr))
266 catcommit(ui, repo, n, ' ', ctx)
266 catcommit(ui, repo, n, ' ', ctx)
267 else:
267 else:
268 (p1, p2) = repo.changelog.parents(n)
268 (p1, p2) = repo.changelog.parents(n)
269 (h, h1, h2) = map(short, (n, p1, p2))
269 (h, h1, h2) = map(short, (n, p1, p2))
270 (i1, i2) = map(repo.changelog.rev, (p1, p2))
270 (i1, i2) = map(repo.changelog.rev, (p1, p2))
271
271
272 date = ctx.date()[0]
272 date = ctx.date()[0]
273 ui.write("%s %s:%s" % (date, h, mask))
273 ui.write("%s %s:%s" % (date, h, mask))
274 mask = is_reachable(want_sha1, reachable, p1)
274 mask = is_reachable(want_sha1, reachable, p1)
275 if i1 != nullrev and mask > 0:
275 if i1 != nullrev and mask > 0:
276 ui.write("%s:%s " % (h1, mask)),
276 ui.write("%s:%s " % (h1, mask)),
277 mask = is_reachable(want_sha1, reachable, p2)
277 mask = is_reachable(want_sha1, reachable, p2)
278 if i2 != nullrev and mask > 0:
278 if i2 != nullrev and mask > 0:
279 ui.write("%s:%s " % (h2, mask))
279 ui.write("%s:%s " % (h2, mask))
280 ui.write("\n")
280 ui.write("\n")
281 if maxnr and count >= maxnr:
281 if maxnr and count >= maxnr:
282 break
282 break
283 count += 1
283 count += 1
284
284
285 def revparse(ui, repo, *revs, **opts):
285 def revparse(ui, repo, *revs, **opts):
286 """parse given revisions"""
286 """parse given revisions"""
287 def revstr(rev):
287 def revstr(rev):
288 if rev == 'HEAD':
288 if rev == 'HEAD':
289 rev = 'tip'
289 rev = 'tip'
290 return revlog.hex(repo.lookup(rev))
290 return revlog.hex(repo.lookup(rev))
291
291
292 for r in revs:
292 for r in revs:
293 revrange = r.split(':', 1)
293 revrange = r.split(':', 1)
294 ui.write('%s\n' % revstr(revrange[0]))
294 ui.write('%s\n' % revstr(revrange[0]))
295 if len(revrange) == 2:
295 if len(revrange) == 2:
296 ui.write('^%s\n' % revstr(revrange[1]))
296 ui.write('^%s\n' % revstr(revrange[1]))
297
297
298 # git rev-list tries to order things by date, and has the ability to stop
298 # git rev-list tries to order things by date, and has the ability to stop
299 # at a given commit without walking the whole repo. TODO add the stop
299 # at a given commit without walking the whole repo. TODO add the stop
300 # parameter
300 # parameter
301 def revlist(ui, repo, *revs, **opts):
301 def revlist(ui, repo, *revs, **opts):
302 """print revisions"""
302 """print revisions"""
303 if opts['header']:
303 if opts['header']:
304 full = "commit"
304 full = "commit"
305 else:
305 else:
306 full = None
306 full = None
307 copy = [x for x in revs]
307 copy = [x for x in revs]
308 revtree(ui, copy, repo, full, opts['max_count'], opts['parents'])
308 revtree(ui, copy, repo, full, opts['max_count'], opts['parents'])
309
309
310 def config(ui, repo, **opts):
310 def config(ui, repo, **opts):
311 """print extension options"""
311 """print extension options"""
312 def writeopt(name, value):
312 def writeopt(name, value):
313 ui.write('k=%s\nv=%s\n' % (name, value))
313 ui.write('k=%s\nv=%s\n' % (name, value))
314
314
315 writeopt('vdiff', ui.config('hgk', 'vdiff', ''))
315 writeopt('vdiff', ui.config('hgk', 'vdiff', ''))
316
316
317
317
318 def view(ui, repo, *etc, **opts):
318 def view(ui, repo, *etc, **opts):
319 "start interactive history viewer"
319 "start interactive history viewer"
320 os.chdir(repo.root)
320 os.chdir(repo.root)
321 optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems() if v])
321 optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems() if v])
322 cmd = ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc))
322 cmd = ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc))
323 ui.debug(_("running %s\n") % cmd)
323 ui.debug(_("running %s\n") % cmd)
324 util.system(cmd)
324 util.system(cmd)
325
325
326 cmdtable = {
326 cmdtable = {
327 "^view":
327 "^view":
328 (view,
328 (view,
329 [('l', 'limit', '', _('limit number of changes displayed'))],
329 [('l', 'limit', '', _('limit number of changes displayed'))],
330 _('hg view [-l LIMIT] [REVRANGE]')),
330 _('hg view [-l LIMIT] [REVRANGE]')),
331 "debug-diff-tree":
331 "debug-diff-tree":
332 (difftree,
332 (difftree,
333 [('p', 'patch', None, _('generate patch')),
333 [('p', 'patch', None, _('generate patch')),
334 ('r', 'recursive', None, _('recursive')),
334 ('r', 'recursive', None, _('recursive')),
335 ('P', 'pretty', None, _('pretty')),
335 ('P', 'pretty', None, _('pretty')),
336 ('s', 'stdin', None, _('stdin')),
336 ('s', 'stdin', None, _('stdin')),
337 ('C', 'copy', None, _('detect copies')),
337 ('C', 'copy', None, _('detect copies')),
338 ('S', 'search', "", _('search'))],
338 ('S', 'search', "", _('search'))],
339 _('hg git-diff-tree [OPTION]... NODE1 NODE2 [FILE]...')),
339 _('hg git-diff-tree [OPTION]... NODE1 NODE2 [FILE]...')),
340 "debug-cat-file":
340 "debug-cat-file":
341 (catfile,
341 (catfile,
342 [('s', 'stdin', None, _('stdin'))],
342 [('s', 'stdin', None, _('stdin'))],
343 _('hg debug-cat-file [OPTION]... TYPE FILE')),
343 _('hg debug-cat-file [OPTION]... TYPE FILE')),
344 "debug-config":
344 "debug-config":
345 (config, [], _('hg debug-config')),
345 (config, [], _('hg debug-config')),
346 "debug-merge-base":
346 "debug-merge-base":
347 (base, [], _('hg debug-merge-base node node')),
347 (base, [], _('hg debug-merge-base node node')),
348 "debug-rev-parse":
348 "debug-rev-parse":
349 (revparse,
349 (revparse,
350 [('', 'default', '', _('ignored'))],
350 [('', 'default', '', _('ignored'))],
351 _('hg debug-rev-parse REV')),
351 _('hg debug-rev-parse REV')),
352 "debug-rev-list":
352 "debug-rev-list":
353 (revlist,
353 (revlist,
354 [('H', 'header', None, _('header')),
354 [('H', 'header', None, _('header')),
355 ('t', 'topo-order', None, _('topo-order')),
355 ('t', 'topo-order', None, _('topo-order')),
356 ('p', 'parents', None, _('parents')),
356 ('p', 'parents', None, _('parents')),
357 ('n', 'max-count', 0, _('max-count'))],
357 ('n', 'max-count', 0, _('max-count'))],
358 _('hg debug-rev-list [options] revs')),
358 _('hg debug-rev-list [options] revs')),
359 }
359 }
@@ -1,474 +1,474 b''
1 # rebase.py - rebasing feature for mercurial
1 # rebase.py - rebasing feature for mercurial
2 #
2 #
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot 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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''move sets of revisions to a different ancestor
8 '''move sets of revisions to a different ancestor
9
9
10 This extension lets you rebase changesets in an existing Mercurial
10 This extension lets you rebase changesets in an existing Mercurial
11 repository.
11 repository.
12
12
13 For more information:
13 For more information:
14 http://www.selenic.com/mercurial/wiki/index.cgi/RebaseProject
14 http://www.selenic.com/mercurial/wiki/index.cgi/RebaseProject
15 '''
15 '''
16
16
17 from mercurial import util, repair, merge, cmdutil, commands, error
17 from mercurial import util, repair, merge, cmdutil, commands, error
18 from mercurial import extensions, ancestor, copies, patch
18 from mercurial import extensions, ancestor, copies, patch
19 from mercurial.commands import templateopts
19 from mercurial.commands import templateopts
20 from mercurial.node import nullrev
20 from mercurial.node import nullrev
21 from mercurial.lock import release
21 from mercurial.lock import release
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 import os, errno
23 import os, errno
24
24
25 def rebasemerge(repo, rev, first=False):
25 def rebasemerge(repo, rev, first=False):
26 'return the correct ancestor'
26 'return the correct ancestor'
27 oldancestor = ancestor.ancestor
27 oldancestor = ancestor.ancestor
28
28
29 def newancestor(a, b, pfunc):
29 def newancestor(a, b, pfunc):
30 ancestor.ancestor = oldancestor
30 ancestor.ancestor = oldancestor
31 if b == rev:
31 if b == rev:
32 return repo[rev].parents()[0].rev()
32 return repo[rev].parents()[0].rev()
33 return ancestor.ancestor(a, b, pfunc)
33 return ancestor.ancestor(a, b, pfunc)
34
34
35 if not first:
35 if not first:
36 ancestor.ancestor = newancestor
36 ancestor.ancestor = newancestor
37 else:
37 else:
38 repo.ui.debug(_("first revision, do not change ancestor\n"))
38 repo.ui.debug(_("first revision, do not change ancestor\n"))
39 stats = merge.update(repo, rev, True, True, False)
39 stats = merge.update(repo, rev, True, True, False)
40 return stats
40 return stats
41
41
42 def rebase(ui, repo, **opts):
42 def rebase(ui, repo, **opts):
43 """move changeset (and descendants) to a different branch
43 """move changeset (and descendants) to a different branch
44
44
45 Rebase uses repeated merging to graft changesets from one part of
45 Rebase uses repeated merging to graft changesets from one part of
46 history onto another. This can be useful for linearizing local
46 history onto another. This can be useful for linearizing local
47 changes relative to a master development tree.
47 changes relative to a master development tree.
48
48
49 If a rebase is interrupted to manually resolve a merge, it can be
49 If a rebase is interrupted to manually resolve a merge, it can be
50 continued with --continue/-c or aborted with --abort/-a.
50 continued with --continue/-c or aborted with --abort/-a.
51 """
51 """
52 originalwd = target = None
52 originalwd = target = None
53 external = nullrev
53 external = nullrev
54 state = {}
54 state = {}
55 skipped = set()
55 skipped = set()
56
56
57 lock = wlock = None
57 lock = wlock = None
58 try:
58 try:
59 lock = repo.lock()
59 lock = repo.lock()
60 wlock = repo.wlock()
60 wlock = repo.wlock()
61
61
62 # Validate input and define rebasing points
62 # Validate input and define rebasing points
63 destf = opts.get('dest', None)
63 destf = opts.get('dest', None)
64 srcf = opts.get('source', None)
64 srcf = opts.get('source', None)
65 basef = opts.get('base', None)
65 basef = opts.get('base', None)
66 contf = opts.get('continue')
66 contf = opts.get('continue')
67 abortf = opts.get('abort')
67 abortf = opts.get('abort')
68 collapsef = opts.get('collapse', False)
68 collapsef = opts.get('collapse', False)
69 extrafn = opts.get('extrafn')
69 extrafn = opts.get('extrafn')
70 keepf = opts.get('keep', False)
70 keepf = opts.get('keep', False)
71 keepbranchesf = opts.get('keepbranches', False)
71 keepbranchesf = opts.get('keepbranches', False)
72
72
73 if contf or abortf:
73 if contf or abortf:
74 if contf and abortf:
74 if contf and abortf:
75 raise error.ParseError('rebase',
75 raise error.ParseError('rebase',
76 _('cannot use both abort and continue'))
76 _('cannot use both abort and continue'))
77 if collapsef:
77 if collapsef:
78 raise error.ParseError(
78 raise error.ParseError(
79 'rebase', _('cannot use collapse with continue or abort'))
79 'rebase', _('cannot use collapse with continue or abort'))
80
80
81 if srcf or basef or destf:
81 if srcf or basef or destf:
82 raise error.ParseError('rebase',
82 raise error.ParseError('rebase',
83 _('abort and continue do not allow specifying revisions'))
83 _('abort and continue do not allow specifying revisions'))
84
84
85 (originalwd, target, state, collapsef, keepf,
85 (originalwd, target, state, collapsef, keepf,
86 keepbranchesf, external) = restorestatus(repo)
86 keepbranchesf, external) = restorestatus(repo)
87 if abortf:
87 if abortf:
88 abort(repo, originalwd, target, state)
88 abort(repo, originalwd, target, state)
89 return
89 return
90 else:
90 else:
91 if srcf and basef:
91 if srcf and basef:
92 raise error.ParseError('rebase', _('cannot specify both a '
92 raise error.ParseError('rebase', _('cannot specify both a '
93 'revision and a base'))
93 'revision and a base'))
94 cmdutil.bail_if_changed(repo)
94 cmdutil.bail_if_changed(repo)
95 result = buildstate(repo, destf, srcf, basef, collapsef)
95 result = buildstate(repo, destf, srcf, basef, collapsef)
96 if result:
96 if result:
97 originalwd, target, state, external = result
97 originalwd, target, state, external = result
98 else: # Empty state built, nothing to rebase
98 else: # Empty state built, nothing to rebase
99 repo.ui.status(_('nothing to rebase\n'))
99 ui.status(_('nothing to rebase\n'))
100 return
100 return
101
101
102 if keepbranchesf:
102 if keepbranchesf:
103 if extrafn:
103 if extrafn:
104 raise error.ParseError(
104 raise error.ParseError(
105 'rebase', _('cannot use both keepbranches and extrafn'))
105 'rebase', _('cannot use both keepbranches and extrafn'))
106 def extrafn(ctx, extra):
106 def extrafn(ctx, extra):
107 extra['branch'] = ctx.branch()
107 extra['branch'] = ctx.branch()
108
108
109 # Rebase
109 # Rebase
110 targetancestors = list(repo.changelog.ancestors(target))
110 targetancestors = list(repo.changelog.ancestors(target))
111 targetancestors.append(target)
111 targetancestors.append(target)
112
112
113 for rev in sorted(state):
113 for rev in sorted(state):
114 if state[rev] == -1:
114 if state[rev] == -1:
115 storestatus(repo, originalwd, target, state, collapsef, keepf,
115 storestatus(repo, originalwd, target, state, collapsef, keepf,
116 keepbranchesf, external)
116 keepbranchesf, external)
117 rebasenode(repo, rev, target, state, skipped, targetancestors,
117 rebasenode(repo, rev, target, state, skipped, targetancestors,
118 collapsef, extrafn)
118 collapsef, extrafn)
119 ui.note(_('rebase merging completed\n'))
119 ui.note(_('rebase merging completed\n'))
120
120
121 if collapsef:
121 if collapsef:
122 p1, p2 = defineparents(repo, min(state), target,
122 p1, p2 = defineparents(repo, min(state), target,
123 state, targetancestors)
123 state, targetancestors)
124 concludenode(repo, rev, p1, external, state, collapsef,
124 concludenode(repo, rev, p1, external, state, collapsef,
125 last=True, skipped=skipped, extrafn=extrafn)
125 last=True, skipped=skipped, extrafn=extrafn)
126
126
127 if 'qtip' in repo.tags():
127 if 'qtip' in repo.tags():
128 updatemq(repo, state, skipped, **opts)
128 updatemq(repo, state, skipped, **opts)
129
129
130 if not keepf:
130 if not keepf:
131 # Remove no more useful revisions
131 # Remove no more useful revisions
132 if set(repo.changelog.descendants(min(state))) - set(state):
132 if set(repo.changelog.descendants(min(state))) - set(state):
133 ui.warn(_("warning: new changesets detected on source branch, "
133 ui.warn(_("warning: new changesets detected on source branch, "
134 "not stripping\n"))
134 "not stripping\n"))
135 else:
135 else:
136 repair.strip(repo.ui, repo, repo[min(state)].node(), "strip")
136 repair.strip(ui, repo, repo[min(state)].node(), "strip")
137
137
138 clearstatus(repo)
138 clearstatus(repo)
139 ui.status(_("rebase completed\n"))
139 ui.status(_("rebase completed\n"))
140 if os.path.exists(repo.sjoin('undo')):
140 if os.path.exists(repo.sjoin('undo')):
141 util.unlink(repo.sjoin('undo'))
141 util.unlink(repo.sjoin('undo'))
142 if skipped:
142 if skipped:
143 ui.note(_("%d revisions have been skipped\n") % len(skipped))
143 ui.note(_("%d revisions have been skipped\n") % len(skipped))
144 finally:
144 finally:
145 release(lock, wlock)
145 release(lock, wlock)
146
146
147 def concludenode(repo, rev, p1, p2, state, collapse, last=False, skipped=None,
147 def concludenode(repo, rev, p1, p2, state, collapse, last=False, skipped=None,
148 extrafn=None):
148 extrafn=None):
149 """Skip commit if collapsing has been required and rev is not the last
149 """Skip commit if collapsing has been required and rev is not the last
150 revision, commit otherwise
150 revision, commit otherwise
151 """
151 """
152 repo.ui.debug(_(" set parents\n"))
152 repo.ui.debug(_(" set parents\n"))
153 if collapse and not last:
153 if collapse and not last:
154 repo.dirstate.setparents(repo[p1].node())
154 repo.dirstate.setparents(repo[p1].node())
155 return None
155 return None
156
156
157 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
157 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
158
158
159 if skipped is None:
159 if skipped is None:
160 skipped = set()
160 skipped = set()
161
161
162 # Commit, record the old nodeid
162 # Commit, record the old nodeid
163 m, a, r = repo.status()[:3]
163 m, a, r = repo.status()[:3]
164 newrev = nullrev
164 newrev = nullrev
165 try:
165 try:
166 if last:
166 if last:
167 commitmsg = 'Collapsed revision'
167 commitmsg = 'Collapsed revision'
168 for rebased in state:
168 for rebased in state:
169 if rebased not in skipped:
169 if rebased not in skipped:
170 commitmsg += '\n* %s' % repo[rebased].description()
170 commitmsg += '\n* %s' % repo[rebased].description()
171 commitmsg = repo.ui.edit(commitmsg, repo.ui.username())
171 commitmsg = repo.ui.edit(commitmsg, repo.ui.username())
172 else:
172 else:
173 commitmsg = repo[rev].description()
173 commitmsg = repo[rev].description()
174 # Commit might fail if unresolved files exist
174 # Commit might fail if unresolved files exist
175 extra = {'rebase_source': repo[rev].hex()}
175 extra = {'rebase_source': repo[rev].hex()}
176 if extrafn:
176 if extrafn:
177 extrafn(repo[rev], extra)
177 extrafn(repo[rev], extra)
178 newrev = repo.commit(m+a+r,
178 newrev = repo.commit(m+a+r,
179 text=commitmsg,
179 text=commitmsg,
180 user=repo[rev].user(),
180 user=repo[rev].user(),
181 date=repo[rev].date(),
181 date=repo[rev].date(),
182 extra=extra)
182 extra=extra)
183 repo.dirstate.setbranch(repo[newrev].branch())
183 repo.dirstate.setbranch(repo[newrev].branch())
184 return newrev
184 return newrev
185 except util.Abort:
185 except util.Abort:
186 # Invalidate the previous setparents
186 # Invalidate the previous setparents
187 repo.dirstate.invalidate()
187 repo.dirstate.invalidate()
188 raise
188 raise
189
189
190 def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse,
190 def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse,
191 extrafn):
191 extrafn):
192 'Rebase a single revision'
192 'Rebase a single revision'
193 repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev]))
193 repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev]))
194
194
195 p1, p2 = defineparents(repo, rev, target, state, targetancestors)
195 p1, p2 = defineparents(repo, rev, target, state, targetancestors)
196
196
197 repo.ui.debug(_(" future parents are %d and %d\n") % (repo[p1].rev(),
197 repo.ui.debug(_(" future parents are %d and %d\n") % (repo[p1].rev(),
198 repo[p2].rev()))
198 repo[p2].rev()))
199
199
200 # Merge phase
200 # Merge phase
201 if len(repo.parents()) != 2:
201 if len(repo.parents()) != 2:
202 # Update to target and merge it with local
202 # Update to target and merge it with local
203 if repo['.'].rev() != repo[p1].rev():
203 if repo['.'].rev() != repo[p1].rev():
204 repo.ui.debug(_(" update to %d:%s\n") % (repo[p1].rev(), repo[p1]))
204 repo.ui.debug(_(" update to %d:%s\n") % (repo[p1].rev(), repo[p1]))
205 merge.update(repo, p1, False, True, False)
205 merge.update(repo, p1, False, True, False)
206 else:
206 else:
207 repo.ui.debug(_(" already in target\n"))
207 repo.ui.debug(_(" already in target\n"))
208 repo.dirstate.write()
208 repo.dirstate.write()
209 repo.ui.debug(_(" merge against %d:%s\n") % (repo[rev].rev(), repo[rev]))
209 repo.ui.debug(_(" merge against %d:%s\n") % (repo[rev].rev(), repo[rev]))
210 first = repo[rev].rev() == repo[min(state)].rev()
210 first = repo[rev].rev() == repo[min(state)].rev()
211 stats = rebasemerge(repo, rev, first)
211 stats = rebasemerge(repo, rev, first)
212
212
213 if stats[3] > 0:
213 if stats[3] > 0:
214 raise util.Abort(_('fix unresolved conflicts with hg resolve then '
214 raise util.Abort(_('fix unresolved conflicts with hg resolve then '
215 'run hg rebase --continue'))
215 'run hg rebase --continue'))
216 else: # we have an interrupted rebase
216 else: # we have an interrupted rebase
217 repo.ui.debug(_('resuming interrupted rebase\n'))
217 repo.ui.debug(_('resuming interrupted rebase\n'))
218
218
219 # Keep track of renamed files in the revision that is going to be rebased
219 # Keep track of renamed files in the revision that is going to be rebased
220 # Here we simulate the copies and renames in the source changeset
220 # Here we simulate the copies and renames in the source changeset
221 cop, diver = copies.copies(repo, repo[rev], repo[target], repo[p2], True)
221 cop, diver = copies.copies(repo, repo[rev], repo[target], repo[p2], True)
222 m1 = repo[rev].manifest()
222 m1 = repo[rev].manifest()
223 m2 = repo[target].manifest()
223 m2 = repo[target].manifest()
224 for k, v in cop.iteritems():
224 for k, v in cop.iteritems():
225 if k in m1:
225 if k in m1:
226 if v in m1 or v in m2:
226 if v in m1 or v in m2:
227 repo.dirstate.copy(v, k)
227 repo.dirstate.copy(v, k)
228 if v in m2 and v not in m1:
228 if v in m2 and v not in m1:
229 repo.dirstate.remove(v)
229 repo.dirstate.remove(v)
230
230
231 newrev = concludenode(repo, rev, p1, p2, state, collapse,
231 newrev = concludenode(repo, rev, p1, p2, state, collapse,
232 extrafn=extrafn)
232 extrafn=extrafn)
233
233
234 # Update the state
234 # Update the state
235 if newrev is not None:
235 if newrev is not None:
236 state[rev] = repo[newrev].rev()
236 state[rev] = repo[newrev].rev()
237 else:
237 else:
238 if not collapse:
238 if not collapse:
239 repo.ui.note(_('no changes, revision %d skipped\n') % rev)
239 repo.ui.note(_('no changes, revision %d skipped\n') % rev)
240 repo.ui.debug(_('next revision set to %s\n') % p1)
240 repo.ui.debug(_('next revision set to %s\n') % p1)
241 skipped.add(rev)
241 skipped.add(rev)
242 state[rev] = p1
242 state[rev] = p1
243
243
244 def defineparents(repo, rev, target, state, targetancestors):
244 def defineparents(repo, rev, target, state, targetancestors):
245 'Return the new parent relationship of the revision that will be rebased'
245 'Return the new parent relationship of the revision that will be rebased'
246 parents = repo[rev].parents()
246 parents = repo[rev].parents()
247 p1 = p2 = nullrev
247 p1 = p2 = nullrev
248
248
249 P1n = parents[0].rev()
249 P1n = parents[0].rev()
250 if P1n in targetancestors:
250 if P1n in targetancestors:
251 p1 = target
251 p1 = target
252 elif P1n in state:
252 elif P1n in state:
253 p1 = state[P1n]
253 p1 = state[P1n]
254 else: # P1n external
254 else: # P1n external
255 p1 = target
255 p1 = target
256 p2 = P1n
256 p2 = P1n
257
257
258 if len(parents) == 2 and parents[1].rev() not in targetancestors:
258 if len(parents) == 2 and parents[1].rev() not in targetancestors:
259 P2n = parents[1].rev()
259 P2n = parents[1].rev()
260 # interesting second parent
260 # interesting second parent
261 if P2n in state:
261 if P2n in state:
262 if p1 == target: # P1n in targetancestors or external
262 if p1 == target: # P1n in targetancestors or external
263 p1 = state[P2n]
263 p1 = state[P2n]
264 else:
264 else:
265 p2 = state[P2n]
265 p2 = state[P2n]
266 else: # P2n external
266 else: # P2n external
267 if p2 != nullrev: # P1n external too => rev is a merged revision
267 if p2 != nullrev: # P1n external too => rev is a merged revision
268 raise util.Abort(_('cannot use revision %d as base, result '
268 raise util.Abort(_('cannot use revision %d as base, result '
269 'would have 3 parents') % rev)
269 'would have 3 parents') % rev)
270 p2 = P2n
270 p2 = P2n
271 return p1, p2
271 return p1, p2
272
272
273 def isagitpatch(repo, patchname):
273 def isagitpatch(repo, patchname):
274 'Return true if the given patch is in git format'
274 'Return true if the given patch is in git format'
275 mqpatch = os.path.join(repo.mq.path, patchname)
275 mqpatch = os.path.join(repo.mq.path, patchname)
276 for line in patch.linereader(file(mqpatch, 'rb')):
276 for line in patch.linereader(file(mqpatch, 'rb')):
277 if line.startswith('diff --git'):
277 if line.startswith('diff --git'):
278 return True
278 return True
279 return False
279 return False
280
280
281 def updatemq(repo, state, skipped, **opts):
281 def updatemq(repo, state, skipped, **opts):
282 'Update rebased mq patches - finalize and then import them'
282 'Update rebased mq patches - finalize and then import them'
283 mqrebase = {}
283 mqrebase = {}
284 for p in repo.mq.applied:
284 for p in repo.mq.applied:
285 if repo[p.rev].rev() in state:
285 if repo[p.rev].rev() in state:
286 repo.ui.debug(_('revision %d is an mq patch (%s), finalize it.\n') %
286 repo.ui.debug(_('revision %d is an mq patch (%s), finalize it.\n') %
287 (repo[p.rev].rev(), p.name))
287 (repo[p.rev].rev(), p.name))
288 mqrebase[repo[p.rev].rev()] = (p.name, isagitpatch(repo, p.name))
288 mqrebase[repo[p.rev].rev()] = (p.name, isagitpatch(repo, p.name))
289
289
290 if mqrebase:
290 if mqrebase:
291 repo.mq.finish(repo, mqrebase.keys())
291 repo.mq.finish(repo, mqrebase.keys())
292
292
293 # We must start import from the newest revision
293 # We must start import from the newest revision
294 for rev in sorted(mqrebase, reverse=True):
294 for rev in sorted(mqrebase, reverse=True):
295 if rev not in skipped:
295 if rev not in skipped:
296 repo.ui.debug(_('import mq patch %d (%s)\n')
296 repo.ui.debug(_('import mq patch %d (%s)\n')
297 % (state[rev], mqrebase[rev][0]))
297 % (state[rev], mqrebase[rev][0]))
298 repo.mq.qimport(repo, (), patchname=mqrebase[rev][0],
298 repo.mq.qimport(repo, (), patchname=mqrebase[rev][0],
299 git=mqrebase[rev][1],rev=[str(state[rev])])
299 git=mqrebase[rev][1],rev=[str(state[rev])])
300 repo.mq.save_dirty()
300 repo.mq.save_dirty()
301
301
302 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
302 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
303 external):
303 external):
304 'Store the current status to allow recovery'
304 'Store the current status to allow recovery'
305 f = repo.opener("rebasestate", "w")
305 f = repo.opener("rebasestate", "w")
306 f.write(repo[originalwd].hex() + '\n')
306 f.write(repo[originalwd].hex() + '\n')
307 f.write(repo[target].hex() + '\n')
307 f.write(repo[target].hex() + '\n')
308 f.write(repo[external].hex() + '\n')
308 f.write(repo[external].hex() + '\n')
309 f.write('%d\n' % int(collapse))
309 f.write('%d\n' % int(collapse))
310 f.write('%d\n' % int(keep))
310 f.write('%d\n' % int(keep))
311 f.write('%d\n' % int(keepbranches))
311 f.write('%d\n' % int(keepbranches))
312 for d, v in state.iteritems():
312 for d, v in state.iteritems():
313 oldrev = repo[d].hex()
313 oldrev = repo[d].hex()
314 newrev = repo[v].hex()
314 newrev = repo[v].hex()
315 f.write("%s:%s\n" % (oldrev, newrev))
315 f.write("%s:%s\n" % (oldrev, newrev))
316 f.close()
316 f.close()
317 repo.ui.debug(_('rebase status stored\n'))
317 repo.ui.debug(_('rebase status stored\n'))
318
318
319 def clearstatus(repo):
319 def clearstatus(repo):
320 'Remove the status files'
320 'Remove the status files'
321 if os.path.exists(repo.join("rebasestate")):
321 if os.path.exists(repo.join("rebasestate")):
322 util.unlink(repo.join("rebasestate"))
322 util.unlink(repo.join("rebasestate"))
323
323
324 def restorestatus(repo):
324 def restorestatus(repo):
325 'Restore a previously stored status'
325 'Restore a previously stored status'
326 try:
326 try:
327 target = None
327 target = None
328 collapse = False
328 collapse = False
329 external = nullrev
329 external = nullrev
330 state = {}
330 state = {}
331 f = repo.opener("rebasestate")
331 f = repo.opener("rebasestate")
332 for i, l in enumerate(f.read().splitlines()):
332 for i, l in enumerate(f.read().splitlines()):
333 if i == 0:
333 if i == 0:
334 originalwd = repo[l].rev()
334 originalwd = repo[l].rev()
335 elif i == 1:
335 elif i == 1:
336 target = repo[l].rev()
336 target = repo[l].rev()
337 elif i == 2:
337 elif i == 2:
338 external = repo[l].rev()
338 external = repo[l].rev()
339 elif i == 3:
339 elif i == 3:
340 collapse = bool(int(l))
340 collapse = bool(int(l))
341 elif i == 4:
341 elif i == 4:
342 keep = bool(int(l))
342 keep = bool(int(l))
343 elif i == 5:
343 elif i == 5:
344 keepbranches = bool(int(l))
344 keepbranches = bool(int(l))
345 else:
345 else:
346 oldrev, newrev = l.split(':')
346 oldrev, newrev = l.split(':')
347 state[repo[oldrev].rev()] = repo[newrev].rev()
347 state[repo[oldrev].rev()] = repo[newrev].rev()
348 repo.ui.debug(_('rebase status resumed\n'))
348 repo.ui.debug(_('rebase status resumed\n'))
349 return originalwd, target, state, collapse, keep, keepbranches, external
349 return originalwd, target, state, collapse, keep, keepbranches, external
350 except IOError, err:
350 except IOError, err:
351 if err.errno != errno.ENOENT:
351 if err.errno != errno.ENOENT:
352 raise
352 raise
353 raise util.Abort(_('no rebase in progress'))
353 raise util.Abort(_('no rebase in progress'))
354
354
355 def abort(repo, originalwd, target, state):
355 def abort(repo, originalwd, target, state):
356 'Restore the repository to its original state'
356 'Restore the repository to its original state'
357 if set(repo.changelog.descendants(target)) - set(state.values()):
357 if set(repo.changelog.descendants(target)) - set(state.values()):
358 repo.ui.warn(_("warning: new changesets detected on target branch, "
358 repo.ui.warn(_("warning: new changesets detected on target branch, "
359 "not stripping\n"))
359 "not stripping\n"))
360 else:
360 else:
361 # Strip from the first rebased revision
361 # Strip from the first rebased revision
362 merge.update(repo, repo[originalwd].rev(), False, True, False)
362 merge.update(repo, repo[originalwd].rev(), False, True, False)
363 rebased = filter(lambda x: x > -1, state.values())
363 rebased = filter(lambda x: x > -1, state.values())
364 if rebased:
364 if rebased:
365 strippoint = min(rebased)
365 strippoint = min(rebased)
366 repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
366 repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
367 clearstatus(repo)
367 clearstatus(repo)
368 repo.ui.status(_('rebase aborted\n'))
368 repo.ui.status(_('rebase aborted\n'))
369
369
370 def buildstate(repo, dest, src, base, collapse):
370 def buildstate(repo, dest, src, base, collapse):
371 'Define which revisions are going to be rebased and where'
371 'Define which revisions are going to be rebased and where'
372 targetancestors = set()
372 targetancestors = set()
373
373
374 if not dest:
374 if not dest:
375 # Destination defaults to the latest revision in the current branch
375 # Destination defaults to the latest revision in the current branch
376 branch = repo[None].branch()
376 branch = repo[None].branch()
377 dest = repo[branch].rev()
377 dest = repo[branch].rev()
378 else:
378 else:
379 if 'qtip' in repo.tags() and (repo[dest].hex() in
379 if 'qtip' in repo.tags() and (repo[dest].hex() in
380 [s.rev for s in repo.mq.applied]):
380 [s.rev for s in repo.mq.applied]):
381 raise util.Abort(_('cannot rebase onto an applied mq patch'))
381 raise util.Abort(_('cannot rebase onto an applied mq patch'))
382 dest = repo[dest].rev()
382 dest = repo[dest].rev()
383
383
384 if src:
384 if src:
385 commonbase = repo[src].ancestor(repo[dest])
385 commonbase = repo[src].ancestor(repo[dest])
386 if commonbase == repo[src]:
386 if commonbase == repo[src]:
387 raise util.Abort(_('cannot rebase an ancestor'))
387 raise util.Abort(_('cannot rebase an ancestor'))
388 if commonbase == repo[dest]:
388 if commonbase == repo[dest]:
389 raise util.Abort(_('cannot rebase a descendant'))
389 raise util.Abort(_('cannot rebase a descendant'))
390 source = repo[src].rev()
390 source = repo[src].rev()
391 else:
391 else:
392 if base:
392 if base:
393 cwd = repo[base].rev()
393 cwd = repo[base].rev()
394 else:
394 else:
395 cwd = repo['.'].rev()
395 cwd = repo['.'].rev()
396
396
397 if cwd == dest:
397 if cwd == dest:
398 repo.ui.debug(_('already working on current\n'))
398 repo.ui.debug(_('already working on current\n'))
399 return None
399 return None
400
400
401 targetancestors = set(repo.changelog.ancestors(dest))
401 targetancestors = set(repo.changelog.ancestors(dest))
402 if cwd in targetancestors:
402 if cwd in targetancestors:
403 repo.ui.debug(_('already working on the current branch\n'))
403 repo.ui.debug(_('already working on the current branch\n'))
404 return None
404 return None
405
405
406 cwdancestors = set(repo.changelog.ancestors(cwd))
406 cwdancestors = set(repo.changelog.ancestors(cwd))
407 cwdancestors.add(cwd)
407 cwdancestors.add(cwd)
408 rebasingbranch = cwdancestors - targetancestors
408 rebasingbranch = cwdancestors - targetancestors
409 source = min(rebasingbranch)
409 source = min(rebasingbranch)
410
410
411 repo.ui.debug(_('rebase onto %d starting from %d\n') % (dest, source))
411 repo.ui.debug(_('rebase onto %d starting from %d\n') % (dest, source))
412 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
412 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
413 external = nullrev
413 external = nullrev
414 if collapse:
414 if collapse:
415 if not targetancestors:
415 if not targetancestors:
416 targetancestors = set(repo.changelog.ancestors(dest))
416 targetancestors = set(repo.changelog.ancestors(dest))
417 for rev in state:
417 for rev in state:
418 # Check externals and fail if there are more than one
418 # Check externals and fail if there are more than one
419 for p in repo[rev].parents():
419 for p in repo[rev].parents():
420 if (p.rev() not in state and p.rev() != source
420 if (p.rev() not in state and p.rev() != source
421 and p.rev() not in targetancestors):
421 and p.rev() not in targetancestors):
422 if external != nullrev:
422 if external != nullrev:
423 raise util.Abort(_('unable to collapse, there is more '
423 raise util.Abort(_('unable to collapse, there is more '
424 'than one external parent'))
424 'than one external parent'))
425 external = p.rev()
425 external = p.rev()
426
426
427 state[source] = nullrev
427 state[source] = nullrev
428 return repo['.'].rev(), repo[dest].rev(), state, external
428 return repo['.'].rev(), repo[dest].rev(), state, external
429
429
430 def pullrebase(orig, ui, repo, *args, **opts):
430 def pullrebase(orig, ui, repo, *args, **opts):
431 'Call rebase after pull if the latter has been invoked with --rebase'
431 'Call rebase after pull if the latter has been invoked with --rebase'
432 if opts.get('rebase'):
432 if opts.get('rebase'):
433 if opts.get('update'):
433 if opts.get('update'):
434 del opts['update']
434 del opts['update']
435 ui.debug(_('--update and --rebase are not compatible, ignoring '
435 ui.debug(_('--update and --rebase are not compatible, ignoring '
436 'the update flag\n'))
436 'the update flag\n'))
437
437
438 cmdutil.bail_if_changed(repo)
438 cmdutil.bail_if_changed(repo)
439 revsprepull = len(repo)
439 revsprepull = len(repo)
440 orig(ui, repo, *args, **opts)
440 orig(ui, repo, *args, **opts)
441 revspostpull = len(repo)
441 revspostpull = len(repo)
442 if revspostpull > revsprepull:
442 if revspostpull > revsprepull:
443 rebase(ui, repo, **opts)
443 rebase(ui, repo, **opts)
444 branch = repo[None].branch()
444 branch = repo[None].branch()
445 dest = repo[branch].rev()
445 dest = repo[branch].rev()
446 if dest != repo['.'].rev():
446 if dest != repo['.'].rev():
447 # there was nothing to rebase we force an update
447 # there was nothing to rebase we force an update
448 merge.update(repo, dest, False, False, False)
448 merge.update(repo, dest, False, False, False)
449 else:
449 else:
450 orig(ui, repo, *args, **opts)
450 orig(ui, repo, *args, **opts)
451
451
452 def uisetup(ui):
452 def uisetup(ui):
453 'Replace pull with a decorator to provide --rebase option'
453 'Replace pull with a decorator to provide --rebase option'
454 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
454 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
455 entry[1].append(('', 'rebase', None,
455 entry[1].append(('', 'rebase', None,
456 _("rebase working directory to branch head"))
456 _("rebase working directory to branch head"))
457 )
457 )
458
458
459 cmdtable = {
459 cmdtable = {
460 "rebase":
460 "rebase":
461 (rebase,
461 (rebase,
462 [
462 [
463 ('s', 'source', '', _('rebase from a given revision')),
463 ('s', 'source', '', _('rebase from a given revision')),
464 ('b', 'base', '', _('rebase from the base of a given revision')),
464 ('b', 'base', '', _('rebase from the base of a given revision')),
465 ('d', 'dest', '', _('rebase onto a given revision')),
465 ('d', 'dest', '', _('rebase onto a given revision')),
466 ('', 'collapse', False, _('collapse the rebased revisions')),
466 ('', 'collapse', False, _('collapse the rebased revisions')),
467 ('', 'keep', False, _('keep original revisions')),
467 ('', 'keep', False, _('keep original revisions')),
468 ('', 'keepbranches', False, _('keep original branches')),
468 ('', 'keepbranches', False, _('keep original branches')),
469 ('c', 'continue', False, _('continue an interrupted rebase')),
469 ('c', 'continue', False, _('continue an interrupted rebase')),
470 ('a', 'abort', False, _('abort an interrupted rebase')),] +
470 ('a', 'abort', False, _('abort an interrupted rebase')),] +
471 templateopts,
471 templateopts,
472 _('hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--keep] '
472 _('hg rebase [-s REV | -b REV] [-d REV] [--collapse] [--keep] '
473 '[--keepbranches] | [-c] | [-a]')),
473 '[--keepbranches] | [-c] | [-a]')),
474 }
474 }
@@ -1,602 +1,602 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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''patch transplanting tool
8 '''patch transplanting tool
9
9
10 This extension allows you to transplant patches from another branch.
10 This extension allows you to transplant patches from another branch.
11
11
12 Transplanted patches are recorded in .hg/transplant/transplants, as a
12 Transplanted patches are recorded in .hg/transplant/transplants, as a
13 map from a changeset hash to its hash in the source repository.
13 map from a changeset hash to its hash in the source repository.
14 '''
14 '''
15
15
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 import os, tempfile
17 import os, tempfile
18 from mercurial import bundlerepo, changegroup, cmdutil, hg, merge
18 from mercurial import bundlerepo, changegroup, cmdutil, hg, merge
19 from mercurial import patch, revlog, util, error
19 from mercurial import patch, revlog, util, error
20
20
21 class transplantentry:
21 class transplantentry:
22 def __init__(self, lnode, rnode):
22 def __init__(self, lnode, rnode):
23 self.lnode = lnode
23 self.lnode = lnode
24 self.rnode = rnode
24 self.rnode = rnode
25
25
26 class transplants:
26 class transplants:
27 def __init__(self, path=None, transplantfile=None, opener=None):
27 def __init__(self, path=None, transplantfile=None, opener=None):
28 self.path = path
28 self.path = path
29 self.transplantfile = transplantfile
29 self.transplantfile = transplantfile
30 self.opener = opener
30 self.opener = opener
31
31
32 if not opener:
32 if not opener:
33 self.opener = util.opener(self.path)
33 self.opener = util.opener(self.path)
34 self.transplants = []
34 self.transplants = []
35 self.dirty = False
35 self.dirty = False
36 self.read()
36 self.read()
37
37
38 def read(self):
38 def read(self):
39 abspath = os.path.join(self.path, self.transplantfile)
39 abspath = os.path.join(self.path, self.transplantfile)
40 if self.transplantfile and os.path.exists(abspath):
40 if self.transplantfile and os.path.exists(abspath):
41 for line in self.opener(self.transplantfile).read().splitlines():
41 for line in self.opener(self.transplantfile).read().splitlines():
42 lnode, rnode = map(revlog.bin, line.split(':'))
42 lnode, rnode = map(revlog.bin, line.split(':'))
43 self.transplants.append(transplantentry(lnode, rnode))
43 self.transplants.append(transplantentry(lnode, rnode))
44
44
45 def write(self):
45 def write(self):
46 if self.dirty and self.transplantfile:
46 if self.dirty and self.transplantfile:
47 if not os.path.isdir(self.path):
47 if not os.path.isdir(self.path):
48 os.mkdir(self.path)
48 os.mkdir(self.path)
49 fp = self.opener(self.transplantfile, 'w')
49 fp = self.opener(self.transplantfile, 'w')
50 for c in self.transplants:
50 for c in self.transplants:
51 l, r = map(revlog.hex, (c.lnode, c.rnode))
51 l, r = map(revlog.hex, (c.lnode, c.rnode))
52 fp.write(l + ':' + r + '\n')
52 fp.write(l + ':' + r + '\n')
53 fp.close()
53 fp.close()
54 self.dirty = False
54 self.dirty = False
55
55
56 def get(self, rnode):
56 def get(self, rnode):
57 return [t for t in self.transplants if t.rnode == rnode]
57 return [t for t in self.transplants if t.rnode == rnode]
58
58
59 def set(self, lnode, rnode):
59 def set(self, lnode, rnode):
60 self.transplants.append(transplantentry(lnode, rnode))
60 self.transplants.append(transplantentry(lnode, rnode))
61 self.dirty = True
61 self.dirty = True
62
62
63 def remove(self, transplant):
63 def remove(self, transplant):
64 del self.transplants[self.transplants.index(transplant)]
64 del self.transplants[self.transplants.index(transplant)]
65 self.dirty = True
65 self.dirty = True
66
66
67 class transplanter:
67 class transplanter:
68 def __init__(self, ui, repo):
68 def __init__(self, ui, repo):
69 self.ui = ui
69 self.ui = ui
70 self.path = repo.join('transplant')
70 self.path = repo.join('transplant')
71 self.opener = util.opener(self.path)
71 self.opener = util.opener(self.path)
72 self.transplants = transplants(self.path, 'transplants',
72 self.transplants = transplants(self.path, 'transplants',
73 opener=self.opener)
73 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 = sorted(revmap)
92 revs = sorted(revmap)
93 p1, p2 = repo.dirstate.parents()
93 p1, p2 = repo.dirstate.parents()
94 pulls = []
94 pulls = []
95 diffopts = patch.diffopts(self.ui, opts)
95 diffopts = patch.diffopts(self.ui, opts)
96 diffopts.git = True
96 diffopts.git = True
97
97
98 lock = wlock = None
98 lock = wlock = None
99 try:
99 try:
100 wlock = repo.wlock()
100 wlock = repo.wlock()
101 lock = repo.lock()
101 lock = repo.lock()
102 for rev in revs:
102 for rev in revs:
103 node = revmap[rev]
103 node = revmap[rev]
104 revstr = '%s:%s' % (rev, revlog.short(node))
104 revstr = '%s:%s' % (rev, revlog.short(node))
105
105
106 if self.applied(repo, node, p1):
106 if self.applied(repo, node, p1):
107 self.ui.warn(_('skipping already applied revision %s\n') %
107 self.ui.warn(_('skipping already applied revision %s\n') %
108 revstr)
108 revstr)
109 continue
109 continue
110
110
111 parents = source.changelog.parents(node)
111 parents = source.changelog.parents(node)
112 if not opts.get('filter'):
112 if not opts.get('filter'):
113 # If the changeset parent is the same as the
113 # If the changeset parent is the same as the
114 # wdir's parent, just pull it.
114 # wdir's parent, just pull it.
115 if parents[0] == p1:
115 if parents[0] == p1:
116 pulls.append(node)
116 pulls.append(node)
117 p1 = node
117 p1 = node
118 continue
118 continue
119 if pulls:
119 if pulls:
120 if source != repo:
120 if source != repo:
121 repo.pull(source, heads=pulls)
121 repo.pull(source, heads=pulls)
122 merge.update(repo, pulls[-1], False, False, None)
122 merge.update(repo, pulls[-1], False, False, None)
123 p1, p2 = repo.dirstate.parents()
123 p1, p2 = repo.dirstate.parents()
124 pulls = []
124 pulls = []
125
125
126 domerge = False
126 domerge = False
127 if node in merges:
127 if node in merges:
128 # pulling all the merge revs at once would mean we
128 # pulling all the merge revs at once would mean we
129 # couldn't transplant after the latest even if
129 # couldn't transplant after the latest even if
130 # transplants before them fail.
130 # transplants before them fail.
131 domerge = True
131 domerge = True
132 if not hasnode(repo, node):
132 if not hasnode(repo, node):
133 repo.pull(source, heads=[node])
133 repo.pull(source, heads=[node])
134
134
135 if parents[1] != revlog.nullid:
135 if parents[1] != revlog.nullid:
136 self.ui.note(_('skipping merge changeset %s:%s\n')
136 self.ui.note(_('skipping merge changeset %s:%s\n')
137 % (rev, revlog.short(node)))
137 % (rev, revlog.short(node)))
138 patchfile = None
138 patchfile = None
139 else:
139 else:
140 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
140 fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
141 fp = os.fdopen(fd, 'w')
141 fp = os.fdopen(fd, 'w')
142 gen = patch.diff(source, parents[0], node, opts=diffopts)
142 gen = patch.diff(source, parents[0], node, opts=diffopts)
143 for chunk in gen:
143 for chunk in gen:
144 fp.write(chunk)
144 fp.write(chunk)
145 fp.close()
145 fp.close()
146
146
147 del revmap[rev]
147 del revmap[rev]
148 if patchfile or domerge:
148 if patchfile or domerge:
149 try:
149 try:
150 n = self.applyone(repo, node,
150 n = self.applyone(repo, node,
151 source.changelog.read(node),
151 source.changelog.read(node),
152 patchfile, merge=domerge,
152 patchfile, merge=domerge,
153 log=opts.get('log'),
153 log=opts.get('log'),
154 filter=opts.get('filter'))
154 filter=opts.get('filter'))
155 if n and domerge:
155 if n and domerge:
156 self.ui.status(_('%s merged at %s\n') % (revstr,
156 self.ui.status(_('%s merged at %s\n') % (revstr,
157 revlog.short(n)))
157 revlog.short(n)))
158 elif n:
158 elif n:
159 self.ui.status(_('%s transplanted to %s\n')
159 self.ui.status(_('%s transplanted to %s\n')
160 % (revlog.short(node),
160 % (revlog.short(node),
161 revlog.short(n)))
161 revlog.short(n)))
162 finally:
162 finally:
163 if patchfile:
163 if patchfile:
164 os.unlink(patchfile)
164 os.unlink(patchfile)
165 if pulls:
165 if pulls:
166 repo.pull(source, heads=pulls)
166 repo.pull(source, heads=pulls)
167 merge.update(repo, pulls[-1], False, False, None)
167 merge.update(repo, pulls[-1], False, False, None)
168 finally:
168 finally:
169 self.saveseries(revmap, merges)
169 self.saveseries(revmap, merges)
170 self.transplants.write()
170 self.transplants.write()
171 lock.release()
171 lock.release()
172 wlock.release()
172 wlock.release()
173
173
174 def filter(self, filter, changelog, patchfile):
174 def filter(self, filter, changelog, patchfile):
175 '''arbitrarily rewrite changeset before applying it'''
175 '''arbitrarily rewrite changeset before applying it'''
176
176
177 self.ui.status(_('filtering %s\n') % patchfile)
177 self.ui.status(_('filtering %s\n') % patchfile)
178 user, date, msg = (changelog[1], changelog[2], changelog[4])
178 user, date, msg = (changelog[1], changelog[2], changelog[4])
179
179
180 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
180 fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
181 fp = os.fdopen(fd, 'w')
181 fp = os.fdopen(fd, 'w')
182 fp.write("# HG changeset patch\n")
182 fp.write("# HG changeset patch\n")
183 fp.write("# User %s\n" % user)
183 fp.write("# User %s\n" % user)
184 fp.write("# Date %d %d\n" % date)
184 fp.write("# Date %d %d\n" % date)
185 fp.write(changelog[4])
185 fp.write(changelog[4])
186 fp.close()
186 fp.close()
187
187
188 try:
188 try:
189 util.system('%s %s %s' % (filter, util.shellquote(headerfile),
189 util.system('%s %s %s' % (filter, util.shellquote(headerfile),
190 util.shellquote(patchfile)),
190 util.shellquote(patchfile)),
191 environ={'HGUSER': changelog[1]},
191 environ={'HGUSER': changelog[1]},
192 onerr=util.Abort, errprefix=_('filter failed'))
192 onerr=util.Abort, errprefix=_('filter failed'))
193 user, date, msg = self.parselog(file(headerfile))[1:4]
193 user, date, msg = self.parselog(file(headerfile))[1:4]
194 finally:
194 finally:
195 os.unlink(headerfile)
195 os.unlink(headerfile)
196
196
197 return (user, date, msg)
197 return (user, date, msg)
198
198
199 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
199 def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
200 filter=None):
200 filter=None):
201 '''apply the patch in patchfile to the repository as a transplant'''
201 '''apply the patch in patchfile to the repository as a transplant'''
202 (manifest, user, (time, timezone), files, message) = cl[:5]
202 (manifest, user, (time, timezone), files, message) = cl[:5]
203 date = "%d %d" % (time, timezone)
203 date = "%d %d" % (time, timezone)
204 extra = {'transplant_source': node}
204 extra = {'transplant_source': node}
205 if filter:
205 if filter:
206 (user, date, message) = self.filter(filter, cl, patchfile)
206 (user, date, message) = self.filter(filter, cl, patchfile)
207
207
208 if log:
208 if log:
209 message += '\n(transplanted from %s)' % revlog.hex(node)
209 message += '\n(transplanted from %s)' % revlog.hex(node)
210
210
211 self.ui.status(_('applying %s\n') % revlog.short(node))
211 self.ui.status(_('applying %s\n') % revlog.short(node))
212 self.ui.note('%s %s\n%s\n' % (user, date, message))
212 self.ui.note('%s %s\n%s\n' % (user, date, message))
213
213
214 if not patchfile and not merge:
214 if not patchfile and not merge:
215 raise util.Abort(_('can only omit patchfile if merging'))
215 raise util.Abort(_('can only omit patchfile if merging'))
216 if patchfile:
216 if patchfile:
217 try:
217 try:
218 files = {}
218 files = {}
219 try:
219 try:
220 patch.patch(patchfile, self.ui, cwd=repo.root,
220 patch.patch(patchfile, self.ui, cwd=repo.root,
221 files=files)
221 files=files)
222 if not files:
222 if not files:
223 self.ui.warn(_('%s: empty changeset')
223 self.ui.warn(_('%s: empty changeset')
224 % revlog.hex(node))
224 % revlog.hex(node))
225 return None
225 return None
226 finally:
226 finally:
227 files = patch.updatedir(self.ui, repo, files)
227 files = patch.updatedir(self.ui, repo, files)
228 except Exception, inst:
228 except Exception, inst:
229 if filter:
229 if filter:
230 os.unlink(patchfile)
230 os.unlink(patchfile)
231 seriespath = os.path.join(self.path, 'series')
231 seriespath = os.path.join(self.path, 'series')
232 if os.path.exists(seriespath):
232 if os.path.exists(seriespath):
233 os.unlink(seriespath)
233 os.unlink(seriespath)
234 p1 = repo.dirstate.parents()[0]
234 p1 = repo.dirstate.parents()[0]
235 p2 = node
235 p2 = node
236 self.log(user, date, message, p1, p2, merge=merge)
236 self.log(user, date, message, p1, p2, merge=merge)
237 self.ui.write(str(inst) + '\n')
237 self.ui.write(str(inst) + '\n')
238 raise util.Abort(_('Fix up the merge and run '
238 raise util.Abort(_('Fix up the merge and run '
239 'hg transplant --continue'))
239 'hg transplant --continue'))
240 else:
240 else:
241 files = None
241 files = None
242 if merge:
242 if merge:
243 p1, p2 = repo.dirstate.parents()
243 p1, p2 = repo.dirstate.parents()
244 repo.dirstate.setparents(p1, node)
244 repo.dirstate.setparents(p1, node)
245
245
246 n = repo.commit(files, message, user, date, extra=extra)
246 n = repo.commit(files, message, user, date, extra=extra)
247 if not merge:
247 if not merge:
248 self.transplants.set(n, node)
248 self.transplants.set(n, node)
249
249
250 return n
250 return n
251
251
252 def resume(self, repo, source, opts=None):
252 def resume(self, repo, source, opts=None):
253 '''recover last transaction and apply remaining changesets'''
253 '''recover last transaction and apply remaining changesets'''
254 if os.path.exists(os.path.join(self.path, 'journal')):
254 if os.path.exists(os.path.join(self.path, 'journal')):
255 n, node = self.recover(repo)
255 n, node = self.recover(repo)
256 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
256 self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
257 revlog.short(n)))
257 revlog.short(n)))
258 seriespath = os.path.join(self.path, 'series')
258 seriespath = os.path.join(self.path, 'series')
259 if not os.path.exists(seriespath):
259 if not os.path.exists(seriespath):
260 self.transplants.write()
260 self.transplants.write()
261 return
261 return
262 nodes, merges = self.readseries()
262 nodes, merges = self.readseries()
263 revmap = {}
263 revmap = {}
264 for n in nodes:
264 for n in nodes:
265 revmap[source.changelog.rev(n)] = n
265 revmap[source.changelog.rev(n)] = n
266 os.unlink(seriespath)
266 os.unlink(seriespath)
267
267
268 self.apply(repo, source, revmap, merges, opts)
268 self.apply(repo, source, revmap, merges, opts)
269
269
270 def recover(self, repo):
270 def recover(self, repo):
271 '''commit working directory using journal metadata'''
271 '''commit working directory using journal metadata'''
272 node, user, date, message, parents = self.readlog()
272 node, user, date, message, parents = self.readlog()
273 merge = len(parents) == 2
273 merge = len(parents) == 2
274
274
275 if not user or not date or not message or not parents[0]:
275 if not user or not date or not message or not parents[0]:
276 raise util.Abort(_('transplant log file is corrupt'))
276 raise util.Abort(_('transplant log file is corrupt'))
277
277
278 extra = {'transplant_source': node}
278 extra = {'transplant_source': node}
279 wlock = repo.wlock()
279 wlock = repo.wlock()
280 try:
280 try:
281 p1, p2 = repo.dirstate.parents()
281 p1, p2 = repo.dirstate.parents()
282 if p1 != parents[0]:
282 if p1 != parents[0]:
283 raise util.Abort(
283 raise util.Abort(
284 _('working dir not at transplant parent %s') %
284 _('working dir not at transplant parent %s') %
285 revlog.hex(parents[0]))
285 revlog.hex(parents[0]))
286 if merge:
286 if merge:
287 repo.dirstate.setparents(p1, parents[1])
287 repo.dirstate.setparents(p1, parents[1])
288 n = repo.commit(None, message, user, date, extra=extra)
288 n = repo.commit(None, message, user, date, extra=extra)
289 if not n:
289 if not n:
290 raise util.Abort(_('commit failed'))
290 raise util.Abort(_('commit failed'))
291 if not merge:
291 if not merge:
292 self.transplants.set(n, node)
292 self.transplants.set(n, node)
293 self.unlog()
293 self.unlog()
294
294
295 return n, node
295 return n, node
296 finally:
296 finally:
297 wlock.release()
297 wlock.release()
298
298
299 def readseries(self):
299 def readseries(self):
300 nodes = []
300 nodes = []
301 merges = []
301 merges = []
302 cur = nodes
302 cur = nodes
303 for line in self.opener('series').read().splitlines():
303 for line in self.opener('series').read().splitlines():
304 if line.startswith('# Merges'):
304 if line.startswith('# Merges'):
305 cur = merges
305 cur = merges
306 continue
306 continue
307 cur.append(revlog.bin(line))
307 cur.append(revlog.bin(line))
308
308
309 return (nodes, merges)
309 return (nodes, merges)
310
310
311 def saveseries(self, revmap, merges):
311 def saveseries(self, revmap, merges):
312 if not revmap:
312 if not revmap:
313 return
313 return
314
314
315 if not os.path.isdir(self.path):
315 if not os.path.isdir(self.path):
316 os.mkdir(self.path)
316 os.mkdir(self.path)
317 series = self.opener('series', 'w')
317 series = self.opener('series', 'w')
318 for rev in sorted(revmap):
318 for rev in sorted(revmap):
319 series.write(revlog.hex(revmap[rev]) + '\n')
319 series.write(revlog.hex(revmap[rev]) + '\n')
320 if merges:
320 if merges:
321 series.write('# Merges\n')
321 series.write('# Merges\n')
322 for m in merges:
322 for m in merges:
323 series.write(revlog.hex(m) + '\n')
323 series.write(revlog.hex(m) + '\n')
324 series.close()
324 series.close()
325
325
326 def parselog(self, fp):
326 def parselog(self, fp):
327 parents = []
327 parents = []
328 message = []
328 message = []
329 node = revlog.nullid
329 node = revlog.nullid
330 inmsg = False
330 inmsg = False
331 for line in fp.read().splitlines():
331 for line in fp.read().splitlines():
332 if inmsg:
332 if inmsg:
333 message.append(line)
333 message.append(line)
334 elif line.startswith('# User '):
334 elif line.startswith('# User '):
335 user = line[7:]
335 user = line[7:]
336 elif line.startswith('# Date '):
336 elif line.startswith('# Date '):
337 date = line[7:]
337 date = line[7:]
338 elif line.startswith('# Node ID '):
338 elif line.startswith('# Node ID '):
339 node = revlog.bin(line[10:])
339 node = revlog.bin(line[10:])
340 elif line.startswith('# Parent '):
340 elif line.startswith('# Parent '):
341 parents.append(revlog.bin(line[9:]))
341 parents.append(revlog.bin(line[9:]))
342 elif not line.startswith('#'):
342 elif not line.startswith('#'):
343 inmsg = True
343 inmsg = True
344 message.append(line)
344 message.append(line)
345 return (node, user, date, '\n'.join(message), parents)
345 return (node, user, date, '\n'.join(message), parents)
346
346
347 def log(self, user, date, message, p1, p2, merge=False):
347 def log(self, user, date, message, p1, p2, merge=False):
348 '''journal changelog metadata for later recover'''
348 '''journal changelog metadata for later recover'''
349
349
350 if not os.path.isdir(self.path):
350 if not os.path.isdir(self.path):
351 os.mkdir(self.path)
351 os.mkdir(self.path)
352 fp = self.opener('journal', 'w')
352 fp = self.opener('journal', 'w')
353 fp.write('# User %s\n' % user)
353 fp.write('# User %s\n' % user)
354 fp.write('# Date %s\n' % date)
354 fp.write('# Date %s\n' % date)
355 fp.write('# Node ID %s\n' % revlog.hex(p2))
355 fp.write('# Node ID %s\n' % revlog.hex(p2))
356 fp.write('# Parent ' + revlog.hex(p1) + '\n')
356 fp.write('# Parent ' + revlog.hex(p1) + '\n')
357 if merge:
357 if merge:
358 fp.write('# Parent ' + revlog.hex(p2) + '\n')
358 fp.write('# Parent ' + revlog.hex(p2) + '\n')
359 fp.write(message.rstrip() + '\n')
359 fp.write(message.rstrip() + '\n')
360 fp.close()
360 fp.close()
361
361
362 def readlog(self):
362 def readlog(self):
363 return self.parselog(self.opener('journal'))
363 return self.parselog(self.opener('journal'))
364
364
365 def unlog(self):
365 def unlog(self):
366 '''remove changelog journal'''
366 '''remove changelog journal'''
367 absdst = os.path.join(self.path, 'journal')
367 absdst = os.path.join(self.path, 'journal')
368 if os.path.exists(absdst):
368 if os.path.exists(absdst):
369 os.unlink(absdst)
369 os.unlink(absdst)
370
370
371 def transplantfilter(self, repo, source, root):
371 def transplantfilter(self, repo, source, root):
372 def matchfn(node):
372 def matchfn(node):
373 if self.applied(repo, node, root):
373 if self.applied(repo, node, root):
374 return False
374 return False
375 if source.changelog.parents(node)[1] != revlog.nullid:
375 if source.changelog.parents(node)[1] != revlog.nullid:
376 return False
376 return False
377 extra = source.changelog.read(node)[5]
377 extra = source.changelog.read(node)[5]
378 cnode = extra.get('transplant_source')
378 cnode = extra.get('transplant_source')
379 if cnode and self.applied(repo, cnode, root):
379 if cnode and self.applied(repo, cnode, root):
380 return False
380 return False
381 return True
381 return True
382
382
383 return matchfn
383 return matchfn
384
384
385 def hasnode(repo, node):
385 def hasnode(repo, node):
386 try:
386 try:
387 return repo.changelog.rev(node) != None
387 return repo.changelog.rev(node) != None
388 except error.RevlogError:
388 except error.RevlogError:
389 return False
389 return False
390
390
391 def browserevs(ui, repo, nodes, opts):
391 def browserevs(ui, repo, nodes, opts):
392 '''interactively transplant changesets'''
392 '''interactively transplant changesets'''
393 def browsehelp(ui):
393 def browsehelp(ui):
394 ui.write('y: transplant this changeset\n'
394 ui.write('y: transplant this changeset\n'
395 'n: skip this changeset\n'
395 'n: skip this changeset\n'
396 'm: merge at this changeset\n'
396 'm: merge at this changeset\n'
397 'p: show patch\n'
397 'p: show patch\n'
398 'c: commit selected changesets\n'
398 'c: commit selected changesets\n'
399 'q: cancel transplant\n'
399 'q: cancel transplant\n'
400 '?: show this help\n')
400 '?: show this help\n')
401
401
402 displayer = cmdutil.show_changeset(ui, repo, opts)
402 displayer = cmdutil.show_changeset(ui, repo, opts)
403 transplants = []
403 transplants = []
404 merges = []
404 merges = []
405 for node in nodes:
405 for node in nodes:
406 displayer.show(repo[node])
406 displayer.show(repo[node])
407 action = None
407 action = None
408 while not action:
408 while not action:
409 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
409 action = ui.prompt(_('apply changeset? [ynmpcq?]:'))
410 if action == '?':
410 if action == '?':
411 browsehelp(ui)
411 browsehelp(ui)
412 action = None
412 action = None
413 elif action == 'p':
413 elif action == 'p':
414 parent = repo.changelog.parents(node)[0]
414 parent = repo.changelog.parents(node)[0]
415 for chunk in patch.diff(repo, parent, node):
415 for chunk in patch.diff(repo, parent, node):
416 repo.ui.write(chunk)
416 ui.write(chunk)
417 action = None
417 action = None
418 elif action not in ('y', 'n', 'm', 'c', 'q'):
418 elif action not in ('y', 'n', 'm', 'c', 'q'):
419 ui.write('no such option\n')
419 ui.write('no such option\n')
420 action = None
420 action = None
421 if action == 'y':
421 if action == 'y':
422 transplants.append(node)
422 transplants.append(node)
423 elif action == 'm':
423 elif action == 'm':
424 merges.append(node)
424 merges.append(node)
425 elif action == 'c':
425 elif action == 'c':
426 break
426 break
427 elif action == 'q':
427 elif action == 'q':
428 transplants = ()
428 transplants = ()
429 merges = ()
429 merges = ()
430 break
430 break
431 return (transplants, merges)
431 return (transplants, merges)
432
432
433 def transplant(ui, repo, *revs, **opts):
433 def transplant(ui, repo, *revs, **opts):
434 '''transplant changesets from another branch
434 '''transplant changesets from another branch
435
435
436 Selected changesets will be applied on top of the current working
436 Selected changesets will be applied on top of the current working
437 directory with the log of the original changeset. If --log is
437 directory with the log of the original changeset. If --log is
438 specified, log messages will have a comment appended of the form:
438 specified, log messages will have a comment appended of the form:
439
439
440 (transplanted from CHANGESETHASH)
440 (transplanted from CHANGESETHASH)
441
441
442 You can rewrite the changelog message with the --filter option.
442 You can rewrite the changelog message with the --filter option.
443 Its argument will be invoked with the current changelog message as
443 Its argument will be invoked with the current changelog message as
444 $1 and the patch as $2.
444 $1 and the patch as $2.
445
445
446 If --source/-s is specified, selects changesets from the named
446 If --source/-s is specified, selects changesets from the named
447 repository. If --branch/-b is specified, selects changesets from
447 repository. If --branch/-b is specified, selects changesets from
448 the branch holding the named revision, up to that revision. If
448 the branch holding the named revision, up to that revision. If
449 --all/-a is specified, all changesets on the branch will be
449 --all/-a is specified, all changesets on the branch will be
450 transplanted, otherwise you will be prompted to select the
450 transplanted, otherwise you will be prompted to select the
451 changesets you want.
451 changesets you want.
452
452
453 hg transplant --branch REVISION --all will rebase the selected
453 hg transplant --branch REVISION --all will rebase the selected
454 branch (up to the named revision) onto your current working
454 branch (up to the named revision) onto your current working
455 directory.
455 directory.
456
456
457 You can optionally mark selected transplanted changesets as merge
457 You can optionally mark selected transplanted changesets as merge
458 changesets. You will not be prompted to transplant any ancestors
458 changesets. You will not be prompted to transplant any ancestors
459 of a merged transplant, and you can merge descendants of them
459 of a merged transplant, and you can merge descendants of them
460 normally instead of transplanting them.
460 normally instead of transplanting them.
461
461
462 If no merges or revisions are provided, hg transplant will start
462 If no merges or revisions are provided, hg transplant will start
463 an interactive changeset browser.
463 an interactive changeset browser.
464
464
465 If a changeset application fails, you can fix the merge by hand
465 If a changeset application fails, you can fix the merge by hand
466 and then resume where you left off by calling hg transplant
466 and then resume where you left off by calling hg transplant
467 --continue/-c.
467 --continue/-c.
468 '''
468 '''
469 def getremotechanges(repo, url):
469 def getremotechanges(repo, url):
470 sourcerepo = ui.expandpath(url)
470 sourcerepo = ui.expandpath(url)
471 source = hg.repository(ui, sourcerepo)
471 source = hg.repository(ui, sourcerepo)
472 common, incoming, rheads = repo.findcommonincoming(source, force=True)
472 common, incoming, rheads = repo.findcommonincoming(source, force=True)
473 if not incoming:
473 if not incoming:
474 return (source, None, None)
474 return (source, None, None)
475
475
476 bundle = None
476 bundle = None
477 if not source.local():
477 if not source.local():
478 if source.capable('changegroupsubset'):
478 if source.capable('changegroupsubset'):
479 cg = source.changegroupsubset(incoming, rheads, 'incoming')
479 cg = source.changegroupsubset(incoming, rheads, 'incoming')
480 else:
480 else:
481 cg = source.changegroup(incoming, 'incoming')
481 cg = source.changegroup(incoming, 'incoming')
482 bundle = changegroup.writebundle(cg, None, 'HG10UN')
482 bundle = changegroup.writebundle(cg, None, 'HG10UN')
483 source = bundlerepo.bundlerepository(ui, repo.root, bundle)
483 source = bundlerepo.bundlerepository(ui, repo.root, bundle)
484
484
485 return (source, incoming, bundle)
485 return (source, incoming, bundle)
486
486
487 def incwalk(repo, incoming, branches, match=util.always):
487 def incwalk(repo, incoming, branches, match=util.always):
488 if not branches:
488 if not branches:
489 branches=None
489 branches=None
490 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
490 for node in repo.changelog.nodesbetween(incoming, branches)[0]:
491 if match(node):
491 if match(node):
492 yield node
492 yield node
493
493
494 def transplantwalk(repo, root, branches, match=util.always):
494 def transplantwalk(repo, root, branches, match=util.always):
495 if not branches:
495 if not branches:
496 branches = repo.heads()
496 branches = repo.heads()
497 ancestors = []
497 ancestors = []
498 for branch in branches:
498 for branch in branches:
499 ancestors.append(repo.changelog.ancestor(root, branch))
499 ancestors.append(repo.changelog.ancestor(root, branch))
500 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
500 for node in repo.changelog.nodesbetween(ancestors, branches)[0]:
501 if match(node):
501 if match(node):
502 yield node
502 yield node
503
503
504 def checkopts(opts, revs):
504 def checkopts(opts, revs):
505 if opts.get('continue'):
505 if opts.get('continue'):
506 if filter(lambda opt: opts.get(opt), ('branch', 'all', 'merge')):
506 if filter(lambda opt: opts.get(opt), ('branch', 'all', 'merge')):
507 raise util.Abort(_('--continue is incompatible with '
507 raise util.Abort(_('--continue is incompatible with '
508 'branch, all or merge'))
508 'branch, all or merge'))
509 return
509 return
510 if not (opts.get('source') or revs or
510 if not (opts.get('source') or revs or
511 opts.get('merge') or opts.get('branch')):
511 opts.get('merge') or opts.get('branch')):
512 raise util.Abort(_('no source URL, branch tag or revision '
512 raise util.Abort(_('no source URL, branch tag or revision '
513 'list provided'))
513 'list provided'))
514 if opts.get('all'):
514 if opts.get('all'):
515 if not opts.get('branch'):
515 if not opts.get('branch'):
516 raise util.Abort(_('--all requires a branch revision'))
516 raise util.Abort(_('--all requires a branch revision'))
517 if revs:
517 if revs:
518 raise util.Abort(_('--all is incompatible with a '
518 raise util.Abort(_('--all is incompatible with a '
519 'revision list'))
519 'revision list'))
520
520
521 checkopts(opts, revs)
521 checkopts(opts, revs)
522
522
523 if not opts.get('log'):
523 if not opts.get('log'):
524 opts['log'] = ui.config('transplant', 'log')
524 opts['log'] = ui.config('transplant', 'log')
525 if not opts.get('filter'):
525 if not opts.get('filter'):
526 opts['filter'] = ui.config('transplant', 'filter')
526 opts['filter'] = ui.config('transplant', 'filter')
527
527
528 tp = transplanter(ui, repo)
528 tp = transplanter(ui, repo)
529
529
530 p1, p2 = repo.dirstate.parents()
530 p1, p2 = repo.dirstate.parents()
531 if len(repo) > 0 and p1 == revlog.nullid:
531 if len(repo) > 0 and p1 == revlog.nullid:
532 raise util.Abort(_('no revision checked out'))
532 raise util.Abort(_('no revision checked out'))
533 if not opts.get('continue'):
533 if not opts.get('continue'):
534 if p2 != revlog.nullid:
534 if p2 != revlog.nullid:
535 raise util.Abort(_('outstanding uncommitted merges'))
535 raise util.Abort(_('outstanding uncommitted merges'))
536 m, a, r, d = repo.status()[:4]
536 m, a, r, d = repo.status()[:4]
537 if m or a or r or d:
537 if m or a or r or d:
538 raise util.Abort(_('outstanding local changes'))
538 raise util.Abort(_('outstanding local changes'))
539
539
540 bundle = None
540 bundle = None
541 source = opts.get('source')
541 source = opts.get('source')
542 if source:
542 if source:
543 (source, incoming, bundle) = getremotechanges(repo, source)
543 (source, incoming, bundle) = getremotechanges(repo, source)
544 else:
544 else:
545 source = repo
545 source = repo
546
546
547 try:
547 try:
548 if opts.get('continue'):
548 if opts.get('continue'):
549 tp.resume(repo, source, opts)
549 tp.resume(repo, source, opts)
550 return
550 return
551
551
552 tf=tp.transplantfilter(repo, source, p1)
552 tf=tp.transplantfilter(repo, source, p1)
553 if opts.get('prune'):
553 if opts.get('prune'):
554 prune = [source.lookup(r)
554 prune = [source.lookup(r)
555 for r in cmdutil.revrange(source, opts.get('prune'))]
555 for r in cmdutil.revrange(source, opts.get('prune'))]
556 matchfn = lambda x: tf(x) and x not in prune
556 matchfn = lambda x: tf(x) and x not in prune
557 else:
557 else:
558 matchfn = tf
558 matchfn = tf
559 branches = map(source.lookup, opts.get('branch', ()))
559 branches = map(source.lookup, opts.get('branch', ()))
560 merges = map(source.lookup, opts.get('merge', ()))
560 merges = map(source.lookup, opts.get('merge', ()))
561 revmap = {}
561 revmap = {}
562 if revs:
562 if revs:
563 for r in cmdutil.revrange(source, revs):
563 for r in cmdutil.revrange(source, revs):
564 revmap[int(r)] = source.lookup(r)
564 revmap[int(r)] = source.lookup(r)
565 elif opts.get('all') or not merges:
565 elif opts.get('all') or not merges:
566 if source != repo:
566 if source != repo:
567 alltransplants = incwalk(source, incoming, branches,
567 alltransplants = incwalk(source, incoming, branches,
568 match=matchfn)
568 match=matchfn)
569 else:
569 else:
570 alltransplants = transplantwalk(source, p1, branches,
570 alltransplants = transplantwalk(source, p1, branches,
571 match=matchfn)
571 match=matchfn)
572 if opts.get('all'):
572 if opts.get('all'):
573 revs = alltransplants
573 revs = alltransplants
574 else:
574 else:
575 revs, newmerges = browserevs(ui, source, alltransplants, opts)
575 revs, newmerges = browserevs(ui, source, alltransplants, opts)
576 merges.extend(newmerges)
576 merges.extend(newmerges)
577 for r in revs:
577 for r in revs:
578 revmap[source.changelog.rev(r)] = r
578 revmap[source.changelog.rev(r)] = r
579 for r in merges:
579 for r in merges:
580 revmap[source.changelog.rev(r)] = r
580 revmap[source.changelog.rev(r)] = r
581
581
582 tp.apply(repo, source, revmap, merges, opts)
582 tp.apply(repo, source, revmap, merges, opts)
583 finally:
583 finally:
584 if bundle:
584 if bundle:
585 source.close()
585 source.close()
586 os.unlink(bundle)
586 os.unlink(bundle)
587
587
588 cmdtable = {
588 cmdtable = {
589 "transplant":
589 "transplant":
590 (transplant,
590 (transplant,
591 [('s', 'source', '', _('pull patches from REPOSITORY')),
591 [('s', 'source', '', _('pull patches from REPOSITORY')),
592 ('b', 'branch', [], _('pull patches from branch BRANCH')),
592 ('b', 'branch', [], _('pull patches from branch BRANCH')),
593 ('a', 'all', None, _('pull all changesets up to BRANCH')),
593 ('a', 'all', None, _('pull all changesets up to BRANCH')),
594 ('p', 'prune', [], _('skip over REV')),
594 ('p', 'prune', [], _('skip over REV')),
595 ('m', 'merge', [], _('merge at REV')),
595 ('m', 'merge', [], _('merge at REV')),
596 ('', 'log', None, _('append transplant info to log message')),
596 ('', 'log', None, _('append transplant info to log message')),
597 ('c', 'continue', None, _('continue last transplant session '
597 ('c', 'continue', None, _('continue last transplant session '
598 'after repair')),
598 'after repair')),
599 ('', 'filter', '', _('filter changesets through FILTER'))],
599 ('', 'filter', '', _('filter changesets through FILTER'))],
600 _('hg transplant [-s REPOSITORY] [-b BRANCH [-a]] [-p REV] '
600 _('hg transplant [-s REPOSITORY] [-b BRANCH [-a]] [-p REV] '
601 '[-m REV] [REV]...'))
601 '[-m REV] [REV]...'))
602 }
602 }
@@ -1,3462 +1,3462 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, textwrap, subprocess, difflib, time
11 import os, re, sys, textwrap, subprocess, difflib, time
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 import patch, help, mdiff, tempfile, url, encoding
13 import patch, help, mdiff, tempfile, url, encoding
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
17
17
18 # Commands start here, listed alphabetically
18 # Commands start here, listed alphabetically
19
19
20 def add(ui, repo, *pats, **opts):
20 def add(ui, repo, *pats, **opts):
21 """add the specified files on the next commit
21 """add the specified files on the next commit
22
22
23 Schedule files to be version controlled and added to the
23 Schedule files to be version controlled and added to the
24 repository.
24 repository.
25
25
26 The files will be added to the repository at the next commit. To
26 The files will be added to the repository at the next commit. To
27 undo an add before that, see hg revert.
27 undo an add before that, see hg revert.
28
28
29 If no names are given, add all files to the repository.
29 If no names are given, add all files to the repository.
30 """
30 """
31
31
32 rejected = None
32 rejected = None
33 exacts = {}
33 exacts = {}
34 names = []
34 names = []
35 m = cmdutil.match(repo, pats, opts)
35 m = cmdutil.match(repo, pats, opts)
36 m.bad = lambda x,y: True
36 m.bad = lambda x,y: True
37 for abs in repo.walk(m):
37 for abs in repo.walk(m):
38 if m.exact(abs):
38 if m.exact(abs):
39 if ui.verbose:
39 if ui.verbose:
40 ui.status(_('adding %s\n') % m.rel(abs))
40 ui.status(_('adding %s\n') % m.rel(abs))
41 names.append(abs)
41 names.append(abs)
42 exacts[abs] = 1
42 exacts[abs] = 1
43 elif abs not in repo.dirstate:
43 elif abs not in repo.dirstate:
44 ui.status(_('adding %s\n') % m.rel(abs))
44 ui.status(_('adding %s\n') % m.rel(abs))
45 names.append(abs)
45 names.append(abs)
46 if not opts.get('dry_run'):
46 if not opts.get('dry_run'):
47 rejected = repo.add(names)
47 rejected = repo.add(names)
48 rejected = [p for p in rejected if p in exacts]
48 rejected = [p for p in rejected if p in exacts]
49 return rejected and 1 or 0
49 return rejected and 1 or 0
50
50
51 def addremove(ui, repo, *pats, **opts):
51 def addremove(ui, repo, *pats, **opts):
52 """add all new files, delete all missing files
52 """add all new files, delete all missing files
53
53
54 Add all new files and remove all missing files from the
54 Add all new files and remove all missing files from the
55 repository.
55 repository.
56
56
57 New files are ignored if they match any of the patterns in
57 New files are ignored if they match any of the patterns in
58 .hgignore. As with add, these changes take effect at the next
58 .hgignore. As with add, these changes take effect at the next
59 commit.
59 commit.
60
60
61 Use the -s/--similarity option to detect renamed files. With a
61 Use the -s/--similarity option to detect renamed files. With a
62 parameter > 0, this compares every removed file with every added
62 parameter > 0, this compares every removed file with every added
63 file and records those similar enough as renames. This option
63 file and records those similar enough as renames. This option
64 takes a percentage between 0 (disabled) and 100 (files must be
64 takes a percentage between 0 (disabled) and 100 (files must be
65 identical) as its parameter. Detecting renamed files this way can
65 identical) as its parameter. Detecting renamed files this way can
66 be expensive.
66 be expensive.
67 """
67 """
68 try:
68 try:
69 sim = float(opts.get('similarity') or 0)
69 sim = float(opts.get('similarity') or 0)
70 except ValueError:
70 except ValueError:
71 raise util.Abort(_('similarity must be a number'))
71 raise util.Abort(_('similarity must be a number'))
72 if sim < 0 or sim > 100:
72 if sim < 0 or sim > 100:
73 raise util.Abort(_('similarity must be between 0 and 100'))
73 raise util.Abort(_('similarity must be between 0 and 100'))
74 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
74 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
75
75
76 def annotate(ui, repo, *pats, **opts):
76 def annotate(ui, repo, *pats, **opts):
77 """show changeset information per file line
77 """show changeset information per file line
78
78
79 List changes in files, showing the revision id responsible for
79 List changes in files, showing the revision id responsible for
80 each line
80 each line
81
81
82 This command is useful to discover who did a change or when a
82 This command is useful to discover who did a change or when a
83 change took place.
83 change took place.
84
84
85 Without the -a/--text option, annotate will avoid processing files
85 Without the -a/--text option, annotate will avoid processing files
86 it detects as binary. With -a, annotate will generate an
86 it detects as binary. With -a, annotate will generate an
87 annotation anyway, probably with undesirable results.
87 annotation anyway, probably with undesirable results.
88 """
88 """
89 datefunc = ui.quiet and util.shortdate or util.datestr
89 datefunc = ui.quiet and util.shortdate or util.datestr
90 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
91
91
92 if not pats:
92 if not pats:
93 raise util.Abort(_('at least one file name or pattern required'))
93 raise util.Abort(_('at least one file name or pattern required'))
94
94
95 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
95 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
96 ('number', lambda x: str(x[0].rev())),
96 ('number', lambda x: str(x[0].rev())),
97 ('changeset', lambda x: short(x[0].node())),
97 ('changeset', lambda x: short(x[0].node())),
98 ('date', getdate),
98 ('date', getdate),
99 ('follow', lambda x: x[0].path()),
99 ('follow', lambda x: x[0].path()),
100 ]
100 ]
101
101
102 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
102 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
103 and not opts.get('follow')):
103 and not opts.get('follow')):
104 opts['number'] = 1
104 opts['number'] = 1
105
105
106 linenumber = opts.get('line_number') is not None
106 linenumber = opts.get('line_number') is not None
107 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
108 raise util.Abort(_('at least one of -n/-c is required for -l'))
108 raise util.Abort(_('at least one of -n/-c is required for -l'))
109
109
110 funcmap = [func for op, func in opmap if opts.get(op)]
110 funcmap = [func for op, func in opmap if opts.get(op)]
111 if linenumber:
111 if linenumber:
112 lastfunc = funcmap[-1]
112 lastfunc = funcmap[-1]
113 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
113 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
114
114
115 ctx = repo[opts.get('rev')]
115 ctx = repo[opts.get('rev')]
116
116
117 m = cmdutil.match(repo, pats, opts)
117 m = cmdutil.match(repo, pats, opts)
118 for abs in ctx.walk(m):
118 for abs in ctx.walk(m):
119 fctx = ctx[abs]
119 fctx = ctx[abs]
120 if not opts.get('text') and util.binary(fctx.data()):
120 if not opts.get('text') and util.binary(fctx.data()):
121 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
122 continue
122 continue
123
123
124 lines = fctx.annotate(follow=opts.get('follow'),
124 lines = fctx.annotate(follow=opts.get('follow'),
125 linenumber=linenumber)
125 linenumber=linenumber)
126 pieces = []
126 pieces = []
127
127
128 for f in funcmap:
128 for f in funcmap:
129 l = [f(n) for n, dummy in lines]
129 l = [f(n) for n, dummy in lines]
130 if l:
130 if l:
131 ml = max(map(len, l))
131 ml = max(map(len, l))
132 pieces.append(["%*s" % (ml, x) for x in l])
132 pieces.append(["%*s" % (ml, x) for x in l])
133
133
134 if pieces:
134 if pieces:
135 for p, l in zip(zip(*pieces), lines):
135 for p, l in zip(zip(*pieces), lines):
136 ui.write("%s: %s" % (" ".join(p), l[1]))
136 ui.write("%s: %s" % (" ".join(p), l[1]))
137
137
138 def archive(ui, repo, dest, **opts):
138 def archive(ui, repo, dest, **opts):
139 '''create unversioned archive of a repository revision
139 '''create unversioned archive of a repository revision
140
140
141 By default, the revision used is the parent of the working
141 By default, the revision used is the parent of the working
142 directory; use -r/--rev to specify a different revision.
142 directory; use -r/--rev to specify a different revision.
143
143
144 To specify the type of archive to create, use -t/--type. Valid
144 To specify the type of archive to create, use -t/--type. Valid
145 types are:
145 types are:
146
146
147 "files" (default): a directory full of files
147 "files" (default): a directory full of files
148 "tar": tar archive, uncompressed
148 "tar": tar archive, uncompressed
149 "tbz2": tar archive, compressed using bzip2
149 "tbz2": tar archive, compressed using bzip2
150 "tgz": tar archive, compressed using gzip
150 "tgz": tar archive, compressed using gzip
151 "uzip": zip archive, uncompressed
151 "uzip": zip archive, uncompressed
152 "zip": zip archive, compressed using deflate
152 "zip": zip archive, compressed using deflate
153
153
154 The exact name of the destination archive or directory is given
154 The exact name of the destination archive or directory is given
155 using a format string; see 'hg help export' for details.
155 using a format string; see 'hg help export' for details.
156
156
157 Each member added to an archive file has a directory prefix
157 Each member added to an archive file has a directory prefix
158 prepended. Use -p/--prefix to specify a format string for the
158 prepended. Use -p/--prefix to specify a format string for the
159 prefix. The default is the basename of the archive, with suffixes
159 prefix. The default is the basename of the archive, with suffixes
160 removed.
160 removed.
161 '''
161 '''
162
162
163 ctx = repo[opts.get('rev')]
163 ctx = repo[opts.get('rev')]
164 if not ctx:
164 if not ctx:
165 raise util.Abort(_('no working directory: please specify a revision'))
165 raise util.Abort(_('no working directory: please specify a revision'))
166 node = ctx.node()
166 node = ctx.node()
167 dest = cmdutil.make_filename(repo, dest, node)
167 dest = cmdutil.make_filename(repo, dest, node)
168 if os.path.realpath(dest) == repo.root:
168 if os.path.realpath(dest) == repo.root:
169 raise util.Abort(_('repository root cannot be destination'))
169 raise util.Abort(_('repository root cannot be destination'))
170 matchfn = cmdutil.match(repo, [], opts)
170 matchfn = cmdutil.match(repo, [], opts)
171 kind = opts.get('type') or 'files'
171 kind = opts.get('type') or 'files'
172 prefix = opts.get('prefix')
172 prefix = opts.get('prefix')
173 if dest == '-':
173 if dest == '-':
174 if kind == 'files':
174 if kind == 'files':
175 raise util.Abort(_('cannot archive plain files to stdout'))
175 raise util.Abort(_('cannot archive plain files to stdout'))
176 dest = sys.stdout
176 dest = sys.stdout
177 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
177 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
178 prefix = cmdutil.make_filename(repo, prefix, node)
178 prefix = cmdutil.make_filename(repo, prefix, node)
179 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
180 matchfn, prefix)
180 matchfn, prefix)
181
181
182 def backout(ui, repo, node=None, rev=None, **opts):
182 def backout(ui, repo, node=None, rev=None, **opts):
183 '''reverse effect of earlier changeset
183 '''reverse effect of earlier changeset
184
184
185 Commit the backed out changes as a new changeset. The new
185 Commit the backed out changes as a new changeset. The new
186 changeset is a child of the backed out changeset.
186 changeset is a child of the backed out changeset.
187
187
188 If you back out a changeset other than the tip, a new head is
188 If you back out a changeset other than the tip, a new head is
189 created. This head will be the new tip and you should merge this
189 created. This head will be the new tip and you should merge this
190 backout changeset with another head (current one by default).
190 backout changeset with another head (current one by default).
191
191
192 The --merge option remembers the parent of the working directory
192 The --merge option remembers the parent of the working directory
193 before starting the backout, then merges the new head with that
193 before starting the backout, then merges the new head with that
194 changeset afterwards. This saves you from doing the merge by hand.
194 changeset afterwards. This saves you from doing the merge by hand.
195 The result of this merge is not committed, as with a normal merge.
195 The result of this merge is not committed, as with a normal merge.
196
196
197 See 'hg help dates' for a list of formats valid for -d/--date.
197 See 'hg help dates' for a list of formats valid for -d/--date.
198 '''
198 '''
199 if rev and node:
199 if rev and node:
200 raise util.Abort(_("please specify just one revision"))
200 raise util.Abort(_("please specify just one revision"))
201
201
202 if not rev:
202 if not rev:
203 rev = node
203 rev = node
204
204
205 if not rev:
205 if not rev:
206 raise util.Abort(_("please specify a revision to backout"))
206 raise util.Abort(_("please specify a revision to backout"))
207
207
208 date = opts.get('date')
208 date = opts.get('date')
209 if date:
209 if date:
210 opts['date'] = util.parsedate(date)
210 opts['date'] = util.parsedate(date)
211
211
212 cmdutil.bail_if_changed(repo)
212 cmdutil.bail_if_changed(repo)
213 node = repo.lookup(rev)
213 node = repo.lookup(rev)
214
214
215 op1, op2 = repo.dirstate.parents()
215 op1, op2 = repo.dirstate.parents()
216 a = repo.changelog.ancestor(op1, node)
216 a = repo.changelog.ancestor(op1, node)
217 if a != node:
217 if a != node:
218 raise util.Abort(_('cannot back out change on a different branch'))
218 raise util.Abort(_('cannot back out change on a different branch'))
219
219
220 p1, p2 = repo.changelog.parents(node)
220 p1, p2 = repo.changelog.parents(node)
221 if p1 == nullid:
221 if p1 == nullid:
222 raise util.Abort(_('cannot back out a change with no parents'))
222 raise util.Abort(_('cannot back out a change with no parents'))
223 if p2 != nullid:
223 if p2 != nullid:
224 if not opts.get('parent'):
224 if not opts.get('parent'):
225 raise util.Abort(_('cannot back out a merge changeset without '
225 raise util.Abort(_('cannot back out a merge changeset without '
226 '--parent'))
226 '--parent'))
227 p = repo.lookup(opts['parent'])
227 p = repo.lookup(opts['parent'])
228 if p not in (p1, p2):
228 if p not in (p1, p2):
229 raise util.Abort(_('%s is not a parent of %s') %
229 raise util.Abort(_('%s is not a parent of %s') %
230 (short(p), short(node)))
230 (short(p), short(node)))
231 parent = p
231 parent = p
232 else:
232 else:
233 if opts.get('parent'):
233 if opts.get('parent'):
234 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 raise util.Abort(_('cannot use --parent on non-merge changeset'))
235 parent = p1
235 parent = p1
236
236
237 # the backout should appear on the same branch
237 # the backout should appear on the same branch
238 branch = repo.dirstate.branch()
238 branch = repo.dirstate.branch()
239 hg.clean(repo, node, show_stats=False)
239 hg.clean(repo, node, show_stats=False)
240 repo.dirstate.setbranch(branch)
240 repo.dirstate.setbranch(branch)
241 revert_opts = opts.copy()
241 revert_opts = opts.copy()
242 revert_opts['date'] = None
242 revert_opts['date'] = None
243 revert_opts['all'] = True
243 revert_opts['all'] = True
244 revert_opts['rev'] = hex(parent)
244 revert_opts['rev'] = hex(parent)
245 revert_opts['no_backup'] = None
245 revert_opts['no_backup'] = None
246 revert(ui, repo, **revert_opts)
246 revert(ui, repo, **revert_opts)
247 commit_opts = opts.copy()
247 commit_opts = opts.copy()
248 commit_opts['addremove'] = False
248 commit_opts['addremove'] = False
249 if not commit_opts['message'] and not commit_opts['logfile']:
249 if not commit_opts['message'] and not commit_opts['logfile']:
250 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
250 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
251 commit_opts['force_editor'] = True
251 commit_opts['force_editor'] = True
252 commit(ui, repo, **commit_opts)
252 commit(ui, repo, **commit_opts)
253 def nice(node):
253 def nice(node):
254 return '%d:%s' % (repo.changelog.rev(node), short(node))
254 return '%d:%s' % (repo.changelog.rev(node), short(node))
255 ui.status(_('changeset %s backs out changeset %s\n') %
255 ui.status(_('changeset %s backs out changeset %s\n') %
256 (nice(repo.changelog.tip()), nice(node)))
256 (nice(repo.changelog.tip()), nice(node)))
257 if op1 != node:
257 if op1 != node:
258 hg.clean(repo, op1, show_stats=False)
258 hg.clean(repo, op1, show_stats=False)
259 if opts.get('merge'):
259 if opts.get('merge'):
260 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
260 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
261 hg.merge(repo, hex(repo.changelog.tip()))
261 hg.merge(repo, hex(repo.changelog.tip()))
262 else:
262 else:
263 ui.status(_('the backout changeset is a new head - '
263 ui.status(_('the backout changeset is a new head - '
264 'do not forget to merge\n'))
264 'do not forget to merge\n'))
265 ui.status(_('(use "backout --merge" '
265 ui.status(_('(use "backout --merge" '
266 'if you want to auto-merge)\n'))
266 'if you want to auto-merge)\n'))
267
267
268 def bisect(ui, repo, rev=None, extra=None, command=None,
268 def bisect(ui, repo, rev=None, extra=None, command=None,
269 reset=None, good=None, bad=None, skip=None, noupdate=None):
269 reset=None, good=None, bad=None, skip=None, noupdate=None):
270 """subdivision search of changesets
270 """subdivision search of changesets
271
271
272 This command helps to find changesets which introduce problems. To
272 This command helps to find changesets which introduce problems. To
273 use, mark the earliest changeset you know exhibits the problem as
273 use, mark the earliest changeset you know exhibits the problem as
274 bad, then mark the latest changeset which is free from the problem
274 bad, then mark the latest changeset which is free from the problem
275 as good. Bisect will update your working directory to a revision
275 as good. Bisect will update your working directory to a revision
276 for testing (unless the -U/--noupdate option is specified). Once
276 for testing (unless the -U/--noupdate option is specified). Once
277 you have performed tests, mark the working directory as bad or
277 you have performed tests, mark the working directory as bad or
278 good and bisect will either update to another candidate changeset
278 good and bisect will either update to another candidate changeset
279 or announce that it has found the bad revision.
279 or announce that it has found the bad revision.
280
280
281 As a shortcut, you can also use the revision argument to mark a
281 As a shortcut, you can also use the revision argument to mark a
282 revision as good or bad without checking it out first.
282 revision as good or bad without checking it out first.
283
283
284 If you supply a command it will be used for automatic bisection.
284 If you supply a command it will be used for automatic bisection.
285 Its exit status will be used as flag to mark revision as bad or
285 Its exit status will be used as flag to mark revision as bad or
286 good. In case exit status is 0 the revision is marked as good, 125
286 good. In case exit status is 0 the revision is marked as good, 125
287 - skipped, 127 (command not found) - bisection will be aborted;
287 - skipped, 127 (command not found) - bisection will be aborted;
288 any other status bigger than 0 will mark revision as bad.
288 any other status bigger than 0 will mark revision as bad.
289 """
289 """
290 def print_result(nodes, good):
290 def print_result(nodes, good):
291 displayer = cmdutil.show_changeset(ui, repo, {})
291 displayer = cmdutil.show_changeset(ui, repo, {})
292 if len(nodes) == 1:
292 if len(nodes) == 1:
293 # narrowed it down to a single revision
293 # narrowed it down to a single revision
294 if good:
294 if good:
295 ui.write(_("The first good revision is:\n"))
295 ui.write(_("The first good revision is:\n"))
296 else:
296 else:
297 ui.write(_("The first bad revision is:\n"))
297 ui.write(_("The first bad revision is:\n"))
298 displayer.show(repo[nodes[0]])
298 displayer.show(repo[nodes[0]])
299 else:
299 else:
300 # multiple possible revisions
300 # multiple possible revisions
301 if good:
301 if good:
302 ui.write(_("Due to skipped revisions, the first "
302 ui.write(_("Due to skipped revisions, the first "
303 "good revision could be any of:\n"))
303 "good revision could be any of:\n"))
304 else:
304 else:
305 ui.write(_("Due to skipped revisions, the first "
305 ui.write(_("Due to skipped revisions, the first "
306 "bad revision could be any of:\n"))
306 "bad revision could be any of:\n"))
307 for n in nodes:
307 for n in nodes:
308 displayer.show(repo[n])
308 displayer.show(repo[n])
309
309
310 def check_state(state, interactive=True):
310 def check_state(state, interactive=True):
311 if not state['good'] or not state['bad']:
311 if not state['good'] or not state['bad']:
312 if (good or bad or skip or reset) and interactive:
312 if (good or bad or skip or reset) and interactive:
313 return
313 return
314 if not state['good']:
314 if not state['good']:
315 raise util.Abort(_('cannot bisect (no known good revisions)'))
315 raise util.Abort(_('cannot bisect (no known good revisions)'))
316 else:
316 else:
317 raise util.Abort(_('cannot bisect (no known bad revisions)'))
317 raise util.Abort(_('cannot bisect (no known bad revisions)'))
318 return True
318 return True
319
319
320 # backward compatibility
320 # backward compatibility
321 if rev in "good bad reset init".split():
321 if rev in "good bad reset init".split():
322 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
322 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
323 cmd, rev, extra = rev, extra, None
323 cmd, rev, extra = rev, extra, None
324 if cmd == "good":
324 if cmd == "good":
325 good = True
325 good = True
326 elif cmd == "bad":
326 elif cmd == "bad":
327 bad = True
327 bad = True
328 else:
328 else:
329 reset = True
329 reset = True
330 elif extra or good + bad + skip + reset + bool(command) > 1:
330 elif extra or good + bad + skip + reset + bool(command) > 1:
331 raise util.Abort(_('incompatible arguments'))
331 raise util.Abort(_('incompatible arguments'))
332
332
333 if reset:
333 if reset:
334 p = repo.join("bisect.state")
334 p = repo.join("bisect.state")
335 if os.path.exists(p):
335 if os.path.exists(p):
336 os.unlink(p)
336 os.unlink(p)
337 return
337 return
338
338
339 state = hbisect.load_state(repo)
339 state = hbisect.load_state(repo)
340
340
341 if command:
341 if command:
342 commandpath = util.find_exe(command)
342 commandpath = util.find_exe(command)
343 changesets = 1
343 changesets = 1
344 try:
344 try:
345 while changesets:
345 while changesets:
346 # update state
346 # update state
347 status = subprocess.call([commandpath])
347 status = subprocess.call([commandpath])
348 if status == 125:
348 if status == 125:
349 transition = "skip"
349 transition = "skip"
350 elif status == 0:
350 elif status == 0:
351 transition = "good"
351 transition = "good"
352 # status < 0 means process was killed
352 # status < 0 means process was killed
353 elif status == 127:
353 elif status == 127:
354 raise util.Abort(_("failed to execute %s") % command)
354 raise util.Abort(_("failed to execute %s") % command)
355 elif status < 0:
355 elif status < 0:
356 raise util.Abort(_("%s killed") % command)
356 raise util.Abort(_("%s killed") % command)
357 else:
357 else:
358 transition = "bad"
358 transition = "bad"
359 node = repo.lookup(rev or '.')
359 node = repo.lookup(rev or '.')
360 state[transition].append(node)
360 state[transition].append(node)
361 ui.note(_('Changeset %s: %s\n') % (short(node), transition))
361 ui.note(_('Changeset %s: %s\n') % (short(node), transition))
362 check_state(state, interactive=False)
362 check_state(state, interactive=False)
363 # bisect
363 # bisect
364 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
364 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
365 # update to next check
365 # update to next check
366 cmdutil.bail_if_changed(repo)
366 cmdutil.bail_if_changed(repo)
367 hg.clean(repo, nodes[0], show_stats=False)
367 hg.clean(repo, nodes[0], show_stats=False)
368 finally:
368 finally:
369 hbisect.save_state(repo, state)
369 hbisect.save_state(repo, state)
370 return print_result(nodes, not status)
370 return print_result(nodes, not status)
371
371
372 # update state
372 # update state
373 node = repo.lookup(rev or '.')
373 node = repo.lookup(rev or '.')
374 if good:
374 if good:
375 state['good'].append(node)
375 state['good'].append(node)
376 elif bad:
376 elif bad:
377 state['bad'].append(node)
377 state['bad'].append(node)
378 elif skip:
378 elif skip:
379 state['skip'].append(node)
379 state['skip'].append(node)
380
380
381 hbisect.save_state(repo, state)
381 hbisect.save_state(repo, state)
382
382
383 if not check_state(state):
383 if not check_state(state):
384 return
384 return
385
385
386 # actually bisect
386 # actually bisect
387 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
387 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
388 if changesets == 0:
388 if changesets == 0:
389 print_result(nodes, good)
389 print_result(nodes, good)
390 else:
390 else:
391 assert len(nodes) == 1 # only a single node can be tested next
391 assert len(nodes) == 1 # only a single node can be tested next
392 node = nodes[0]
392 node = nodes[0]
393 # compute the approximate number of remaining tests
393 # compute the approximate number of remaining tests
394 tests, size = 0, 2
394 tests, size = 0, 2
395 while size <= changesets:
395 while size <= changesets:
396 tests, size = tests + 1, size * 2
396 tests, size = tests + 1, size * 2
397 rev = repo.changelog.rev(node)
397 rev = repo.changelog.rev(node)
398 ui.write(_("Testing changeset %s:%s "
398 ui.write(_("Testing changeset %s:%s "
399 "(%s changesets remaining, ~%s tests)\n")
399 "(%s changesets remaining, ~%s tests)\n")
400 % (rev, short(node), changesets, tests))
400 % (rev, short(node), changesets, tests))
401 if not noupdate:
401 if not noupdate:
402 cmdutil.bail_if_changed(repo)
402 cmdutil.bail_if_changed(repo)
403 return hg.clean(repo, node)
403 return hg.clean(repo, node)
404
404
405 def branch(ui, repo, label=None, **opts):
405 def branch(ui, repo, label=None, **opts):
406 """set or show the current branch name
406 """set or show the current branch name
407
407
408 With no argument, show the current branch name. With one argument,
408 With no argument, show the current branch name. With one argument,
409 set the working directory branch name (the branch does not exist
409 set the working directory branch name (the branch does not exist
410 in the repository until the next commit). It is recommended to use
410 in the repository until the next commit). It is recommended to use
411 the 'default' branch as your primary development branch.
411 the 'default' branch as your primary development branch.
412
412
413 Unless -f/--force is specified, branch will not let you set a
413 Unless -f/--force is specified, branch will not let you set a
414 branch name that shadows an existing branch.
414 branch name that shadows an existing branch.
415
415
416 Use -C/--clean to reset the working directory branch to that of
416 Use -C/--clean to reset the working directory branch to that of
417 the parent of the working directory, negating a previous branch
417 the parent of the working directory, negating a previous branch
418 change.
418 change.
419
419
420 Use the command 'hg update' to switch to an existing branch.
420 Use the command 'hg update' to switch to an existing branch.
421 """
421 """
422
422
423 if opts.get('clean'):
423 if opts.get('clean'):
424 label = repo[None].parents()[0].branch()
424 label = repo[None].parents()[0].branch()
425 repo.dirstate.setbranch(label)
425 repo.dirstate.setbranch(label)
426 ui.status(_('reset working directory to branch %s\n') % label)
426 ui.status(_('reset working directory to branch %s\n') % label)
427 elif label:
427 elif label:
428 if not opts.get('force') and label in repo.branchtags():
428 if not opts.get('force') and label in repo.branchtags():
429 if label not in [p.branch() for p in repo.parents()]:
429 if label not in [p.branch() for p in repo.parents()]:
430 raise util.Abort(_('a branch of the same name already exists'
430 raise util.Abort(_('a branch of the same name already exists'
431 ' (use --force to override)'))
431 ' (use --force to override)'))
432 repo.dirstate.setbranch(encoding.fromlocal(label))
432 repo.dirstate.setbranch(encoding.fromlocal(label))
433 ui.status(_('marked working directory as branch %s\n') % label)
433 ui.status(_('marked working directory as branch %s\n') % label)
434 else:
434 else:
435 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
435 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
436
436
437 def branches(ui, repo, active=False):
437 def branches(ui, repo, active=False):
438 """list repository named branches
438 """list repository named branches
439
439
440 List the repository's named branches, indicating which ones are
440 List the repository's named branches, indicating which ones are
441 inactive. If active is specified, only show active branches.
441 inactive. If active is specified, only show active branches.
442
442
443 A branch is considered active if it contains repository heads.
443 A branch is considered active if it contains repository heads.
444
444
445 Use the command 'hg update' to switch to an existing branch.
445 Use the command 'hg update' to switch to an existing branch.
446 """
446 """
447 hexfunc = ui.debugflag and hex or short
447 hexfunc = ui.debugflag and hex or short
448 activebranches = [encoding.tolocal(repo[n].branch())
448 activebranches = [encoding.tolocal(repo[n].branch())
449 for n in repo.heads(closed=False)]
449 for n in repo.heads(closed=False)]
450 branches = sorted([(tag in activebranches, repo.changelog.rev(node), tag)
450 branches = sorted([(tag in activebranches, repo.changelog.rev(node), tag)
451 for tag, node in repo.branchtags().items()],
451 for tag, node in repo.branchtags().items()],
452 reverse=True)
452 reverse=True)
453
453
454 for isactive, node, tag in branches:
454 for isactive, node, tag in branches:
455 if (not active) or isactive:
455 if (not active) or isactive:
456 if ui.quiet:
456 if ui.quiet:
457 ui.write("%s\n" % tag)
457 ui.write("%s\n" % tag)
458 else:
458 else:
459 hn = repo.lookup(node)
459 hn = repo.lookup(node)
460 if isactive:
460 if isactive:
461 notice = ''
461 notice = ''
462 elif hn not in repo.branchheads(tag, closed=False):
462 elif hn not in repo.branchheads(tag, closed=False):
463 notice = ' (closed)'
463 notice = ' (closed)'
464 else:
464 else:
465 notice = ' (inactive)'
465 notice = ' (inactive)'
466 rev = str(node).rjust(31 - encoding.colwidth(tag))
466 rev = str(node).rjust(31 - encoding.colwidth(tag))
467 data = tag, rev, hexfunc(hn), notice
467 data = tag, rev, hexfunc(hn), notice
468 ui.write("%s %s:%s%s\n" % data)
468 ui.write("%s %s:%s%s\n" % data)
469
469
470 def bundle(ui, repo, fname, dest=None, **opts):
470 def bundle(ui, repo, fname, dest=None, **opts):
471 """create a changegroup file
471 """create a changegroup file
472
472
473 Generate a compressed changegroup file collecting changesets not
473 Generate a compressed changegroup file collecting changesets not
474 known to be in another repository.
474 known to be in another repository.
475
475
476 If no destination repository is specified the destination is
476 If no destination repository is specified the destination is
477 assumed to have all the nodes specified by one or more --base
477 assumed to have all the nodes specified by one or more --base
478 parameters. To create a bundle containing all changesets, use
478 parameters. To create a bundle containing all changesets, use
479 -a/--all (or --base null). To change the compression method
479 -a/--all (or --base null). To change the compression method
480 applied, use the -t/--type option (by default, bundles are
480 applied, use the -t/--type option (by default, bundles are
481 compressed using bz2).
481 compressed using bz2).
482
482
483 The bundle file can then be transferred using conventional means
483 The bundle file can then be transferred using conventional means
484 and applied to another repository with the unbundle or pull
484 and applied to another repository with the unbundle or pull
485 command. This is useful when direct push and pull are not
485 command. This is useful when direct push and pull are not
486 available or when exporting an entire repository is undesirable.
486 available or when exporting an entire repository is undesirable.
487
487
488 Applying bundles preserves all changeset contents including
488 Applying bundles preserves all changeset contents including
489 permissions, copy/rename information, and revision history.
489 permissions, copy/rename information, and revision history.
490 """
490 """
491 revs = opts.get('rev') or None
491 revs = opts.get('rev') or None
492 if revs:
492 if revs:
493 revs = [repo.lookup(rev) for rev in revs]
493 revs = [repo.lookup(rev) for rev in revs]
494 if opts.get('all'):
494 if opts.get('all'):
495 base = ['null']
495 base = ['null']
496 else:
496 else:
497 base = opts.get('base')
497 base = opts.get('base')
498 if base:
498 if base:
499 if dest:
499 if dest:
500 raise util.Abort(_("--base is incompatible with specifiying "
500 raise util.Abort(_("--base is incompatible with specifiying "
501 "a destination"))
501 "a destination"))
502 base = [repo.lookup(rev) for rev in base]
502 base = [repo.lookup(rev) for rev in base]
503 # create the right base
503 # create the right base
504 # XXX: nodesbetween / changegroup* should be "fixed" instead
504 # XXX: nodesbetween / changegroup* should be "fixed" instead
505 o = []
505 o = []
506 has = set((nullid,))
506 has = set((nullid,))
507 for n in base:
507 for n in base:
508 has.update(repo.changelog.reachable(n))
508 has.update(repo.changelog.reachable(n))
509 if revs:
509 if revs:
510 visit = list(revs)
510 visit = list(revs)
511 else:
511 else:
512 visit = repo.changelog.heads()
512 visit = repo.changelog.heads()
513 seen = {}
513 seen = {}
514 while visit:
514 while visit:
515 n = visit.pop(0)
515 n = visit.pop(0)
516 parents = [p for p in repo.changelog.parents(n) if p not in has]
516 parents = [p for p in repo.changelog.parents(n) if p not in has]
517 if len(parents) == 0:
517 if len(parents) == 0:
518 o.insert(0, n)
518 o.insert(0, n)
519 else:
519 else:
520 for p in parents:
520 for p in parents:
521 if p not in seen:
521 if p not in seen:
522 seen[p] = 1
522 seen[p] = 1
523 visit.append(p)
523 visit.append(p)
524 else:
524 else:
525 dest, revs, checkout = hg.parseurl(
525 dest, revs, checkout = hg.parseurl(
526 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
526 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
527 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
527 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
528 o = repo.findoutgoing(other, force=opts.get('force'))
528 o = repo.findoutgoing(other, force=opts.get('force'))
529
529
530 if revs:
530 if revs:
531 cg = repo.changegroupsubset(o, revs, 'bundle')
531 cg = repo.changegroupsubset(o, revs, 'bundle')
532 else:
532 else:
533 cg = repo.changegroup(o, 'bundle')
533 cg = repo.changegroup(o, 'bundle')
534
534
535 bundletype = opts.get('type', 'bzip2').lower()
535 bundletype = opts.get('type', 'bzip2').lower()
536 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
536 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
537 bundletype = btypes.get(bundletype)
537 bundletype = btypes.get(bundletype)
538 if bundletype not in changegroup.bundletypes:
538 if bundletype not in changegroup.bundletypes:
539 raise util.Abort(_('unknown bundle type specified with --type'))
539 raise util.Abort(_('unknown bundle type specified with --type'))
540
540
541 changegroup.writebundle(cg, fname, bundletype)
541 changegroup.writebundle(cg, fname, bundletype)
542
542
543 def cat(ui, repo, file1, *pats, **opts):
543 def cat(ui, repo, file1, *pats, **opts):
544 """output the current or given revision of files
544 """output the current or given revision of files
545
545
546 Print the specified files as they were at the given revision. If
546 Print the specified files as they were at the given revision. If
547 no revision is given, the parent of the working directory is used,
547 no revision is given, the parent of the working directory is used,
548 or tip if no revision is checked out.
548 or tip if no revision is checked out.
549
549
550 Output may be to a file, in which case the name of the file is
550 Output may be to a file, in which case the name of the file is
551 given using a format string. The formatting rules are the same as
551 given using a format string. The formatting rules are the same as
552 for the export command, with the following additions:
552 for the export command, with the following additions:
553
553
554 %s basename of file being printed
554 %s basename of file being printed
555 %d dirname of file being printed, or '.' if in repository root
555 %d dirname of file being printed, or '.' if in repository root
556 %p root-relative path name of file being printed
556 %p root-relative path name of file being printed
557 """
557 """
558 ctx = repo[opts.get('rev')]
558 ctx = repo[opts.get('rev')]
559 err = 1
559 err = 1
560 m = cmdutil.match(repo, (file1,) + pats, opts)
560 m = cmdutil.match(repo, (file1,) + pats, opts)
561 for abs in ctx.walk(m):
561 for abs in ctx.walk(m):
562 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
562 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
563 data = ctx[abs].data()
563 data = ctx[abs].data()
564 if opts.get('decode'):
564 if opts.get('decode'):
565 data = repo.wwritedata(abs, data)
565 data = repo.wwritedata(abs, data)
566 fp.write(data)
566 fp.write(data)
567 err = 0
567 err = 0
568 return err
568 return err
569
569
570 def clone(ui, source, dest=None, **opts):
570 def clone(ui, source, dest=None, **opts):
571 """make a copy of an existing repository
571 """make a copy of an existing repository
572
572
573 Create a copy of an existing repository in a new directory.
573 Create a copy of an existing repository in a new directory.
574
574
575 If no destination directory name is specified, it defaults to the
575 If no destination directory name is specified, it defaults to the
576 basename of the source.
576 basename of the source.
577
577
578 The location of the source is added to the new repository's
578 The location of the source is added to the new repository's
579 .hg/hgrc file, as the default to be used for future pulls.
579 .hg/hgrc file, as the default to be used for future pulls.
580
580
581 If you use the -r/--rev option to clone up to a specific revision,
581 If you use the -r/--rev option to clone up to a specific revision,
582 no subsequent revisions (including subsequent tags) will be
582 no subsequent revisions (including subsequent tags) will be
583 present in the cloned repository. This option implies --pull, even
583 present in the cloned repository. This option implies --pull, even
584 on local repositories.
584 on local repositories.
585
585
586 By default, clone will check out the head of the 'default' branch.
586 By default, clone will check out the head of the 'default' branch.
587 If the -U/--noupdate option is used, the new clone will contain
587 If the -U/--noupdate option is used, the new clone will contain
588 only a repository (.hg) and no working copy (the working copy
588 only a repository (.hg) and no working copy (the working copy
589 parent is the null revision).
589 parent is the null revision).
590
590
591 See 'hg help urls' for valid source format details.
591 See 'hg help urls' for valid source format details.
592
592
593 It is possible to specify an ssh:// URL as the destination, but no
593 It is possible to specify an ssh:// URL as the destination, but no
594 .hg/hgrc and working directory will be created on the remote side.
594 .hg/hgrc and working directory will be created on the remote side.
595 Look at the help text for URLs for important details about ssh://
595 Look at the help text for URLs for important details about ssh://
596 URLs.
596 URLs.
597
597
598 For efficiency, hardlinks are used for cloning whenever the source
598 For efficiency, hardlinks are used for cloning whenever the source
599 and destination are on the same filesystem (note this applies only
599 and destination are on the same filesystem (note this applies only
600 to the repository data, not to the checked out files). Some
600 to the repository data, not to the checked out files). Some
601 filesystems, such as AFS, implement hardlinking incorrectly, but
601 filesystems, such as AFS, implement hardlinking incorrectly, but
602 do not report errors. In these cases, use the --pull option to
602 do not report errors. In these cases, use the --pull option to
603 avoid hardlinking.
603 avoid hardlinking.
604
604
605 In some cases, you can clone repositories and checked out files
605 In some cases, you can clone repositories and checked out files
606 using full hardlinks with
606 using full hardlinks with
607
607
608 $ cp -al REPO REPOCLONE
608 $ cp -al REPO REPOCLONE
609
609
610 This is the fastest way to clone, but it is not always safe. The
610 This is the fastest way to clone, but it is not always safe. The
611 operation is not atomic (making sure REPO is not modified during
611 operation is not atomic (making sure REPO is not modified during
612 the operation is up to you) and you have to make sure your editor
612 the operation is up to you) and you have to make sure your editor
613 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
613 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
614 this is not compatible with certain extensions that place their
614 this is not compatible with certain extensions that place their
615 metadata under the .hg directory, such as mq.
615 metadata under the .hg directory, such as mq.
616
616
617 """
617 """
618 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
618 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
619 pull=opts.get('pull'),
619 pull=opts.get('pull'),
620 stream=opts.get('uncompressed'),
620 stream=opts.get('uncompressed'),
621 rev=opts.get('rev'),
621 rev=opts.get('rev'),
622 update=not opts.get('noupdate'))
622 update=not opts.get('noupdate'))
623
623
624 def commit(ui, repo, *pats, **opts):
624 def commit(ui, repo, *pats, **opts):
625 """commit the specified files or all outstanding changes
625 """commit the specified files or all outstanding changes
626
626
627 Commit changes to the given files into the repository. Unlike a
627 Commit changes to the given files into the repository. Unlike a
628 centralized RCS, this operation is a local operation. See hg push
628 centralized RCS, this operation is a local operation. See hg push
629 for means to actively distribute your changes.
629 for means to actively distribute your changes.
630
630
631 If a list of files is omitted, all changes reported by "hg status"
631 If a list of files is omitted, all changes reported by "hg status"
632 will be committed.
632 will be committed.
633
633
634 If you are committing the result of a merge, do not provide any
634 If you are committing the result of a merge, do not provide any
635 file names or -I/-X filters.
635 file names or -I/-X filters.
636
636
637 If no commit message is specified, the configured editor is
637 If no commit message is specified, the configured editor is
638 started to prompt you for a message.
638 started to prompt you for a message.
639
639
640 See 'hg help dates' for a list of formats valid for -d/--date.
640 See 'hg help dates' for a list of formats valid for -d/--date.
641 """
641 """
642 extra = {}
642 extra = {}
643 if opts.get('close_branch'):
643 if opts.get('close_branch'):
644 extra['close'] = 1
644 extra['close'] = 1
645 e = cmdutil.commiteditor
645 e = cmdutil.commiteditor
646 if opts.get('force_editor'):
646 if opts.get('force_editor'):
647 e = cmdutil.commitforceeditor
647 e = cmdutil.commitforceeditor
648
648
649 def commitfunc(ui, repo, message, match, opts):
649 def commitfunc(ui, repo, message, match, opts):
650 return repo.commit(match.files(), message, opts.get('user'),
650 return repo.commit(match.files(), message, opts.get('user'),
651 opts.get('date'), match, editor=e, extra=extra)
651 opts.get('date'), match, editor=e, extra=extra)
652
652
653 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
653 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
654 if not node:
654 if not node:
655 return
655 return
656 cl = repo.changelog
656 cl = repo.changelog
657 rev = cl.rev(node)
657 rev = cl.rev(node)
658 parents = cl.parentrevs(rev)
658 parents = cl.parentrevs(rev)
659 if rev - 1 in parents:
659 if rev - 1 in parents:
660 # one of the parents was the old tip
660 # one of the parents was the old tip
661 pass
661 pass
662 elif (parents == (nullrev, nullrev) or
662 elif (parents == (nullrev, nullrev) or
663 len(cl.heads(cl.node(parents[0]))) > 1 and
663 len(cl.heads(cl.node(parents[0]))) > 1 and
664 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
664 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
665 ui.status(_('created new head\n'))
665 ui.status(_('created new head\n'))
666
666
667 if ui.debugflag:
667 if ui.debugflag:
668 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
668 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
669 elif ui.verbose:
669 elif ui.verbose:
670 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
670 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
671
671
672 def copy(ui, repo, *pats, **opts):
672 def copy(ui, repo, *pats, **opts):
673 """mark files as copied for the next commit
673 """mark files as copied for the next commit
674
674
675 Mark dest as having copies of source files. If dest is a
675 Mark dest as having copies of source files. If dest is a
676 directory, copies are put in that directory. If dest is a file,
676 directory, copies are put in that directory. If dest is a file,
677 the source must be a single file.
677 the source must be a single file.
678
678
679 By default, this command copies the contents of files as they
679 By default, this command copies the contents of files as they
680 stand in the working directory. If invoked with -A/--after, the
680 stand in the working directory. If invoked with -A/--after, the
681 operation is recorded, but no copying is performed.
681 operation is recorded, but no copying is performed.
682
682
683 This command takes effect with the next commit. To undo a copy
683 This command takes effect with the next commit. To undo a copy
684 before that, see hg revert.
684 before that, see hg revert.
685 """
685 """
686 wlock = repo.wlock(False)
686 wlock = repo.wlock(False)
687 try:
687 try:
688 return cmdutil.copy(ui, repo, pats, opts)
688 return cmdutil.copy(ui, repo, pats, opts)
689 finally:
689 finally:
690 wlock.release()
690 wlock.release()
691
691
692 def debugancestor(ui, repo, *args):
692 def debugancestor(ui, repo, *args):
693 """find the ancestor revision of two revisions in a given index"""
693 """find the ancestor revision of two revisions in a given index"""
694 if len(args) == 3:
694 if len(args) == 3:
695 index, rev1, rev2 = args
695 index, rev1, rev2 = args
696 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
696 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
697 lookup = r.lookup
697 lookup = r.lookup
698 elif len(args) == 2:
698 elif len(args) == 2:
699 if not repo:
699 if not repo:
700 raise util.Abort(_("There is no Mercurial repository here "
700 raise util.Abort(_("There is no Mercurial repository here "
701 "(.hg not found)"))
701 "(.hg not found)"))
702 rev1, rev2 = args
702 rev1, rev2 = args
703 r = repo.changelog
703 r = repo.changelog
704 lookup = repo.lookup
704 lookup = repo.lookup
705 else:
705 else:
706 raise util.Abort(_('either two or three arguments required'))
706 raise util.Abort(_('either two or three arguments required'))
707 a = r.ancestor(lookup(rev1), lookup(rev2))
707 a = r.ancestor(lookup(rev1), lookup(rev2))
708 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
708 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
709
709
710 def debugcommands(ui, cmd='', *args):
710 def debugcommands(ui, cmd='', *args):
711 for cmd, vals in sorted(table.iteritems()):
711 for cmd, vals in sorted(table.iteritems()):
712 cmd = cmd.split('|')[0].strip('^')
712 cmd = cmd.split('|')[0].strip('^')
713 opts = ', '.join([i[1] for i in vals[1]])
713 opts = ', '.join([i[1] for i in vals[1]])
714 ui.write('%s: %s\n' % (cmd, opts))
714 ui.write('%s: %s\n' % (cmd, opts))
715
715
716 def debugcomplete(ui, cmd='', **opts):
716 def debugcomplete(ui, cmd='', **opts):
717 """returns the completion list associated with the given command"""
717 """returns the completion list associated with the given command"""
718
718
719 if opts.get('options'):
719 if opts.get('options'):
720 options = []
720 options = []
721 otables = [globalopts]
721 otables = [globalopts]
722 if cmd:
722 if cmd:
723 aliases, entry = cmdutil.findcmd(cmd, table, False)
723 aliases, entry = cmdutil.findcmd(cmd, table, False)
724 otables.append(entry[1])
724 otables.append(entry[1])
725 for t in otables:
725 for t in otables:
726 for o in t:
726 for o in t:
727 if o[0]:
727 if o[0]:
728 options.append('-%s' % o[0])
728 options.append('-%s' % o[0])
729 options.append('--%s' % o[1])
729 options.append('--%s' % o[1])
730 ui.write("%s\n" % "\n".join(options))
730 ui.write("%s\n" % "\n".join(options))
731 return
731 return
732
732
733 cmdlist = cmdutil.findpossible(cmd, table)
733 cmdlist = cmdutil.findpossible(cmd, table)
734 if ui.verbose:
734 if ui.verbose:
735 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
735 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
736 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
736 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
737
737
738 def debugfsinfo(ui, path = "."):
738 def debugfsinfo(ui, path = "."):
739 file('.debugfsinfo', 'w').write('')
739 file('.debugfsinfo', 'w').write('')
740 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
740 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
741 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
741 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
742 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
742 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
743 and 'yes' or 'no'))
743 and 'yes' or 'no'))
744 os.unlink('.debugfsinfo')
744 os.unlink('.debugfsinfo')
745
745
746 def debugrebuildstate(ui, repo, rev="tip"):
746 def debugrebuildstate(ui, repo, rev="tip"):
747 """rebuild the dirstate as it would look like for the given revision"""
747 """rebuild the dirstate as it would look like for the given revision"""
748 ctx = repo[rev]
748 ctx = repo[rev]
749 wlock = repo.wlock()
749 wlock = repo.wlock()
750 try:
750 try:
751 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
751 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
752 finally:
752 finally:
753 wlock.release()
753 wlock.release()
754
754
755 def debugcheckstate(ui, repo):
755 def debugcheckstate(ui, repo):
756 """validate the correctness of the current dirstate"""
756 """validate the correctness of the current dirstate"""
757 parent1, parent2 = repo.dirstate.parents()
757 parent1, parent2 = repo.dirstate.parents()
758 m1 = repo[parent1].manifest()
758 m1 = repo[parent1].manifest()
759 m2 = repo[parent2].manifest()
759 m2 = repo[parent2].manifest()
760 errors = 0
760 errors = 0
761 for f in repo.dirstate:
761 for f in repo.dirstate:
762 state = repo.dirstate[f]
762 state = repo.dirstate[f]
763 if state in "nr" and f not in m1:
763 if state in "nr" and f not in m1:
764 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
764 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
765 errors += 1
765 errors += 1
766 if state in "a" and f in m1:
766 if state in "a" and f in m1:
767 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
767 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
768 errors += 1
768 errors += 1
769 if state in "m" and f not in m1 and f not in m2:
769 if state in "m" and f not in m1 and f not in m2:
770 ui.warn(_("%s in state %s, but not in either manifest\n") %
770 ui.warn(_("%s in state %s, but not in either manifest\n") %
771 (f, state))
771 (f, state))
772 errors += 1
772 errors += 1
773 for f in m1:
773 for f in m1:
774 state = repo.dirstate[f]
774 state = repo.dirstate[f]
775 if state not in "nrm":
775 if state not in "nrm":
776 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
776 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
777 errors += 1
777 errors += 1
778 if errors:
778 if errors:
779 error = _(".hg/dirstate inconsistent with current parent's manifest")
779 error = _(".hg/dirstate inconsistent with current parent's manifest")
780 raise util.Abort(error)
780 raise util.Abort(error)
781
781
782 def showconfig(ui, repo, *values, **opts):
782 def showconfig(ui, repo, *values, **opts):
783 """show combined config settings from all hgrc files
783 """show combined config settings from all hgrc files
784
784
785 With no args, print names and values of all config items.
785 With no args, print names and values of all config items.
786
786
787 With one arg of the form section.name, print just the value of
787 With one arg of the form section.name, print just the value of
788 that config item.
788 that config item.
789
789
790 With multiple args, print names and values of all config items
790 With multiple args, print names and values of all config items
791 with matching section names.
791 with matching section names.
792
792
793 With the --debug flag, the source (filename and line number) is
793 With the --debug flag, the source (filename and line number) is
794 printed for each config item.
794 printed for each config item.
795 """
795 """
796
796
797 untrusted = bool(opts.get('untrusted'))
797 untrusted = bool(opts.get('untrusted'))
798 if values:
798 if values:
799 if len([v for v in values if '.' in v]) > 1:
799 if len([v for v in values if '.' in v]) > 1:
800 raise util.Abort(_('only one config item permitted'))
800 raise util.Abort(_('only one config item permitted'))
801 for section, name, value in ui.walkconfig(untrusted=untrusted):
801 for section, name, value in ui.walkconfig(untrusted=untrusted):
802 sectname = section + '.' + name
802 sectname = section + '.' + name
803 if values:
803 if values:
804 for v in values:
804 for v in values:
805 if v == section:
805 if v == section:
806 ui.debug('%s: ' %
806 ui.debug('%s: ' %
807 ui.configsource(section, name, untrusted))
807 ui.configsource(section, name, untrusted))
808 ui.write('%s=%s\n' % (sectname, value))
808 ui.write('%s=%s\n' % (sectname, value))
809 elif v == sectname:
809 elif v == sectname:
810 ui.debug('%s: ' %
810 ui.debug('%s: ' %
811 ui.configsource(section, name, untrusted))
811 ui.configsource(section, name, untrusted))
812 ui.write(value, '\n')
812 ui.write(value, '\n')
813 else:
813 else:
814 ui.debug('%s: ' %
814 ui.debug('%s: ' %
815 ui.configsource(section, name, untrusted))
815 ui.configsource(section, name, untrusted))
816 ui.write('%s=%s\n' % (sectname, value))
816 ui.write('%s=%s\n' % (sectname, value))
817
817
818 def debugsetparents(ui, repo, rev1, rev2=None):
818 def debugsetparents(ui, repo, rev1, rev2=None):
819 """manually set the parents of the current working directory
819 """manually set the parents of the current working directory
820
820
821 This is useful for writing repository conversion tools, but should
821 This is useful for writing repository conversion tools, but should
822 be used with care.
822 be used with care.
823 """
823 """
824
824
825 if not rev2:
825 if not rev2:
826 rev2 = hex(nullid)
826 rev2 = hex(nullid)
827
827
828 wlock = repo.wlock()
828 wlock = repo.wlock()
829 try:
829 try:
830 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
830 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
831 finally:
831 finally:
832 wlock.release()
832 wlock.release()
833
833
834 def debugstate(ui, repo, nodates=None):
834 def debugstate(ui, repo, nodates=None):
835 """show the contents of the current dirstate"""
835 """show the contents of the current dirstate"""
836 timestr = ""
836 timestr = ""
837 showdate = not nodates
837 showdate = not nodates
838 for file_, ent in sorted(repo.dirstate._map.iteritems()):
838 for file_, ent in sorted(repo.dirstate._map.iteritems()):
839 if showdate:
839 if showdate:
840 if ent[3] == -1:
840 if ent[3] == -1:
841 # Pad or slice to locale representation
841 # Pad or slice to locale representation
842 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
842 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
843 timestr = 'unset'
843 timestr = 'unset'
844 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
844 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
845 else:
845 else:
846 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
846 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
847 if ent[1] & 020000:
847 if ent[1] & 020000:
848 mode = 'lnk'
848 mode = 'lnk'
849 else:
849 else:
850 mode = '%3o' % (ent[1] & 0777)
850 mode = '%3o' % (ent[1] & 0777)
851 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
851 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
852 for f in repo.dirstate.copies():
852 for f in repo.dirstate.copies():
853 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
853 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
854
854
855 def debugdata(ui, file_, rev):
855 def debugdata(ui, file_, rev):
856 """dump the contents of a data file revision"""
856 """dump the contents of a data file revision"""
857 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
857 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
858 try:
858 try:
859 ui.write(r.revision(r.lookup(rev)))
859 ui.write(r.revision(r.lookup(rev)))
860 except KeyError:
860 except KeyError:
861 raise util.Abort(_('invalid revision identifier %s') % rev)
861 raise util.Abort(_('invalid revision identifier %s') % rev)
862
862
863 def debugdate(ui, date, range=None, **opts):
863 def debugdate(ui, date, range=None, **opts):
864 """parse and display a date"""
864 """parse and display a date"""
865 if opts["extended"]:
865 if opts["extended"]:
866 d = util.parsedate(date, util.extendeddateformats)
866 d = util.parsedate(date, util.extendeddateformats)
867 else:
867 else:
868 d = util.parsedate(date)
868 d = util.parsedate(date)
869 ui.write("internal: %s %s\n" % d)
869 ui.write("internal: %s %s\n" % d)
870 ui.write("standard: %s\n" % util.datestr(d))
870 ui.write("standard: %s\n" % util.datestr(d))
871 if range:
871 if range:
872 m = util.matchdate(range)
872 m = util.matchdate(range)
873 ui.write("match: %s\n" % m(d[0]))
873 ui.write("match: %s\n" % m(d[0]))
874
874
875 def debugindex(ui, file_):
875 def debugindex(ui, file_):
876 """dump the contents of an index file"""
876 """dump the contents of an index file"""
877 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
877 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
878 ui.write(" rev offset length base linkrev"
878 ui.write(" rev offset length base linkrev"
879 " nodeid p1 p2\n")
879 " nodeid p1 p2\n")
880 for i in r:
880 for i in r:
881 node = r.node(i)
881 node = r.node(i)
882 try:
882 try:
883 pp = r.parents(node)
883 pp = r.parents(node)
884 except:
884 except:
885 pp = [nullid, nullid]
885 pp = [nullid, nullid]
886 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
886 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
887 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
887 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
888 short(node), short(pp[0]), short(pp[1])))
888 short(node), short(pp[0]), short(pp[1])))
889
889
890 def debugindexdot(ui, file_):
890 def debugindexdot(ui, file_):
891 """dump an index DAG as a .dot file"""
891 """dump an index DAG as a .dot file"""
892 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
892 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
893 ui.write("digraph G {\n")
893 ui.write("digraph G {\n")
894 for i in r:
894 for i in r:
895 node = r.node(i)
895 node = r.node(i)
896 pp = r.parents(node)
896 pp = r.parents(node)
897 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
897 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
898 if pp[1] != nullid:
898 if pp[1] != nullid:
899 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
899 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
900 ui.write("}\n")
900 ui.write("}\n")
901
901
902 def debuginstall(ui):
902 def debuginstall(ui):
903 '''test Mercurial installation'''
903 '''test Mercurial installation'''
904
904
905 def writetemp(contents):
905 def writetemp(contents):
906 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
906 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
907 f = os.fdopen(fd, "wb")
907 f = os.fdopen(fd, "wb")
908 f.write(contents)
908 f.write(contents)
909 f.close()
909 f.close()
910 return name
910 return name
911
911
912 problems = 0
912 problems = 0
913
913
914 # encoding
914 # encoding
915 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
915 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
916 try:
916 try:
917 encoding.fromlocal("test")
917 encoding.fromlocal("test")
918 except util.Abort, inst:
918 except util.Abort, inst:
919 ui.write(" %s\n" % inst)
919 ui.write(" %s\n" % inst)
920 ui.write(_(" (check that your locale is properly set)\n"))
920 ui.write(_(" (check that your locale is properly set)\n"))
921 problems += 1
921 problems += 1
922
922
923 # compiled modules
923 # compiled modules
924 ui.status(_("Checking extensions...\n"))
924 ui.status(_("Checking extensions...\n"))
925 try:
925 try:
926 import bdiff, mpatch, base85
926 import bdiff, mpatch, base85
927 except Exception, inst:
927 except Exception, inst:
928 ui.write(" %s\n" % inst)
928 ui.write(" %s\n" % inst)
929 ui.write(_(" One or more extensions could not be found"))
929 ui.write(_(" One or more extensions could not be found"))
930 ui.write(_(" (check that you compiled the extensions)\n"))
930 ui.write(_(" (check that you compiled the extensions)\n"))
931 problems += 1
931 problems += 1
932
932
933 # templates
933 # templates
934 ui.status(_("Checking templates...\n"))
934 ui.status(_("Checking templates...\n"))
935 try:
935 try:
936 import templater
936 import templater
937 templater.templater(templater.templatepath("map-cmdline.default"))
937 templater.templater(templater.templatepath("map-cmdline.default"))
938 except Exception, inst:
938 except Exception, inst:
939 ui.write(" %s\n" % inst)
939 ui.write(" %s\n" % inst)
940 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
940 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
941 problems += 1
941 problems += 1
942
942
943 # patch
943 # patch
944 ui.status(_("Checking patch...\n"))
944 ui.status(_("Checking patch...\n"))
945 patchproblems = 0
945 patchproblems = 0
946 a = "1\n2\n3\n4\n"
946 a = "1\n2\n3\n4\n"
947 b = "1\n2\n3\ninsert\n4\n"
947 b = "1\n2\n3\ninsert\n4\n"
948 fa = writetemp(a)
948 fa = writetemp(a)
949 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
949 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
950 os.path.basename(fa))
950 os.path.basename(fa))
951 fd = writetemp(d)
951 fd = writetemp(d)
952
952
953 files = {}
953 files = {}
954 try:
954 try:
955 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
955 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
956 except util.Abort, e:
956 except util.Abort, e:
957 ui.write(_(" patch call failed:\n"))
957 ui.write(_(" patch call failed:\n"))
958 ui.write(" " + str(e) + "\n")
958 ui.write(" " + str(e) + "\n")
959 patchproblems += 1
959 patchproblems += 1
960 else:
960 else:
961 if list(files) != [os.path.basename(fa)]:
961 if list(files) != [os.path.basename(fa)]:
962 ui.write(_(" unexpected patch output!\n"))
962 ui.write(_(" unexpected patch output!\n"))
963 patchproblems += 1
963 patchproblems += 1
964 a = file(fa).read()
964 a = file(fa).read()
965 if a != b:
965 if a != b:
966 ui.write(_(" patch test failed!\n"))
966 ui.write(_(" patch test failed!\n"))
967 patchproblems += 1
967 patchproblems += 1
968
968
969 if patchproblems:
969 if patchproblems:
970 if ui.config('ui', 'patch'):
970 if ui.config('ui', 'patch'):
971 ui.write(_(" (Current patch tool may be incompatible with patch,"
971 ui.write(_(" (Current patch tool may be incompatible with patch,"
972 " or misconfigured. Please check your .hgrc file)\n"))
972 " or misconfigured. Please check your .hgrc file)\n"))
973 else:
973 else:
974 ui.write(_(" Internal patcher failure, please report this error"
974 ui.write(_(" Internal patcher failure, please report this error"
975 " to http://www.selenic.com/mercurial/bts\n"))
975 " to http://www.selenic.com/mercurial/bts\n"))
976 problems += patchproblems
976 problems += patchproblems
977
977
978 os.unlink(fa)
978 os.unlink(fa)
979 os.unlink(fd)
979 os.unlink(fd)
980
980
981 # editor
981 # editor
982 ui.status(_("Checking commit editor...\n"))
982 ui.status(_("Checking commit editor...\n"))
983 editor = ui.geteditor()
983 editor = ui.geteditor()
984 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
984 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
985 if not cmdpath:
985 if not cmdpath:
986 if editor == 'vi':
986 if editor == 'vi':
987 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
987 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
988 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
988 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
989 else:
989 else:
990 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
990 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
991 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
991 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
992 problems += 1
992 problems += 1
993
993
994 # check username
994 # check username
995 ui.status(_("Checking username...\n"))
995 ui.status(_("Checking username...\n"))
996 user = os.environ.get("HGUSER")
996 user = os.environ.get("HGUSER")
997 if user is None:
997 if user is None:
998 user = ui.config("ui", "username")
998 user = ui.config("ui", "username")
999 if user is None:
999 if user is None:
1000 user = os.environ.get("EMAIL")
1000 user = os.environ.get("EMAIL")
1001 if not user:
1001 if not user:
1002 ui.warn(" ")
1002 ui.warn(" ")
1003 ui.username()
1003 ui.username()
1004 ui.write(_(" (specify a username in your .hgrc file)\n"))
1004 ui.write(_(" (specify a username in your .hgrc file)\n"))
1005
1005
1006 if not problems:
1006 if not problems:
1007 ui.status(_("No problems detected\n"))
1007 ui.status(_("No problems detected\n"))
1008 else:
1008 else:
1009 ui.write(_("%s problems detected,"
1009 ui.write(_("%s problems detected,"
1010 " please check your install!\n") % problems)
1010 " please check your install!\n") % problems)
1011
1011
1012 return problems
1012 return problems
1013
1013
1014 def debugrename(ui, repo, file1, *pats, **opts):
1014 def debugrename(ui, repo, file1, *pats, **opts):
1015 """dump rename information"""
1015 """dump rename information"""
1016
1016
1017 ctx = repo[opts.get('rev')]
1017 ctx = repo[opts.get('rev')]
1018 m = cmdutil.match(repo, (file1,) + pats, opts)
1018 m = cmdutil.match(repo, (file1,) + pats, opts)
1019 for abs in ctx.walk(m):
1019 for abs in ctx.walk(m):
1020 fctx = ctx[abs]
1020 fctx = ctx[abs]
1021 o = fctx.filelog().renamed(fctx.filenode())
1021 o = fctx.filelog().renamed(fctx.filenode())
1022 rel = m.rel(abs)
1022 rel = m.rel(abs)
1023 if o:
1023 if o:
1024 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1024 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1025 else:
1025 else:
1026 ui.write(_("%s not renamed\n") % rel)
1026 ui.write(_("%s not renamed\n") % rel)
1027
1027
1028 def debugwalk(ui, repo, *pats, **opts):
1028 def debugwalk(ui, repo, *pats, **opts):
1029 """show how files match on given patterns"""
1029 """show how files match on given patterns"""
1030 m = cmdutil.match(repo, pats, opts)
1030 m = cmdutil.match(repo, pats, opts)
1031 items = list(repo.walk(m))
1031 items = list(repo.walk(m))
1032 if not items:
1032 if not items:
1033 return
1033 return
1034 fmt = 'f %%-%ds %%-%ds %%s' % (
1034 fmt = 'f %%-%ds %%-%ds %%s' % (
1035 max([len(abs) for abs in items]),
1035 max([len(abs) for abs in items]),
1036 max([len(m.rel(abs)) for abs in items]))
1036 max([len(m.rel(abs)) for abs in items]))
1037 for abs in items:
1037 for abs in items:
1038 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1038 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1039 ui.write("%s\n" % line.rstrip())
1039 ui.write("%s\n" % line.rstrip())
1040
1040
1041 def diff(ui, repo, *pats, **opts):
1041 def diff(ui, repo, *pats, **opts):
1042 """diff repository (or selected files)
1042 """diff repository (or selected files)
1043
1043
1044 Show differences between revisions for the specified files.
1044 Show differences between revisions for the specified files.
1045
1045
1046 Differences between files are shown using the unified diff format.
1046 Differences between files are shown using the unified diff format.
1047
1047
1048 NOTE: diff may generate unexpected results for merges, as it will
1048 NOTE: diff may generate unexpected results for merges, as it will
1049 default to comparing against the working directory's first parent
1049 default to comparing against the working directory's first parent
1050 changeset if no revisions are specified.
1050 changeset if no revisions are specified.
1051
1051
1052 When two revision arguments are given, then changes are shown
1052 When two revision arguments are given, then changes are shown
1053 between those revisions. If only one revision is specified then
1053 between those revisions. If only one revision is specified then
1054 that revision is compared to the working directory, and, when no
1054 that revision is compared to the working directory, and, when no
1055 revisions are specified, the working directory files are compared
1055 revisions are specified, the working directory files are compared
1056 to its parent.
1056 to its parent.
1057
1057
1058 Without the -a/--text option, diff will avoid generating diffs of
1058 Without the -a/--text option, diff will avoid generating diffs of
1059 files it detects as binary. With -a, diff will generate a diff
1059 files it detects as binary. With -a, diff will generate a diff
1060 anyway, probably with undesirable results.
1060 anyway, probably with undesirable results.
1061
1061
1062 Use the -g/--git option to generate diffs in the git extended diff
1062 Use the -g/--git option to generate diffs in the git extended diff
1063 format. For more information, read 'hg help diffs'.
1063 format. For more information, read 'hg help diffs'.
1064 """
1064 """
1065
1065
1066 revs = opts.get('rev')
1066 revs = opts.get('rev')
1067 change = opts.get('change')
1067 change = opts.get('change')
1068
1068
1069 if revs and change:
1069 if revs and change:
1070 msg = _('cannot specify --rev and --change at the same time')
1070 msg = _('cannot specify --rev and --change at the same time')
1071 raise util.Abort(msg)
1071 raise util.Abort(msg)
1072 elif change:
1072 elif change:
1073 node2 = repo.lookup(change)
1073 node2 = repo.lookup(change)
1074 node1 = repo[node2].parents()[0].node()
1074 node1 = repo[node2].parents()[0].node()
1075 else:
1075 else:
1076 node1, node2 = cmdutil.revpair(repo, revs)
1076 node1, node2 = cmdutil.revpair(repo, revs)
1077
1077
1078 m = cmdutil.match(repo, pats, opts)
1078 m = cmdutil.match(repo, pats, opts)
1079 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1079 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1080 for chunk in it:
1080 for chunk in it:
1081 repo.ui.write(chunk)
1081 ui.write(chunk)
1082
1082
1083 def export(ui, repo, *changesets, **opts):
1083 def export(ui, repo, *changesets, **opts):
1084 """dump the header and diffs for one or more changesets
1084 """dump the header and diffs for one or more changesets
1085
1085
1086 Print the changeset header and diffs for one or more revisions.
1086 Print the changeset header and diffs for one or more revisions.
1087
1087
1088 The information shown in the changeset header is: author,
1088 The information shown in the changeset header is: author,
1089 changeset hash, parent(s) and commit comment.
1089 changeset hash, parent(s) and commit comment.
1090
1090
1091 NOTE: export may generate unexpected diff output for merge
1091 NOTE: export may generate unexpected diff output for merge
1092 changesets, as it will compare the merge changeset against its
1092 changesets, as it will compare the merge changeset against its
1093 first parent only.
1093 first parent only.
1094
1094
1095 Output may be to a file, in which case the name of the file is
1095 Output may be to a file, in which case the name of the file is
1096 given using a format string. The formatting rules are as follows:
1096 given using a format string. The formatting rules are as follows:
1097
1097
1098 %% literal "%" character
1098 %% literal "%" character
1099 %H changeset hash (40 bytes of hexadecimal)
1099 %H changeset hash (40 bytes of hexadecimal)
1100 %N number of patches being generated
1100 %N number of patches being generated
1101 %R changeset revision number
1101 %R changeset revision number
1102 %b basename of the exporting repository
1102 %b basename of the exporting repository
1103 %h short-form changeset hash (12 bytes of hexadecimal)
1103 %h short-form changeset hash (12 bytes of hexadecimal)
1104 %n zero-padded sequence number, starting at 1
1104 %n zero-padded sequence number, starting at 1
1105 %r zero-padded changeset revision number
1105 %r zero-padded changeset revision number
1106
1106
1107 Without the -a/--text option, export will avoid generating diffs
1107 Without the -a/--text option, export will avoid generating diffs
1108 of files it detects as binary. With -a, export will generate a
1108 of files it detects as binary. With -a, export will generate a
1109 diff anyway, probably with undesirable results.
1109 diff anyway, probably with undesirable results.
1110
1110
1111 Use the -g/--git option to generate diffs in the git extended diff
1111 Use the -g/--git option to generate diffs in the git extended diff
1112 format. Read the diffs help topic for more information.
1112 format. Read the diffs help topic for more information.
1113
1113
1114 With the --switch-parent option, the diff will be against the
1114 With the --switch-parent option, the diff will be against the
1115 second parent. It can be useful to review a merge.
1115 second parent. It can be useful to review a merge.
1116 """
1116 """
1117 if not changesets:
1117 if not changesets:
1118 raise util.Abort(_("export requires at least one changeset"))
1118 raise util.Abort(_("export requires at least one changeset"))
1119 revs = cmdutil.revrange(repo, changesets)
1119 revs = cmdutil.revrange(repo, changesets)
1120 if len(revs) > 1:
1120 if len(revs) > 1:
1121 ui.note(_('exporting patches:\n'))
1121 ui.note(_('exporting patches:\n'))
1122 else:
1122 else:
1123 ui.note(_('exporting patch:\n'))
1123 ui.note(_('exporting patch:\n'))
1124 patch.export(repo, revs, template=opts.get('output'),
1124 patch.export(repo, revs, template=opts.get('output'),
1125 switch_parent=opts.get('switch_parent'),
1125 switch_parent=opts.get('switch_parent'),
1126 opts=patch.diffopts(ui, opts))
1126 opts=patch.diffopts(ui, opts))
1127
1127
1128 def grep(ui, repo, pattern, *pats, **opts):
1128 def grep(ui, repo, pattern, *pats, **opts):
1129 """search for a pattern in specified files and revisions
1129 """search for a pattern in specified files and revisions
1130
1130
1131 Search revisions of files for a regular expression.
1131 Search revisions of files for a regular expression.
1132
1132
1133 This command behaves differently than Unix grep. It only accepts
1133 This command behaves differently than Unix grep. It only accepts
1134 Python/Perl regexps. It searches repository history, not the
1134 Python/Perl regexps. It searches repository history, not the
1135 working directory. It always prints the revision number in which a
1135 working directory. It always prints the revision number in which a
1136 match appears.
1136 match appears.
1137
1137
1138 By default, grep only prints output for the first revision of a
1138 By default, grep only prints output for the first revision of a
1139 file in which it finds a match. To get it to print every revision
1139 file in which it finds a match. To get it to print every revision
1140 that contains a change in match status ("-" for a match that
1140 that contains a change in match status ("-" for a match that
1141 becomes a non-match, or "+" for a non-match that becomes a match),
1141 becomes a non-match, or "+" for a non-match that becomes a match),
1142 use the --all flag.
1142 use the --all flag.
1143 """
1143 """
1144 reflags = 0
1144 reflags = 0
1145 if opts.get('ignore_case'):
1145 if opts.get('ignore_case'):
1146 reflags |= re.I
1146 reflags |= re.I
1147 try:
1147 try:
1148 regexp = re.compile(pattern, reflags)
1148 regexp = re.compile(pattern, reflags)
1149 except Exception, inst:
1149 except Exception, inst:
1150 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1150 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1151 return None
1151 return None
1152 sep, eol = ':', '\n'
1152 sep, eol = ':', '\n'
1153 if opts.get('print0'):
1153 if opts.get('print0'):
1154 sep = eol = '\0'
1154 sep = eol = '\0'
1155
1155
1156 fcache = {}
1156 fcache = {}
1157 forder = []
1157 forder = []
1158 def getfile(fn):
1158 def getfile(fn):
1159 if fn not in fcache:
1159 if fn not in fcache:
1160 if len(fcache) > 20:
1160 if len(fcache) > 20:
1161 del fcache[forder.pop(0)]
1161 del fcache[forder.pop(0)]
1162 fcache[fn] = repo.file(fn)
1162 fcache[fn] = repo.file(fn)
1163 else:
1163 else:
1164 forder.remove(fn)
1164 forder.remove(fn)
1165
1165
1166 forder.append(fn)
1166 forder.append(fn)
1167 return fcache[fn]
1167 return fcache[fn]
1168
1168
1169 def matchlines(body):
1169 def matchlines(body):
1170 begin = 0
1170 begin = 0
1171 linenum = 0
1171 linenum = 0
1172 while True:
1172 while True:
1173 match = regexp.search(body, begin)
1173 match = regexp.search(body, begin)
1174 if not match:
1174 if not match:
1175 break
1175 break
1176 mstart, mend = match.span()
1176 mstart, mend = match.span()
1177 linenum += body.count('\n', begin, mstart) + 1
1177 linenum += body.count('\n', begin, mstart) + 1
1178 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1178 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1179 begin = body.find('\n', mend) + 1 or len(body)
1179 begin = body.find('\n', mend) + 1 or len(body)
1180 lend = begin - 1
1180 lend = begin - 1
1181 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1181 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1182
1182
1183 class linestate(object):
1183 class linestate(object):
1184 def __init__(self, line, linenum, colstart, colend):
1184 def __init__(self, line, linenum, colstart, colend):
1185 self.line = line
1185 self.line = line
1186 self.linenum = linenum
1186 self.linenum = linenum
1187 self.colstart = colstart
1187 self.colstart = colstart
1188 self.colend = colend
1188 self.colend = colend
1189
1189
1190 def __hash__(self):
1190 def __hash__(self):
1191 return hash((self.linenum, self.line))
1191 return hash((self.linenum, self.line))
1192
1192
1193 def __eq__(self, other):
1193 def __eq__(self, other):
1194 return self.line == other.line
1194 return self.line == other.line
1195
1195
1196 matches = {}
1196 matches = {}
1197 copies = {}
1197 copies = {}
1198 def grepbody(fn, rev, body):
1198 def grepbody(fn, rev, body):
1199 matches[rev].setdefault(fn, [])
1199 matches[rev].setdefault(fn, [])
1200 m = matches[rev][fn]
1200 m = matches[rev][fn]
1201 for lnum, cstart, cend, line in matchlines(body):
1201 for lnum, cstart, cend, line in matchlines(body):
1202 s = linestate(line, lnum, cstart, cend)
1202 s = linestate(line, lnum, cstart, cend)
1203 m.append(s)
1203 m.append(s)
1204
1204
1205 def difflinestates(a, b):
1205 def difflinestates(a, b):
1206 sm = difflib.SequenceMatcher(None, a, b)
1206 sm = difflib.SequenceMatcher(None, a, b)
1207 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1207 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1208 if tag == 'insert':
1208 if tag == 'insert':
1209 for i in xrange(blo, bhi):
1209 for i in xrange(blo, bhi):
1210 yield ('+', b[i])
1210 yield ('+', b[i])
1211 elif tag == 'delete':
1211 elif tag == 'delete':
1212 for i in xrange(alo, ahi):
1212 for i in xrange(alo, ahi):
1213 yield ('-', a[i])
1213 yield ('-', a[i])
1214 elif tag == 'replace':
1214 elif tag == 'replace':
1215 for i in xrange(alo, ahi):
1215 for i in xrange(alo, ahi):
1216 yield ('-', a[i])
1216 yield ('-', a[i])
1217 for i in xrange(blo, bhi):
1217 for i in xrange(blo, bhi):
1218 yield ('+', b[i])
1218 yield ('+', b[i])
1219
1219
1220 prev = {}
1220 prev = {}
1221 def display(fn, rev, states, prevstates):
1221 def display(fn, rev, states, prevstates):
1222 datefunc = ui.quiet and util.shortdate or util.datestr
1222 datefunc = ui.quiet and util.shortdate or util.datestr
1223 found = False
1223 found = False
1224 filerevmatches = {}
1224 filerevmatches = {}
1225 r = prev.get(fn, -1)
1225 r = prev.get(fn, -1)
1226 if opts.get('all'):
1226 if opts.get('all'):
1227 iter = difflinestates(states, prevstates)
1227 iter = difflinestates(states, prevstates)
1228 else:
1228 else:
1229 iter = [('', l) for l in prevstates]
1229 iter = [('', l) for l in prevstates]
1230 for change, l in iter:
1230 for change, l in iter:
1231 cols = [fn, str(r)]
1231 cols = [fn, str(r)]
1232 if opts.get('line_number'):
1232 if opts.get('line_number'):
1233 cols.append(str(l.linenum))
1233 cols.append(str(l.linenum))
1234 if opts.get('all'):
1234 if opts.get('all'):
1235 cols.append(change)
1235 cols.append(change)
1236 if opts.get('user'):
1236 if opts.get('user'):
1237 cols.append(ui.shortuser(get(r)[1]))
1237 cols.append(ui.shortuser(get(r)[1]))
1238 if opts.get('date'):
1238 if opts.get('date'):
1239 cols.append(datefunc(get(r)[2]))
1239 cols.append(datefunc(get(r)[2]))
1240 if opts.get('files_with_matches'):
1240 if opts.get('files_with_matches'):
1241 c = (fn, r)
1241 c = (fn, r)
1242 if c in filerevmatches:
1242 if c in filerevmatches:
1243 continue
1243 continue
1244 filerevmatches[c] = 1
1244 filerevmatches[c] = 1
1245 else:
1245 else:
1246 cols.append(l.line)
1246 cols.append(l.line)
1247 ui.write(sep.join(cols), eol)
1247 ui.write(sep.join(cols), eol)
1248 found = True
1248 found = True
1249 return found
1249 return found
1250
1250
1251 fstate = {}
1251 fstate = {}
1252 skip = {}
1252 skip = {}
1253 get = util.cachefunc(lambda r: repo[r].changeset())
1253 get = util.cachefunc(lambda r: repo[r].changeset())
1254 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1254 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1255 found = False
1255 found = False
1256 follow = opts.get('follow')
1256 follow = opts.get('follow')
1257 for st, rev, fns in changeiter:
1257 for st, rev, fns in changeiter:
1258 if st == 'window':
1258 if st == 'window':
1259 matches.clear()
1259 matches.clear()
1260 elif st == 'add':
1260 elif st == 'add':
1261 ctx = repo[rev]
1261 ctx = repo[rev]
1262 matches[rev] = {}
1262 matches[rev] = {}
1263 for fn in fns:
1263 for fn in fns:
1264 if fn in skip:
1264 if fn in skip:
1265 continue
1265 continue
1266 try:
1266 try:
1267 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1267 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1268 fstate.setdefault(fn, [])
1268 fstate.setdefault(fn, [])
1269 if follow:
1269 if follow:
1270 copied = getfile(fn).renamed(ctx.filenode(fn))
1270 copied = getfile(fn).renamed(ctx.filenode(fn))
1271 if copied:
1271 if copied:
1272 copies.setdefault(rev, {})[fn] = copied[0]
1272 copies.setdefault(rev, {})[fn] = copied[0]
1273 except error.LookupError:
1273 except error.LookupError:
1274 pass
1274 pass
1275 elif st == 'iter':
1275 elif st == 'iter':
1276 for fn, m in sorted(matches[rev].items()):
1276 for fn, m in sorted(matches[rev].items()):
1277 copy = copies.get(rev, {}).get(fn)
1277 copy = copies.get(rev, {}).get(fn)
1278 if fn in skip:
1278 if fn in skip:
1279 if copy:
1279 if copy:
1280 skip[copy] = True
1280 skip[copy] = True
1281 continue
1281 continue
1282 if fn in prev or fstate[fn]:
1282 if fn in prev or fstate[fn]:
1283 r = display(fn, rev, m, fstate[fn])
1283 r = display(fn, rev, m, fstate[fn])
1284 found = found or r
1284 found = found or r
1285 if r and not opts.get('all'):
1285 if r and not opts.get('all'):
1286 skip[fn] = True
1286 skip[fn] = True
1287 if copy:
1287 if copy:
1288 skip[copy] = True
1288 skip[copy] = True
1289 fstate[fn] = m
1289 fstate[fn] = m
1290 if copy:
1290 if copy:
1291 fstate[copy] = m
1291 fstate[copy] = m
1292 prev[fn] = rev
1292 prev[fn] = rev
1293
1293
1294 for fn, state in sorted(fstate.items()):
1294 for fn, state in sorted(fstate.items()):
1295 if fn in skip:
1295 if fn in skip:
1296 continue
1296 continue
1297 if fn not in copies.get(prev[fn], {}):
1297 if fn not in copies.get(prev[fn], {}):
1298 found = display(fn, rev, {}, state) or found
1298 found = display(fn, rev, {}, state) or found
1299 return (not found and 1) or 0
1299 return (not found and 1) or 0
1300
1300
1301 def heads(ui, repo, *branchrevs, **opts):
1301 def heads(ui, repo, *branchrevs, **opts):
1302 """show current repository heads or show branch heads
1302 """show current repository heads or show branch heads
1303
1303
1304 With no arguments, show all repository head changesets.
1304 With no arguments, show all repository head changesets.
1305
1305
1306 If branch or revisions names are given this will show the heads of
1306 If branch or revisions names are given this will show the heads of
1307 the specified branches or the branches those revisions are tagged
1307 the specified branches or the branches those revisions are tagged
1308 with.
1308 with.
1309
1309
1310 Repository "heads" are changesets that don't have child
1310 Repository "heads" are changesets that don't have child
1311 changesets. They are where development generally takes place and
1311 changesets. They are where development generally takes place and
1312 are the usual targets for update and merge operations.
1312 are the usual targets for update and merge operations.
1313
1313
1314 Branch heads are changesets that have a given branch tag, but have
1314 Branch heads are changesets that have a given branch tag, but have
1315 no child changesets with that tag. They are usually where
1315 no child changesets with that tag. They are usually where
1316 development on the given branch takes place.
1316 development on the given branch takes place.
1317 """
1317 """
1318 if opts.get('rev'):
1318 if opts.get('rev'):
1319 start = repo.lookup(opts['rev'])
1319 start = repo.lookup(opts['rev'])
1320 else:
1320 else:
1321 start = None
1321 start = None
1322 closed = not opts.get('active')
1322 closed = not opts.get('active')
1323 if not branchrevs:
1323 if not branchrevs:
1324 # Assume we're looking repo-wide heads if no revs were specified.
1324 # Assume we're looking repo-wide heads if no revs were specified.
1325 heads = repo.heads(start, closed=closed)
1325 heads = repo.heads(start, closed=closed)
1326 else:
1326 else:
1327 heads = []
1327 heads = []
1328 visitedset = set()
1328 visitedset = set()
1329 for branchrev in branchrevs:
1329 for branchrev in branchrevs:
1330 branch = repo[branchrev].branch()
1330 branch = repo[branchrev].branch()
1331 if branch in visitedset:
1331 if branch in visitedset:
1332 continue
1332 continue
1333 visitedset.add(branch)
1333 visitedset.add(branch)
1334 bheads = repo.branchheads(branch, start, closed=closed)
1334 bheads = repo.branchheads(branch, start, closed=closed)
1335 if not bheads:
1335 if not bheads:
1336 if branch != branchrev:
1336 if branch != branchrev:
1337 ui.warn(_("no changes on branch %s containing %s are "
1337 ui.warn(_("no changes on branch %s containing %s are "
1338 "reachable from %s\n")
1338 "reachable from %s\n")
1339 % (branch, branchrev, opts.get('rev')))
1339 % (branch, branchrev, opts.get('rev')))
1340 else:
1340 else:
1341 ui.warn(_("no changes on branch %s are reachable from %s\n")
1341 ui.warn(_("no changes on branch %s are reachable from %s\n")
1342 % (branch, opts.get('rev')))
1342 % (branch, opts.get('rev')))
1343 heads.extend(bheads)
1343 heads.extend(bheads)
1344 if not heads:
1344 if not heads:
1345 return 1
1345 return 1
1346 displayer = cmdutil.show_changeset(ui, repo, opts)
1346 displayer = cmdutil.show_changeset(ui, repo, opts)
1347 for n in heads:
1347 for n in heads:
1348 displayer.show(repo[n])
1348 displayer.show(repo[n])
1349
1349
1350 def help_(ui, name=None, with_version=False):
1350 def help_(ui, name=None, with_version=False):
1351 """show help for a given topic or a help overview
1351 """show help for a given topic or a help overview
1352
1352
1353 With no arguments, print a list of commands and short help.
1353 With no arguments, print a list of commands and short help.
1354
1354
1355 Given a topic, extension, or command name, print help for that
1355 Given a topic, extension, or command name, print help for that
1356 topic."""
1356 topic."""
1357 option_lists = []
1357 option_lists = []
1358
1358
1359 def addglobalopts(aliases):
1359 def addglobalopts(aliases):
1360 if ui.verbose:
1360 if ui.verbose:
1361 option_lists.append((_("global options:"), globalopts))
1361 option_lists.append((_("global options:"), globalopts))
1362 if name == 'shortlist':
1362 if name == 'shortlist':
1363 option_lists.append((_('use "hg help" for the full list '
1363 option_lists.append((_('use "hg help" for the full list '
1364 'of commands'), ()))
1364 'of commands'), ()))
1365 else:
1365 else:
1366 if name == 'shortlist':
1366 if name == 'shortlist':
1367 msg = _('use "hg help" for the full list of commands '
1367 msg = _('use "hg help" for the full list of commands '
1368 'or "hg -v" for details')
1368 'or "hg -v" for details')
1369 elif aliases:
1369 elif aliases:
1370 msg = _('use "hg -v help%s" to show aliases and '
1370 msg = _('use "hg -v help%s" to show aliases and '
1371 'global options') % (name and " " + name or "")
1371 'global options') % (name and " " + name or "")
1372 else:
1372 else:
1373 msg = _('use "hg -v help %s" to show global options') % name
1373 msg = _('use "hg -v help %s" to show global options') % name
1374 option_lists.append((msg, ()))
1374 option_lists.append((msg, ()))
1375
1375
1376 def helpcmd(name):
1376 def helpcmd(name):
1377 if with_version:
1377 if with_version:
1378 version_(ui)
1378 version_(ui)
1379 ui.write('\n')
1379 ui.write('\n')
1380
1380
1381 try:
1381 try:
1382 aliases, i = cmdutil.findcmd(name, table, False)
1382 aliases, i = cmdutil.findcmd(name, table, False)
1383 except error.AmbiguousCommand, inst:
1383 except error.AmbiguousCommand, inst:
1384 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1384 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1385 helplist(_('list of commands:\n\n'), select)
1385 helplist(_('list of commands:\n\n'), select)
1386 return
1386 return
1387
1387
1388 # synopsis
1388 # synopsis
1389 if len(i) > 2:
1389 if len(i) > 2:
1390 if i[2].startswith('hg'):
1390 if i[2].startswith('hg'):
1391 ui.write("%s\n" % i[2])
1391 ui.write("%s\n" % i[2])
1392 else:
1392 else:
1393 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1393 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1394 else:
1394 else:
1395 ui.write('hg %s\n' % aliases[0])
1395 ui.write('hg %s\n' % aliases[0])
1396
1396
1397 # aliases
1397 # aliases
1398 if not ui.quiet and len(aliases) > 1:
1398 if not ui.quiet and len(aliases) > 1:
1399 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1399 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1400
1400
1401 # description
1401 # description
1402 doc = gettext(i[0].__doc__)
1402 doc = gettext(i[0].__doc__)
1403 if not doc:
1403 if not doc:
1404 doc = _("(no help text available)")
1404 doc = _("(no help text available)")
1405 if ui.quiet:
1405 if ui.quiet:
1406 doc = doc.splitlines(0)[0]
1406 doc = doc.splitlines(0)[0]
1407 ui.write("\n%s\n" % doc.rstrip())
1407 ui.write("\n%s\n" % doc.rstrip())
1408
1408
1409 if not ui.quiet:
1409 if not ui.quiet:
1410 # options
1410 # options
1411 if i[1]:
1411 if i[1]:
1412 option_lists.append((_("options:\n"), i[1]))
1412 option_lists.append((_("options:\n"), i[1]))
1413
1413
1414 addglobalopts(False)
1414 addglobalopts(False)
1415
1415
1416 def helplist(header, select=None):
1416 def helplist(header, select=None):
1417 h = {}
1417 h = {}
1418 cmds = {}
1418 cmds = {}
1419 for c, e in table.iteritems():
1419 for c, e in table.iteritems():
1420 f = c.split("|", 1)[0]
1420 f = c.split("|", 1)[0]
1421 if select and not select(f):
1421 if select and not select(f):
1422 continue
1422 continue
1423 if (not select and name != 'shortlist' and
1423 if (not select and name != 'shortlist' and
1424 e[0].__module__ != __name__):
1424 e[0].__module__ != __name__):
1425 continue
1425 continue
1426 if name == "shortlist" and not f.startswith("^"):
1426 if name == "shortlist" and not f.startswith("^"):
1427 continue
1427 continue
1428 f = f.lstrip("^")
1428 f = f.lstrip("^")
1429 if not ui.debugflag and f.startswith("debug"):
1429 if not ui.debugflag and f.startswith("debug"):
1430 continue
1430 continue
1431 doc = gettext(e[0].__doc__)
1431 doc = gettext(e[0].__doc__)
1432 if not doc:
1432 if not doc:
1433 doc = _("(no help text available)")
1433 doc = _("(no help text available)")
1434 h[f] = doc.splitlines(0)[0].rstrip()
1434 h[f] = doc.splitlines(0)[0].rstrip()
1435 cmds[f] = c.lstrip("^")
1435 cmds[f] = c.lstrip("^")
1436
1436
1437 if not h:
1437 if not h:
1438 ui.status(_('no commands defined\n'))
1438 ui.status(_('no commands defined\n'))
1439 return
1439 return
1440
1440
1441 ui.status(header)
1441 ui.status(header)
1442 fns = sorted(h)
1442 fns = sorted(h)
1443 m = max(map(len, fns))
1443 m = max(map(len, fns))
1444 for f in fns:
1444 for f in fns:
1445 if ui.verbose:
1445 if ui.verbose:
1446 commands = cmds[f].replace("|",", ")
1446 commands = cmds[f].replace("|",", ")
1447 ui.write(" %s:\n %s\n"%(commands, h[f]))
1447 ui.write(" %s:\n %s\n"%(commands, h[f]))
1448 else:
1448 else:
1449 ui.write(' %-*s %s\n' % (m, f, h[f]))
1449 ui.write(' %-*s %s\n' % (m, f, h[f]))
1450
1450
1451 exts = list(extensions.extensions())
1451 exts = list(extensions.extensions())
1452 if exts and name != 'shortlist':
1452 if exts and name != 'shortlist':
1453 ui.write(_('\nenabled extensions:\n\n'))
1453 ui.write(_('\nenabled extensions:\n\n'))
1454 maxlength = 0
1454 maxlength = 0
1455 exthelps = []
1455 exthelps = []
1456 for ename, ext in exts:
1456 for ename, ext in exts:
1457 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1457 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1458 ename = ename.split('.')[-1]
1458 ename = ename.split('.')[-1]
1459 maxlength = max(len(ename), maxlength)
1459 maxlength = max(len(ename), maxlength)
1460 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1460 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1461 for ename, text in exthelps:
1461 for ename, text in exthelps:
1462 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1462 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1463
1463
1464 if not ui.quiet:
1464 if not ui.quiet:
1465 addglobalopts(True)
1465 addglobalopts(True)
1466
1466
1467 def helptopic(name):
1467 def helptopic(name):
1468 for names, header, doc in help.helptable:
1468 for names, header, doc in help.helptable:
1469 if name in names:
1469 if name in names:
1470 break
1470 break
1471 else:
1471 else:
1472 raise error.UnknownCommand(name)
1472 raise error.UnknownCommand(name)
1473
1473
1474 # description
1474 # description
1475 if not doc:
1475 if not doc:
1476 doc = _("(no help text available)")
1476 doc = _("(no help text available)")
1477 if hasattr(doc, '__call__'):
1477 if hasattr(doc, '__call__'):
1478 doc = doc()
1478 doc = doc()
1479
1479
1480 ui.write("%s\n" % header)
1480 ui.write("%s\n" % header)
1481 ui.write("%s\n" % doc.rstrip())
1481 ui.write("%s\n" % doc.rstrip())
1482
1482
1483 def helpext(name):
1483 def helpext(name):
1484 try:
1484 try:
1485 mod = extensions.find(name)
1485 mod = extensions.find(name)
1486 except KeyError:
1486 except KeyError:
1487 raise error.UnknownCommand(name)
1487 raise error.UnknownCommand(name)
1488
1488
1489 doc = gettext(mod.__doc__) or _('no help text available')
1489 doc = gettext(mod.__doc__) or _('no help text available')
1490 doc = doc.splitlines(0)
1490 doc = doc.splitlines(0)
1491 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1491 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1492 for d in doc[1:]:
1492 for d in doc[1:]:
1493 ui.write(d, '\n')
1493 ui.write(d, '\n')
1494
1494
1495 ui.status('\n')
1495 ui.status('\n')
1496
1496
1497 try:
1497 try:
1498 ct = mod.cmdtable
1498 ct = mod.cmdtable
1499 except AttributeError:
1499 except AttributeError:
1500 ct = {}
1500 ct = {}
1501
1501
1502 modcmds = set([c.split('|', 1)[0] for c in ct])
1502 modcmds = set([c.split('|', 1)[0] for c in ct])
1503 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1503 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1504
1504
1505 if name and name != 'shortlist':
1505 if name and name != 'shortlist':
1506 i = None
1506 i = None
1507 for f in (helptopic, helpcmd, helpext):
1507 for f in (helptopic, helpcmd, helpext):
1508 try:
1508 try:
1509 f(name)
1509 f(name)
1510 i = None
1510 i = None
1511 break
1511 break
1512 except error.UnknownCommand, inst:
1512 except error.UnknownCommand, inst:
1513 i = inst
1513 i = inst
1514 if i:
1514 if i:
1515 raise i
1515 raise i
1516
1516
1517 else:
1517 else:
1518 # program name
1518 # program name
1519 if ui.verbose or with_version:
1519 if ui.verbose or with_version:
1520 version_(ui)
1520 version_(ui)
1521 else:
1521 else:
1522 ui.status(_("Mercurial Distributed SCM\n"))
1522 ui.status(_("Mercurial Distributed SCM\n"))
1523 ui.status('\n')
1523 ui.status('\n')
1524
1524
1525 # list of commands
1525 # list of commands
1526 if name == "shortlist":
1526 if name == "shortlist":
1527 header = _('basic commands:\n\n')
1527 header = _('basic commands:\n\n')
1528 else:
1528 else:
1529 header = _('list of commands:\n\n')
1529 header = _('list of commands:\n\n')
1530
1530
1531 helplist(header)
1531 helplist(header)
1532
1532
1533 # list all option lists
1533 # list all option lists
1534 opt_output = []
1534 opt_output = []
1535 for title, options in option_lists:
1535 for title, options in option_lists:
1536 opt_output.append(("\n%s" % title, None))
1536 opt_output.append(("\n%s" % title, None))
1537 for shortopt, longopt, default, desc in options:
1537 for shortopt, longopt, default, desc in options:
1538 if "DEPRECATED" in desc and not ui.verbose: continue
1538 if "DEPRECATED" in desc and not ui.verbose: continue
1539 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1539 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1540 longopt and " --%s" % longopt),
1540 longopt and " --%s" % longopt),
1541 "%s%s" % (desc,
1541 "%s%s" % (desc,
1542 default
1542 default
1543 and _(" (default: %s)") % default
1543 and _(" (default: %s)") % default
1544 or "")))
1544 or "")))
1545
1545
1546 if not name:
1546 if not name:
1547 ui.write(_("\nadditional help topics:\n\n"))
1547 ui.write(_("\nadditional help topics:\n\n"))
1548 topics = []
1548 topics = []
1549 for names, header, doc in help.helptable:
1549 for names, header, doc in help.helptable:
1550 names = [(-len(name), name) for name in names]
1550 names = [(-len(name), name) for name in names]
1551 names.sort()
1551 names.sort()
1552 topics.append((names[0][1], header))
1552 topics.append((names[0][1], header))
1553 topics_len = max([len(s[0]) for s in topics])
1553 topics_len = max([len(s[0]) for s in topics])
1554 for t, desc in topics:
1554 for t, desc in topics:
1555 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1555 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1556
1556
1557 if opt_output:
1557 if opt_output:
1558 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1558 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1559 for first, second in opt_output:
1559 for first, second in opt_output:
1560 if second:
1560 if second:
1561 # wrap descriptions at 70 characters, just like the
1561 # wrap descriptions at 70 characters, just like the
1562 # main help texts
1562 # main help texts
1563 second = textwrap.wrap(second, width=70 - opts_len - 3)
1563 second = textwrap.wrap(second, width=70 - opts_len - 3)
1564 pad = '\n' + ' ' * (opts_len + 3)
1564 pad = '\n' + ' ' * (opts_len + 3)
1565 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1565 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1566 else:
1566 else:
1567 ui.write("%s\n" % first)
1567 ui.write("%s\n" % first)
1568
1568
1569 def identify(ui, repo, source=None,
1569 def identify(ui, repo, source=None,
1570 rev=None, num=None, id=None, branch=None, tags=None):
1570 rev=None, num=None, id=None, branch=None, tags=None):
1571 """identify the working copy or specified revision
1571 """identify the working copy or specified revision
1572
1572
1573 With no revision, print a summary of the current state of the
1573 With no revision, print a summary of the current state of the
1574 repository.
1574 repository.
1575
1575
1576 With a path, do a lookup in another repository.
1576 With a path, do a lookup in another repository.
1577
1577
1578 This summary identifies the repository state using one or two
1578 This summary identifies the repository state using one or two
1579 parent hash identifiers, followed by a "+" if there are
1579 parent hash identifiers, followed by a "+" if there are
1580 uncommitted changes in the working directory, a list of tags for
1580 uncommitted changes in the working directory, a list of tags for
1581 this revision and a branch name for non-default branches.
1581 this revision and a branch name for non-default branches.
1582 """
1582 """
1583
1583
1584 if not repo and not source:
1584 if not repo and not source:
1585 raise util.Abort(_("There is no Mercurial repository here "
1585 raise util.Abort(_("There is no Mercurial repository here "
1586 "(.hg not found)"))
1586 "(.hg not found)"))
1587
1587
1588 hexfunc = ui.debugflag and hex or short
1588 hexfunc = ui.debugflag and hex or short
1589 default = not (num or id or branch or tags)
1589 default = not (num or id or branch or tags)
1590 output = []
1590 output = []
1591
1591
1592 revs = []
1592 revs = []
1593 if source:
1593 if source:
1594 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1594 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1595 repo = hg.repository(ui, source)
1595 repo = hg.repository(ui, source)
1596
1596
1597 if not repo.local():
1597 if not repo.local():
1598 if not rev and revs:
1598 if not rev and revs:
1599 rev = revs[0]
1599 rev = revs[0]
1600 if not rev:
1600 if not rev:
1601 rev = "tip"
1601 rev = "tip"
1602 if num or branch or tags:
1602 if num or branch or tags:
1603 raise util.Abort(
1603 raise util.Abort(
1604 "can't query remote revision number, branch, or tags")
1604 "can't query remote revision number, branch, or tags")
1605 output = [hexfunc(repo.lookup(rev))]
1605 output = [hexfunc(repo.lookup(rev))]
1606 elif not rev:
1606 elif not rev:
1607 ctx = repo[None]
1607 ctx = repo[None]
1608 parents = ctx.parents()
1608 parents = ctx.parents()
1609 changed = False
1609 changed = False
1610 if default or id or num:
1610 if default or id or num:
1611 changed = ctx.files() + ctx.deleted()
1611 changed = ctx.files() + ctx.deleted()
1612 if default or id:
1612 if default or id:
1613 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1613 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1614 (changed) and "+" or "")]
1614 (changed) and "+" or "")]
1615 if num:
1615 if num:
1616 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1616 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1617 (changed) and "+" or ""))
1617 (changed) and "+" or ""))
1618 else:
1618 else:
1619 ctx = repo[rev]
1619 ctx = repo[rev]
1620 if default or id:
1620 if default or id:
1621 output = [hexfunc(ctx.node())]
1621 output = [hexfunc(ctx.node())]
1622 if num:
1622 if num:
1623 output.append(str(ctx.rev()))
1623 output.append(str(ctx.rev()))
1624
1624
1625 if repo.local() and default and not ui.quiet:
1625 if repo.local() and default and not ui.quiet:
1626 b = encoding.tolocal(ctx.branch())
1626 b = encoding.tolocal(ctx.branch())
1627 if b != 'default':
1627 if b != 'default':
1628 output.append("(%s)" % b)
1628 output.append("(%s)" % b)
1629
1629
1630 # multiple tags for a single parent separated by '/'
1630 # multiple tags for a single parent separated by '/'
1631 t = "/".join(ctx.tags())
1631 t = "/".join(ctx.tags())
1632 if t:
1632 if t:
1633 output.append(t)
1633 output.append(t)
1634
1634
1635 if branch:
1635 if branch:
1636 output.append(encoding.tolocal(ctx.branch()))
1636 output.append(encoding.tolocal(ctx.branch()))
1637
1637
1638 if tags:
1638 if tags:
1639 output.extend(ctx.tags())
1639 output.extend(ctx.tags())
1640
1640
1641 ui.write("%s\n" % ' '.join(output))
1641 ui.write("%s\n" % ' '.join(output))
1642
1642
1643 def import_(ui, repo, patch1, *patches, **opts):
1643 def import_(ui, repo, patch1, *patches, **opts):
1644 """import an ordered set of patches
1644 """import an ordered set of patches
1645
1645
1646 Import a list of patches and commit them individually.
1646 Import a list of patches and commit them individually.
1647
1647
1648 If there are outstanding changes in the working directory, import
1648 If there are outstanding changes in the working directory, import
1649 will abort unless given the -f/--force flag.
1649 will abort unless given the -f/--force flag.
1650
1650
1651 You can import a patch straight from a mail message. Even patches
1651 You can import a patch straight from a mail message. Even patches
1652 as attachments work (body part must be type text/plain or
1652 as attachments work (body part must be type text/plain or
1653 text/x-patch to be used). From and Subject headers of email
1653 text/x-patch to be used). From and Subject headers of email
1654 message are used as default committer and commit message. All
1654 message are used as default committer and commit message. All
1655 text/plain body parts before first diff are added to commit
1655 text/plain body parts before first diff are added to commit
1656 message.
1656 message.
1657
1657
1658 If the imported patch was generated by hg export, user and
1658 If the imported patch was generated by hg export, user and
1659 description from patch override values from message headers and
1659 description from patch override values from message headers and
1660 body. Values given on command line with -m/--message and -u/--user
1660 body. Values given on command line with -m/--message and -u/--user
1661 override these.
1661 override these.
1662
1662
1663 If --exact is specified, import will set the working directory to
1663 If --exact is specified, import will set the working directory to
1664 the parent of each patch before applying it, and will abort if the
1664 the parent of each patch before applying it, and will abort if the
1665 resulting changeset has a different ID than the one recorded in
1665 resulting changeset has a different ID than the one recorded in
1666 the patch. This may happen due to character set problems or other
1666 the patch. This may happen due to character set problems or other
1667 deficiencies in the text patch format.
1667 deficiencies in the text patch format.
1668
1668
1669 With -s/--similarity, hg will attempt to discover renames and
1669 With -s/--similarity, hg will attempt to discover renames and
1670 copies in the patch in the same way as 'addremove'.
1670 copies in the patch in the same way as 'addremove'.
1671
1671
1672 To read a patch from standard input, use patch name "-". See 'hg
1672 To read a patch from standard input, use patch name "-". See 'hg
1673 help dates' for a list of formats valid for -d/--date.
1673 help dates' for a list of formats valid for -d/--date.
1674 """
1674 """
1675 patches = (patch1,) + patches
1675 patches = (patch1,) + patches
1676
1676
1677 date = opts.get('date')
1677 date = opts.get('date')
1678 if date:
1678 if date:
1679 opts['date'] = util.parsedate(date)
1679 opts['date'] = util.parsedate(date)
1680
1680
1681 try:
1681 try:
1682 sim = float(opts.get('similarity') or 0)
1682 sim = float(opts.get('similarity') or 0)
1683 except ValueError:
1683 except ValueError:
1684 raise util.Abort(_('similarity must be a number'))
1684 raise util.Abort(_('similarity must be a number'))
1685 if sim < 0 or sim > 100:
1685 if sim < 0 or sim > 100:
1686 raise util.Abort(_('similarity must be between 0 and 100'))
1686 raise util.Abort(_('similarity must be between 0 and 100'))
1687
1687
1688 if opts.get('exact') or not opts.get('force'):
1688 if opts.get('exact') or not opts.get('force'):
1689 cmdutil.bail_if_changed(repo)
1689 cmdutil.bail_if_changed(repo)
1690
1690
1691 d = opts["base"]
1691 d = opts["base"]
1692 strip = opts["strip"]
1692 strip = opts["strip"]
1693 wlock = lock = None
1693 wlock = lock = None
1694 try:
1694 try:
1695 wlock = repo.wlock()
1695 wlock = repo.wlock()
1696 lock = repo.lock()
1696 lock = repo.lock()
1697 for p in patches:
1697 for p in patches:
1698 pf = os.path.join(d, p)
1698 pf = os.path.join(d, p)
1699
1699
1700 if pf == '-':
1700 if pf == '-':
1701 ui.status(_("applying patch from stdin\n"))
1701 ui.status(_("applying patch from stdin\n"))
1702 pf = sys.stdin
1702 pf = sys.stdin
1703 else:
1703 else:
1704 ui.status(_("applying %s\n") % p)
1704 ui.status(_("applying %s\n") % p)
1705 pf = url.open(ui, pf)
1705 pf = url.open(ui, pf)
1706 data = patch.extract(ui, pf)
1706 data = patch.extract(ui, pf)
1707 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1707 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1708
1708
1709 if tmpname is None:
1709 if tmpname is None:
1710 raise util.Abort(_('no diffs found'))
1710 raise util.Abort(_('no diffs found'))
1711
1711
1712 try:
1712 try:
1713 cmdline_message = cmdutil.logmessage(opts)
1713 cmdline_message = cmdutil.logmessage(opts)
1714 if cmdline_message:
1714 if cmdline_message:
1715 # pickup the cmdline msg
1715 # pickup the cmdline msg
1716 message = cmdline_message
1716 message = cmdline_message
1717 elif message:
1717 elif message:
1718 # pickup the patch msg
1718 # pickup the patch msg
1719 message = message.strip()
1719 message = message.strip()
1720 else:
1720 else:
1721 # launch the editor
1721 # launch the editor
1722 message = None
1722 message = None
1723 ui.debug(_('message:\n%s\n') % message)
1723 ui.debug(_('message:\n%s\n') % message)
1724
1724
1725 wp = repo.parents()
1725 wp = repo.parents()
1726 if opts.get('exact'):
1726 if opts.get('exact'):
1727 if not nodeid or not p1:
1727 if not nodeid or not p1:
1728 raise util.Abort(_('not a mercurial patch'))
1728 raise util.Abort(_('not a mercurial patch'))
1729 p1 = repo.lookup(p1)
1729 p1 = repo.lookup(p1)
1730 p2 = repo.lookup(p2 or hex(nullid))
1730 p2 = repo.lookup(p2 or hex(nullid))
1731
1731
1732 if p1 != wp[0].node():
1732 if p1 != wp[0].node():
1733 hg.clean(repo, p1)
1733 hg.clean(repo, p1)
1734 repo.dirstate.setparents(p1, p2)
1734 repo.dirstate.setparents(p1, p2)
1735 elif p2:
1735 elif p2:
1736 try:
1736 try:
1737 p1 = repo.lookup(p1)
1737 p1 = repo.lookup(p1)
1738 p2 = repo.lookup(p2)
1738 p2 = repo.lookup(p2)
1739 if p1 == wp[0].node():
1739 if p1 == wp[0].node():
1740 repo.dirstate.setparents(p1, p2)
1740 repo.dirstate.setparents(p1, p2)
1741 except error.RepoError:
1741 except error.RepoError:
1742 pass
1742 pass
1743 if opts.get('exact') or opts.get('import_branch'):
1743 if opts.get('exact') or opts.get('import_branch'):
1744 repo.dirstate.setbranch(branch or 'default')
1744 repo.dirstate.setbranch(branch or 'default')
1745
1745
1746 files = {}
1746 files = {}
1747 try:
1747 try:
1748 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1748 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1749 files=files)
1749 files=files)
1750 finally:
1750 finally:
1751 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1751 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1752 if not opts.get('no_commit'):
1752 if not opts.get('no_commit'):
1753 n = repo.commit(files, message, opts.get('user') or user,
1753 n = repo.commit(files, message, opts.get('user') or user,
1754 opts.get('date') or date,
1754 opts.get('date') or date,
1755 editor=cmdutil.commiteditor)
1755 editor=cmdutil.commiteditor)
1756 if opts.get('exact'):
1756 if opts.get('exact'):
1757 if hex(n) != nodeid:
1757 if hex(n) != nodeid:
1758 repo.rollback()
1758 repo.rollback()
1759 raise util.Abort(_('patch is damaged'
1759 raise util.Abort(_('patch is damaged'
1760 ' or loses information'))
1760 ' or loses information'))
1761 # Force a dirstate write so that the next transaction
1761 # Force a dirstate write so that the next transaction
1762 # backups an up-do-date file.
1762 # backups an up-do-date file.
1763 repo.dirstate.write()
1763 repo.dirstate.write()
1764 finally:
1764 finally:
1765 os.unlink(tmpname)
1765 os.unlink(tmpname)
1766 finally:
1766 finally:
1767 release(lock, wlock)
1767 release(lock, wlock)
1768
1768
1769 def incoming(ui, repo, source="default", **opts):
1769 def incoming(ui, repo, source="default", **opts):
1770 """show new changesets found in source
1770 """show new changesets found in source
1771
1771
1772 Show new changesets found in the specified path/URL or the default
1772 Show new changesets found in the specified path/URL or the default
1773 pull location. These are the changesets that would be pulled if a
1773 pull location. These are the changesets that would be pulled if a
1774 pull was requested.
1774 pull was requested.
1775
1775
1776 For remote repository, using --bundle avoids downloading the
1776 For remote repository, using --bundle avoids downloading the
1777 changesets twice if the incoming is followed by a pull.
1777 changesets twice if the incoming is followed by a pull.
1778
1778
1779 See pull for valid source format details.
1779 See pull for valid source format details.
1780 """
1780 """
1781 limit = cmdutil.loglimit(opts)
1781 limit = cmdutil.loglimit(opts)
1782 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1782 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1783 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1783 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1784 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1784 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1785 if revs:
1785 if revs:
1786 revs = [other.lookup(rev) for rev in revs]
1786 revs = [other.lookup(rev) for rev in revs]
1787 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1787 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1788 force=opts["force"])
1788 force=opts["force"])
1789 if not incoming:
1789 if not incoming:
1790 try:
1790 try:
1791 os.unlink(opts["bundle"])
1791 os.unlink(opts["bundle"])
1792 except:
1792 except:
1793 pass
1793 pass
1794 ui.status(_("no changes found\n"))
1794 ui.status(_("no changes found\n"))
1795 return 1
1795 return 1
1796
1796
1797 cleanup = None
1797 cleanup = None
1798 try:
1798 try:
1799 fname = opts["bundle"]
1799 fname = opts["bundle"]
1800 if fname or not other.local():
1800 if fname or not other.local():
1801 # create a bundle (uncompressed if other repo is not local)
1801 # create a bundle (uncompressed if other repo is not local)
1802
1802
1803 if revs is None and other.capable('changegroupsubset'):
1803 if revs is None and other.capable('changegroupsubset'):
1804 revs = rheads
1804 revs = rheads
1805
1805
1806 if revs is None:
1806 if revs is None:
1807 cg = other.changegroup(incoming, "incoming")
1807 cg = other.changegroup(incoming, "incoming")
1808 else:
1808 else:
1809 cg = other.changegroupsubset(incoming, revs, 'incoming')
1809 cg = other.changegroupsubset(incoming, revs, 'incoming')
1810 bundletype = other.local() and "HG10BZ" or "HG10UN"
1810 bundletype = other.local() and "HG10BZ" or "HG10UN"
1811 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1811 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1812 # keep written bundle?
1812 # keep written bundle?
1813 if opts["bundle"]:
1813 if opts["bundle"]:
1814 cleanup = None
1814 cleanup = None
1815 if not other.local():
1815 if not other.local():
1816 # use the created uncompressed bundlerepo
1816 # use the created uncompressed bundlerepo
1817 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1817 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1818
1818
1819 o = other.changelog.nodesbetween(incoming, revs)[0]
1819 o = other.changelog.nodesbetween(incoming, revs)[0]
1820 if opts.get('newest_first'):
1820 if opts.get('newest_first'):
1821 o.reverse()
1821 o.reverse()
1822 displayer = cmdutil.show_changeset(ui, other, opts)
1822 displayer = cmdutil.show_changeset(ui, other, opts)
1823 count = 0
1823 count = 0
1824 for n in o:
1824 for n in o:
1825 if count >= limit:
1825 if count >= limit:
1826 break
1826 break
1827 parents = [p for p in other.changelog.parents(n) if p != nullid]
1827 parents = [p for p in other.changelog.parents(n) if p != nullid]
1828 if opts.get('no_merges') and len(parents) == 2:
1828 if opts.get('no_merges') and len(parents) == 2:
1829 continue
1829 continue
1830 count += 1
1830 count += 1
1831 displayer.show(other[n])
1831 displayer.show(other[n])
1832 finally:
1832 finally:
1833 if hasattr(other, 'close'):
1833 if hasattr(other, 'close'):
1834 other.close()
1834 other.close()
1835 if cleanup:
1835 if cleanup:
1836 os.unlink(cleanup)
1836 os.unlink(cleanup)
1837
1837
1838 def init(ui, dest=".", **opts):
1838 def init(ui, dest=".", **opts):
1839 """create a new repository in the given directory
1839 """create a new repository in the given directory
1840
1840
1841 Initialize a new repository in the given directory. If the given
1841 Initialize a new repository in the given directory. If the given
1842 directory does not exist, it is created.
1842 directory does not exist, it is created.
1843
1843
1844 If no directory is given, the current directory is used.
1844 If no directory is given, the current directory is used.
1845
1845
1846 It is possible to specify an ssh:// URL as the destination.
1846 It is possible to specify an ssh:// URL as the destination.
1847 See 'hg help urls' for more information.
1847 See 'hg help urls' for more information.
1848 """
1848 """
1849 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1849 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1850
1850
1851 def locate(ui, repo, *pats, **opts):
1851 def locate(ui, repo, *pats, **opts):
1852 """locate files matching specific patterns
1852 """locate files matching specific patterns
1853
1853
1854 Print all files under Mercurial control whose names match the
1854 Print all files under Mercurial control whose names match the
1855 given patterns.
1855 given patterns.
1856
1856
1857 This command searches the entire repository by default. To search
1857 This command searches the entire repository by default. To search
1858 just the current directory and its subdirectories, use
1858 just the current directory and its subdirectories, use
1859 "--include .".
1859 "--include .".
1860
1860
1861 If no patterns are given to match, this command prints all file
1861 If no patterns are given to match, this command prints all file
1862 names.
1862 names.
1863
1863
1864 If you want to feed the output of this command into the "xargs"
1864 If you want to feed the output of this command into the "xargs"
1865 command, use the -0 option to both this command and "xargs". This
1865 command, use the -0 option to both this command and "xargs". This
1866 will avoid the problem of "xargs" treating single filenames that
1866 will avoid the problem of "xargs" treating single filenames that
1867 contain white space as multiple filenames.
1867 contain white space as multiple filenames.
1868 """
1868 """
1869 end = opts.get('print0') and '\0' or '\n'
1869 end = opts.get('print0') and '\0' or '\n'
1870 rev = opts.get('rev') or None
1870 rev = opts.get('rev') or None
1871
1871
1872 ret = 1
1872 ret = 1
1873 m = cmdutil.match(repo, pats, opts, default='relglob')
1873 m = cmdutil.match(repo, pats, opts, default='relglob')
1874 m.bad = lambda x,y: False
1874 m.bad = lambda x,y: False
1875 for abs in repo[rev].walk(m):
1875 for abs in repo[rev].walk(m):
1876 if not rev and abs not in repo.dirstate:
1876 if not rev and abs not in repo.dirstate:
1877 continue
1877 continue
1878 if opts.get('fullpath'):
1878 if opts.get('fullpath'):
1879 ui.write(repo.wjoin(abs), end)
1879 ui.write(repo.wjoin(abs), end)
1880 else:
1880 else:
1881 ui.write(((pats and m.rel(abs)) or abs), end)
1881 ui.write(((pats and m.rel(abs)) or abs), end)
1882 ret = 0
1882 ret = 0
1883
1883
1884 return ret
1884 return ret
1885
1885
1886 def log(ui, repo, *pats, **opts):
1886 def log(ui, repo, *pats, **opts):
1887 """show revision history of entire repository or files
1887 """show revision history of entire repository or files
1888
1888
1889 Print the revision history of the specified files or the entire
1889 Print the revision history of the specified files or the entire
1890 project.
1890 project.
1891
1891
1892 File history is shown without following rename or copy history of
1892 File history is shown without following rename or copy history of
1893 files. Use -f/--follow with a file name to follow history across
1893 files. Use -f/--follow with a file name to follow history across
1894 renames and copies. --follow without a file name will only show
1894 renames and copies. --follow without a file name will only show
1895 ancestors or descendants of the starting revision. --follow-first
1895 ancestors or descendants of the starting revision. --follow-first
1896 only follows the first parent of merge revisions.
1896 only follows the first parent of merge revisions.
1897
1897
1898 If no revision range is specified, the default is tip:0 unless
1898 If no revision range is specified, the default is tip:0 unless
1899 --follow is set, in which case the working directory parent is
1899 --follow is set, in which case the working directory parent is
1900 used as the starting revision.
1900 used as the starting revision.
1901
1901
1902 See 'hg help dates' for a list of formats valid for -d/--date.
1902 See 'hg help dates' for a list of formats valid for -d/--date.
1903
1903
1904 By default this command outputs: changeset id and hash, tags,
1904 By default this command outputs: changeset id and hash, tags,
1905 non-trivial parents, user, date and time, and a summary for each
1905 non-trivial parents, user, date and time, and a summary for each
1906 commit. When the -v/--verbose switch is used, the list of changed
1906 commit. When the -v/--verbose switch is used, the list of changed
1907 files and full commit message is shown.
1907 files and full commit message is shown.
1908
1908
1909 NOTE: log -p/--patch may generate unexpected diff output for merge
1909 NOTE: log -p/--patch may generate unexpected diff output for merge
1910 changesets, as it will only compare the merge changeset against
1910 changesets, as it will only compare the merge changeset against
1911 its first parent. Also, the files: list will only reflect files
1911 its first parent. Also, the files: list will only reflect files
1912 that are different from BOTH parents.
1912 that are different from BOTH parents.
1913
1913
1914 """
1914 """
1915
1915
1916 get = util.cachefunc(lambda r: repo[r].changeset())
1916 get = util.cachefunc(lambda r: repo[r].changeset())
1917 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1917 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1918
1918
1919 limit = cmdutil.loglimit(opts)
1919 limit = cmdutil.loglimit(opts)
1920 count = 0
1920 count = 0
1921
1921
1922 if opts.get('copies') and opts.get('rev'):
1922 if opts.get('copies') and opts.get('rev'):
1923 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1923 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1924 else:
1924 else:
1925 endrev = len(repo)
1925 endrev = len(repo)
1926 rcache = {}
1926 rcache = {}
1927 ncache = {}
1927 ncache = {}
1928 def getrenamed(fn, rev):
1928 def getrenamed(fn, rev):
1929 '''looks up all renames for a file (up to endrev) the first
1929 '''looks up all renames for a file (up to endrev) the first
1930 time the file is given. It indexes on the changerev and only
1930 time the file is given. It indexes on the changerev and only
1931 parses the manifest if linkrev != changerev.
1931 parses the manifest if linkrev != changerev.
1932 Returns rename info for fn at changerev rev.'''
1932 Returns rename info for fn at changerev rev.'''
1933 if fn not in rcache:
1933 if fn not in rcache:
1934 rcache[fn] = {}
1934 rcache[fn] = {}
1935 ncache[fn] = {}
1935 ncache[fn] = {}
1936 fl = repo.file(fn)
1936 fl = repo.file(fn)
1937 for i in fl:
1937 for i in fl:
1938 node = fl.node(i)
1938 node = fl.node(i)
1939 lr = fl.linkrev(i)
1939 lr = fl.linkrev(i)
1940 renamed = fl.renamed(node)
1940 renamed = fl.renamed(node)
1941 rcache[fn][lr] = renamed
1941 rcache[fn][lr] = renamed
1942 if renamed:
1942 if renamed:
1943 ncache[fn][node] = renamed
1943 ncache[fn][node] = renamed
1944 if lr >= endrev:
1944 if lr >= endrev:
1945 break
1945 break
1946 if rev in rcache[fn]:
1946 if rev in rcache[fn]:
1947 return rcache[fn][rev]
1947 return rcache[fn][rev]
1948
1948
1949 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1949 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1950 # filectx logic.
1950 # filectx logic.
1951
1951
1952 try:
1952 try:
1953 return repo[rev][fn].renamed()
1953 return repo[rev][fn].renamed()
1954 except error.LookupError:
1954 except error.LookupError:
1955 pass
1955 pass
1956 return None
1956 return None
1957
1957
1958 df = False
1958 df = False
1959 if opts["date"]:
1959 if opts["date"]:
1960 df = util.matchdate(opts["date"])
1960 df = util.matchdate(opts["date"])
1961
1961
1962 only_branches = opts.get('only_branch')
1962 only_branches = opts.get('only_branch')
1963
1963
1964 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1964 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1965 for st, rev, fns in changeiter:
1965 for st, rev, fns in changeiter:
1966 if st == 'add':
1966 if st == 'add':
1967 parents = [p for p in repo.changelog.parentrevs(rev)
1967 parents = [p for p in repo.changelog.parentrevs(rev)
1968 if p != nullrev]
1968 if p != nullrev]
1969 if opts.get('no_merges') and len(parents) == 2:
1969 if opts.get('no_merges') and len(parents) == 2:
1970 continue
1970 continue
1971 if opts.get('only_merges') and len(parents) != 2:
1971 if opts.get('only_merges') and len(parents) != 2:
1972 continue
1972 continue
1973
1973
1974 if only_branches:
1974 if only_branches:
1975 revbranch = get(rev)[5]['branch']
1975 revbranch = get(rev)[5]['branch']
1976 if revbranch not in only_branches:
1976 if revbranch not in only_branches:
1977 continue
1977 continue
1978
1978
1979 if df:
1979 if df:
1980 changes = get(rev)
1980 changes = get(rev)
1981 if not df(changes[2][0]):
1981 if not df(changes[2][0]):
1982 continue
1982 continue
1983
1983
1984 if opts.get('keyword'):
1984 if opts.get('keyword'):
1985 changes = get(rev)
1985 changes = get(rev)
1986 miss = 0
1986 miss = 0
1987 for k in [kw.lower() for kw in opts['keyword']]:
1987 for k in [kw.lower() for kw in opts['keyword']]:
1988 if not (k in changes[1].lower() or
1988 if not (k in changes[1].lower() or
1989 k in changes[4].lower() or
1989 k in changes[4].lower() or
1990 k in " ".join(changes[3]).lower()):
1990 k in " ".join(changes[3]).lower()):
1991 miss = 1
1991 miss = 1
1992 break
1992 break
1993 if miss:
1993 if miss:
1994 continue
1994 continue
1995
1995
1996 if opts['user']:
1996 if opts['user']:
1997 changes = get(rev)
1997 changes = get(rev)
1998 if not [k for k in opts['user'] if k in changes[1]]:
1998 if not [k for k in opts['user'] if k in changes[1]]:
1999 continue
1999 continue
2000
2000
2001 copies = []
2001 copies = []
2002 if opts.get('copies') and rev:
2002 if opts.get('copies') and rev:
2003 for fn in get(rev)[3]:
2003 for fn in get(rev)[3]:
2004 rename = getrenamed(fn, rev)
2004 rename = getrenamed(fn, rev)
2005 if rename:
2005 if rename:
2006 copies.append((fn, rename[0]))
2006 copies.append((fn, rename[0]))
2007 displayer.show(context.changectx(repo, rev), copies=copies)
2007 displayer.show(context.changectx(repo, rev), copies=copies)
2008 elif st == 'iter':
2008 elif st == 'iter':
2009 if count == limit: break
2009 if count == limit: break
2010 if displayer.flush(rev):
2010 if displayer.flush(rev):
2011 count += 1
2011 count += 1
2012
2012
2013 def manifest(ui, repo, node=None, rev=None):
2013 def manifest(ui, repo, node=None, rev=None):
2014 """output the current or given revision of the project manifest
2014 """output the current or given revision of the project manifest
2015
2015
2016 Print a list of version controlled files for the given revision.
2016 Print a list of version controlled files for the given revision.
2017 If no revision is given, the first parent of the working directory
2017 If no revision is given, the first parent of the working directory
2018 is used, or the null revision if none is checked out.
2018 is used, or the null revision if none is checked out.
2019
2019
2020 With -v flag, print file permissions, symlink and executable bits.
2020 With -v flag, print file permissions, symlink and executable bits.
2021 With --debug flag, print file revision hashes.
2021 With --debug flag, print file revision hashes.
2022 """
2022 """
2023
2023
2024 if rev and node:
2024 if rev and node:
2025 raise util.Abort(_("please specify just one revision"))
2025 raise util.Abort(_("please specify just one revision"))
2026
2026
2027 if not node:
2027 if not node:
2028 node = rev
2028 node = rev
2029
2029
2030 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2030 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2031 ctx = repo[node]
2031 ctx = repo[node]
2032 for f in ctx:
2032 for f in ctx:
2033 if ui.debugflag:
2033 if ui.debugflag:
2034 ui.write("%40s " % hex(ctx.manifest()[f]))
2034 ui.write("%40s " % hex(ctx.manifest()[f]))
2035 if ui.verbose:
2035 if ui.verbose:
2036 ui.write(decor[ctx.flags(f)])
2036 ui.write(decor[ctx.flags(f)])
2037 ui.write("%s\n" % f)
2037 ui.write("%s\n" % f)
2038
2038
2039 def merge(ui, repo, node=None, **opts):
2039 def merge(ui, repo, node=None, **opts):
2040 """merge working directory with another revision
2040 """merge working directory with another revision
2041
2041
2042 The contents of the current working directory is updated with all
2042 The contents of the current working directory is updated with all
2043 changes made in the requested revision since the last common
2043 changes made in the requested revision since the last common
2044 predecessor revision.
2044 predecessor revision.
2045
2045
2046 Files that changed between either parent are marked as changed for
2046 Files that changed between either parent are marked as changed for
2047 the next commit and a commit must be performed before any further
2047 the next commit and a commit must be performed before any further
2048 updates are allowed. The next commit has two parents.
2048 updates are allowed. The next commit has two parents.
2049
2049
2050 If no revision is specified, the working directory's parent is a
2050 If no revision is specified, the working directory's parent is a
2051 head revision, and the current branch contains exactly one other
2051 head revision, and the current branch contains exactly one other
2052 head, the other head is merged with by default. Otherwise, an
2052 head, the other head is merged with by default. Otherwise, an
2053 explicit revision to merge with must be provided.
2053 explicit revision to merge with must be provided.
2054 """
2054 """
2055
2055
2056 if opts.get('rev') and node:
2056 if opts.get('rev') and node:
2057 raise util.Abort(_("please specify just one revision"))
2057 raise util.Abort(_("please specify just one revision"))
2058 if not node:
2058 if not node:
2059 node = opts.get('rev')
2059 node = opts.get('rev')
2060
2060
2061 if not node:
2061 if not node:
2062 branch = repo.changectx(None).branch()
2062 branch = repo.changectx(None).branch()
2063 bheads = repo.branchheads(branch)
2063 bheads = repo.branchheads(branch)
2064 if len(bheads) > 2:
2064 if len(bheads) > 2:
2065 raise util.Abort(_("branch '%s' has %d heads - "
2065 raise util.Abort(_("branch '%s' has %d heads - "
2066 "please merge with an explicit rev") %
2066 "please merge with an explicit rev") %
2067 (branch, len(bheads)))
2067 (branch, len(bheads)))
2068
2068
2069 parent = repo.dirstate.parents()[0]
2069 parent = repo.dirstate.parents()[0]
2070 if len(bheads) == 1:
2070 if len(bheads) == 1:
2071 if len(repo.heads()) > 1:
2071 if len(repo.heads()) > 1:
2072 raise util.Abort(_("branch '%s' has one head - "
2072 raise util.Abort(_("branch '%s' has one head - "
2073 "please merge with an explicit rev") %
2073 "please merge with an explicit rev") %
2074 branch)
2074 branch)
2075 msg = _('there is nothing to merge')
2075 msg = _('there is nothing to merge')
2076 if parent != repo.lookup(repo[None].branch()):
2076 if parent != repo.lookup(repo[None].branch()):
2077 msg = _('%s - use "hg update" instead') % msg
2077 msg = _('%s - use "hg update" instead') % msg
2078 raise util.Abort(msg)
2078 raise util.Abort(msg)
2079
2079
2080 if parent not in bheads:
2080 if parent not in bheads:
2081 raise util.Abort(_('working dir not at a head rev - '
2081 raise util.Abort(_('working dir not at a head rev - '
2082 'use "hg update" or merge with an explicit rev'))
2082 'use "hg update" or merge with an explicit rev'))
2083 node = parent == bheads[0] and bheads[-1] or bheads[0]
2083 node = parent == bheads[0] and bheads[-1] or bheads[0]
2084
2084
2085 if opts.get('show'):
2085 if opts.get('show'):
2086 p1 = repo['.']
2086 p1 = repo['.']
2087 p2 = repo[node]
2087 p2 = repo[node]
2088 common = p1.ancestor(p2)
2088 common = p1.ancestor(p2)
2089 roots, heads = [common.node()], [p2.node()]
2089 roots, heads = [common.node()], [p2.node()]
2090 displayer = cmdutil.show_changeset(ui, repo, opts)
2090 displayer = cmdutil.show_changeset(ui, repo, opts)
2091 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2091 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2092 displayer.show(repo[node])
2092 displayer.show(repo[node])
2093 return 0
2093 return 0
2094
2094
2095 return hg.merge(repo, node, force=opts.get('force'))
2095 return hg.merge(repo, node, force=opts.get('force'))
2096
2096
2097 def outgoing(ui, repo, dest=None, **opts):
2097 def outgoing(ui, repo, dest=None, **opts):
2098 """show changesets not found in destination
2098 """show changesets not found in destination
2099
2099
2100 Show changesets not found in the specified destination repository
2100 Show changesets not found in the specified destination repository
2101 or the default push location. These are the changesets that would
2101 or the default push location. These are the changesets that would
2102 be pushed if a push was requested.
2102 be pushed if a push was requested.
2103
2103
2104 See pull for valid destination format details.
2104 See pull for valid destination format details.
2105 """
2105 """
2106 limit = cmdutil.loglimit(opts)
2106 limit = cmdutil.loglimit(opts)
2107 dest, revs, checkout = hg.parseurl(
2107 dest, revs, checkout = hg.parseurl(
2108 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2108 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2109 if revs:
2109 if revs:
2110 revs = [repo.lookup(rev) for rev in revs]
2110 revs = [repo.lookup(rev) for rev in revs]
2111
2111
2112 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2112 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2113 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2113 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2114 o = repo.findoutgoing(other, force=opts.get('force'))
2114 o = repo.findoutgoing(other, force=opts.get('force'))
2115 if not o:
2115 if not o:
2116 ui.status(_("no changes found\n"))
2116 ui.status(_("no changes found\n"))
2117 return 1
2117 return 1
2118 o = repo.changelog.nodesbetween(o, revs)[0]
2118 o = repo.changelog.nodesbetween(o, revs)[0]
2119 if opts.get('newest_first'):
2119 if opts.get('newest_first'):
2120 o.reverse()
2120 o.reverse()
2121 displayer = cmdutil.show_changeset(ui, repo, opts)
2121 displayer = cmdutil.show_changeset(ui, repo, opts)
2122 count = 0
2122 count = 0
2123 for n in o:
2123 for n in o:
2124 if count >= limit:
2124 if count >= limit:
2125 break
2125 break
2126 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2126 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2127 if opts.get('no_merges') and len(parents) == 2:
2127 if opts.get('no_merges') and len(parents) == 2:
2128 continue
2128 continue
2129 count += 1
2129 count += 1
2130 displayer.show(repo[n])
2130 displayer.show(repo[n])
2131
2131
2132 def parents(ui, repo, file_=None, **opts):
2132 def parents(ui, repo, file_=None, **opts):
2133 """show the parents of the working directory or revision
2133 """show the parents of the working directory or revision
2134
2134
2135 Print the working directory's parent revisions. If a revision is
2135 Print the working directory's parent revisions. If a revision is
2136 given via -r/--rev, the parent of that revision will be printed.
2136 given via -r/--rev, the parent of that revision will be printed.
2137 If a file argument is given, revision in which the file was last
2137 If a file argument is given, revision in which the file was last
2138 changed (before the working directory revision or the argument to
2138 changed (before the working directory revision or the argument to
2139 --rev if given) is printed.
2139 --rev if given) is printed.
2140 """
2140 """
2141 rev = opts.get('rev')
2141 rev = opts.get('rev')
2142 if rev:
2142 if rev:
2143 ctx = repo[rev]
2143 ctx = repo[rev]
2144 else:
2144 else:
2145 ctx = repo[None]
2145 ctx = repo[None]
2146
2146
2147 if file_:
2147 if file_:
2148 m = cmdutil.match(repo, (file_,), opts)
2148 m = cmdutil.match(repo, (file_,), opts)
2149 if m.anypats() or len(m.files()) != 1:
2149 if m.anypats() or len(m.files()) != 1:
2150 raise util.Abort(_('can only specify an explicit file name'))
2150 raise util.Abort(_('can only specify an explicit file name'))
2151 file_ = m.files()[0]
2151 file_ = m.files()[0]
2152 filenodes = []
2152 filenodes = []
2153 for cp in ctx.parents():
2153 for cp in ctx.parents():
2154 if not cp:
2154 if not cp:
2155 continue
2155 continue
2156 try:
2156 try:
2157 filenodes.append(cp.filenode(file_))
2157 filenodes.append(cp.filenode(file_))
2158 except error.LookupError:
2158 except error.LookupError:
2159 pass
2159 pass
2160 if not filenodes:
2160 if not filenodes:
2161 raise util.Abort(_("'%s' not found in manifest!") % file_)
2161 raise util.Abort(_("'%s' not found in manifest!") % file_)
2162 fl = repo.file(file_)
2162 fl = repo.file(file_)
2163 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2163 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2164 else:
2164 else:
2165 p = [cp.node() for cp in ctx.parents()]
2165 p = [cp.node() for cp in ctx.parents()]
2166
2166
2167 displayer = cmdutil.show_changeset(ui, repo, opts)
2167 displayer = cmdutil.show_changeset(ui, repo, opts)
2168 for n in p:
2168 for n in p:
2169 if n != nullid:
2169 if n != nullid:
2170 displayer.show(repo[n])
2170 displayer.show(repo[n])
2171
2171
2172 def paths(ui, repo, search=None):
2172 def paths(ui, repo, search=None):
2173 """show aliases for remote repositories
2173 """show aliases for remote repositories
2174
2174
2175 Show definition of symbolic path name NAME. If no name is given,
2175 Show definition of symbolic path name NAME. If no name is given,
2176 show definition of available names.
2176 show definition of available names.
2177
2177
2178 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2178 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2179 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2179 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2180
2180
2181 See 'hg help urls' for more information.
2181 See 'hg help urls' for more information.
2182 """
2182 """
2183 if search:
2183 if search:
2184 for name, path in ui.configitems("paths"):
2184 for name, path in ui.configitems("paths"):
2185 if name == search:
2185 if name == search:
2186 ui.write("%s\n" % url.hidepassword(path))
2186 ui.write("%s\n" % url.hidepassword(path))
2187 return
2187 return
2188 ui.warn(_("not found!\n"))
2188 ui.warn(_("not found!\n"))
2189 return 1
2189 return 1
2190 else:
2190 else:
2191 for name, path in ui.configitems("paths"):
2191 for name, path in ui.configitems("paths"):
2192 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2192 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2193
2193
2194 def postincoming(ui, repo, modheads, optupdate, checkout):
2194 def postincoming(ui, repo, modheads, optupdate, checkout):
2195 if modheads == 0:
2195 if modheads == 0:
2196 return
2196 return
2197 if optupdate:
2197 if optupdate:
2198 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2198 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2199 return hg.update(repo, checkout)
2199 return hg.update(repo, checkout)
2200 else:
2200 else:
2201 ui.status(_("not updating, since new heads added\n"))
2201 ui.status(_("not updating, since new heads added\n"))
2202 if modheads > 1:
2202 if modheads > 1:
2203 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2203 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2204 else:
2204 else:
2205 ui.status(_("(run 'hg update' to get a working copy)\n"))
2205 ui.status(_("(run 'hg update' to get a working copy)\n"))
2206
2206
2207 def pull(ui, repo, source="default", **opts):
2207 def pull(ui, repo, source="default", **opts):
2208 """pull changes from the specified source
2208 """pull changes from the specified source
2209
2209
2210 Pull changes from a remote repository to the local one.
2210 Pull changes from a remote repository to the local one.
2211
2211
2212 This finds all changes from the repository at the specified path
2212 This finds all changes from the repository at the specified path
2213 or URL and adds them to the local repository. By default, this
2213 or URL and adds them to the local repository. By default, this
2214 does not update the copy of the project in the working directory.
2214 does not update the copy of the project in the working directory.
2215
2215
2216 Use hg incoming if you want to see what will be added by the next
2216 Use hg incoming if you want to see what will be added by the next
2217 pull without actually adding the changes to the repository.
2217 pull without actually adding the changes to the repository.
2218
2218
2219 If SOURCE is omitted, the 'default' path will be used.
2219 If SOURCE is omitted, the 'default' path will be used.
2220 See 'hg help urls' for more information.
2220 See 'hg help urls' for more information.
2221 """
2221 """
2222 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2222 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2223 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2223 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2224 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2224 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2225 if revs:
2225 if revs:
2226 try:
2226 try:
2227 revs = [other.lookup(rev) for rev in revs]
2227 revs = [other.lookup(rev) for rev in revs]
2228 except error.CapabilityError:
2228 except error.CapabilityError:
2229 err = _("Other repository doesn't support revision lookup, "
2229 err = _("Other repository doesn't support revision lookup, "
2230 "so a rev cannot be specified.")
2230 "so a rev cannot be specified.")
2231 raise util.Abort(err)
2231 raise util.Abort(err)
2232
2232
2233 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2233 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2234 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2234 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2235
2235
2236 def push(ui, repo, dest=None, **opts):
2236 def push(ui, repo, dest=None, **opts):
2237 """push changes to the specified destination
2237 """push changes to the specified destination
2238
2238
2239 Push changes from the local repository to the given destination.
2239 Push changes from the local repository to the given destination.
2240
2240
2241 This is the symmetrical operation for pull. It moves changes from
2241 This is the symmetrical operation for pull. It moves changes from
2242 the current repository to a different one. If the destination is
2242 the current repository to a different one. If the destination is
2243 local this is identical to a pull in that directory from the
2243 local this is identical to a pull in that directory from the
2244 current one.
2244 current one.
2245
2245
2246 By default, push will refuse to run if it detects the result would
2246 By default, push will refuse to run if it detects the result would
2247 increase the number of remote heads. This generally indicates the
2247 increase the number of remote heads. This generally indicates the
2248 the client has forgotten to pull and merge before pushing.
2248 the client has forgotten to pull and merge before pushing.
2249
2249
2250 If -r/--rev is used, the named revision and all its ancestors will
2250 If -r/--rev is used, the named revision and all its ancestors will
2251 be pushed to the remote repository.
2251 be pushed to the remote repository.
2252
2252
2253 Look at the help text for URLs for important details about ssh://
2253 Look at the help text for URLs for important details about ssh://
2254 URLs. If DESTINATION is omitted, a default path will be used.
2254 URLs. If DESTINATION is omitted, a default path will be used.
2255 See 'hg help urls' for more information.
2255 See 'hg help urls' for more information.
2256 """
2256 """
2257 dest, revs, checkout = hg.parseurl(
2257 dest, revs, checkout = hg.parseurl(
2258 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2258 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2259 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2259 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2260 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2260 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2261 if revs:
2261 if revs:
2262 revs = [repo.lookup(rev) for rev in revs]
2262 revs = [repo.lookup(rev) for rev in revs]
2263 r = repo.push(other, opts.get('force'), revs=revs)
2263 r = repo.push(other, opts.get('force'), revs=revs)
2264 return r == 0
2264 return r == 0
2265
2265
2266 def recover(ui, repo):
2266 def recover(ui, repo):
2267 """roll back an interrupted transaction
2267 """roll back an interrupted transaction
2268
2268
2269 Recover from an interrupted commit or pull.
2269 Recover from an interrupted commit or pull.
2270
2270
2271 This command tries to fix the repository status after an
2271 This command tries to fix the repository status after an
2272 interrupted operation. It should only be necessary when Mercurial
2272 interrupted operation. It should only be necessary when Mercurial
2273 suggests it.
2273 suggests it.
2274 """
2274 """
2275 if repo.recover():
2275 if repo.recover():
2276 return hg.verify(repo)
2276 return hg.verify(repo)
2277 return 1
2277 return 1
2278
2278
2279 def remove(ui, repo, *pats, **opts):
2279 def remove(ui, repo, *pats, **opts):
2280 """remove the specified files on the next commit
2280 """remove the specified files on the next commit
2281
2281
2282 Schedule the indicated files for removal from the repository.
2282 Schedule the indicated files for removal from the repository.
2283
2283
2284 This only removes files from the current branch, not from the
2284 This only removes files from the current branch, not from the
2285 entire project history. -A/--after can be used to remove only
2285 entire project history. -A/--after can be used to remove only
2286 files that have already been deleted, -f/--force can be used to
2286 files that have already been deleted, -f/--force can be used to
2287 force deletion, and -Af can be used to remove files from the next
2287 force deletion, and -Af can be used to remove files from the next
2288 revision without deleting them.
2288 revision without deleting them.
2289
2289
2290 The following table details the behavior of remove for different
2290 The following table details the behavior of remove for different
2291 file states (columns) and option combinations (rows). The file
2291 file states (columns) and option combinations (rows). The file
2292 states are Added, Clean, Modified and Missing (as reported by hg
2292 states are Added, Clean, Modified and Missing (as reported by hg
2293 status). The actions are Warn, Remove (from branch) and Delete
2293 status). The actions are Warn, Remove (from branch) and Delete
2294 (from disk).
2294 (from disk).
2295
2295
2296 A C M !
2296 A C M !
2297 none W RD W R
2297 none W RD W R
2298 -f R RD RD R
2298 -f R RD RD R
2299 -A W W W R
2299 -A W W W R
2300 -Af R R R R
2300 -Af R R R R
2301
2301
2302 This command schedules the files to be removed at the next commit.
2302 This command schedules the files to be removed at the next commit.
2303 To undo a remove before that, see hg revert.
2303 To undo a remove before that, see hg revert.
2304 """
2304 """
2305
2305
2306 after, force = opts.get('after'), opts.get('force')
2306 after, force = opts.get('after'), opts.get('force')
2307 if not pats and not after:
2307 if not pats and not after:
2308 raise util.Abort(_('no files specified'))
2308 raise util.Abort(_('no files specified'))
2309
2309
2310 m = cmdutil.match(repo, pats, opts)
2310 m = cmdutil.match(repo, pats, opts)
2311 s = repo.status(match=m, clean=True)
2311 s = repo.status(match=m, clean=True)
2312 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2312 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2313
2313
2314 for f in m.files():
2314 for f in m.files():
2315 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2315 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2316 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2316 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2317
2317
2318 def warn(files, reason):
2318 def warn(files, reason):
2319 for f in files:
2319 for f in files:
2320 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2320 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2321 % (m.rel(f), reason))
2321 % (m.rel(f), reason))
2322
2322
2323 if force:
2323 if force:
2324 remove, forget = modified + deleted + clean, added
2324 remove, forget = modified + deleted + clean, added
2325 elif after:
2325 elif after:
2326 remove, forget = deleted, []
2326 remove, forget = deleted, []
2327 warn(modified + added + clean, _('still exists'))
2327 warn(modified + added + clean, _('still exists'))
2328 else:
2328 else:
2329 remove, forget = deleted + clean, []
2329 remove, forget = deleted + clean, []
2330 warn(modified, _('is modified'))
2330 warn(modified, _('is modified'))
2331 warn(added, _('has been marked for add'))
2331 warn(added, _('has been marked for add'))
2332
2332
2333 for f in sorted(remove + forget):
2333 for f in sorted(remove + forget):
2334 if ui.verbose or not m.exact(f):
2334 if ui.verbose or not m.exact(f):
2335 ui.status(_('removing %s\n') % m.rel(f))
2335 ui.status(_('removing %s\n') % m.rel(f))
2336
2336
2337 repo.forget(forget)
2337 repo.forget(forget)
2338 repo.remove(remove, unlink=not after)
2338 repo.remove(remove, unlink=not after)
2339
2339
2340 def rename(ui, repo, *pats, **opts):
2340 def rename(ui, repo, *pats, **opts):
2341 """rename files; equivalent of copy + remove
2341 """rename files; equivalent of copy + remove
2342
2342
2343 Mark dest as copies of sources; mark sources for deletion. If dest
2343 Mark dest as copies of sources; mark sources for deletion. If dest
2344 is a directory, copies are put in that directory. If dest is a
2344 is a directory, copies are put in that directory. If dest is a
2345 file, there can only be one source.
2345 file, there can only be one source.
2346
2346
2347 By default, this command copies the contents of files as they
2347 By default, this command copies the contents of files as they
2348 exist in the working directory. If invoked with -A/--after, the
2348 exist in the working directory. If invoked with -A/--after, the
2349 operation is recorded, but no copying is performed.
2349 operation is recorded, but no copying is performed.
2350
2350
2351 This command takes effect at the next commit. To undo a rename
2351 This command takes effect at the next commit. To undo a rename
2352 before that, see hg revert.
2352 before that, see hg revert.
2353 """
2353 """
2354 wlock = repo.wlock(False)
2354 wlock = repo.wlock(False)
2355 try:
2355 try:
2356 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2356 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2357 finally:
2357 finally:
2358 wlock.release()
2358 wlock.release()
2359
2359
2360 def resolve(ui, repo, *pats, **opts):
2360 def resolve(ui, repo, *pats, **opts):
2361 """retry file merges from a merge or update
2361 """retry file merges from a merge or update
2362
2362
2363 This command will cleanly retry unresolved file merges using file
2363 This command will cleanly retry unresolved file merges using file
2364 revisions preserved from the last update or merge. To attempt to
2364 revisions preserved from the last update or merge. To attempt to
2365 resolve all unresolved files, use the -a/--all switch.
2365 resolve all unresolved files, use the -a/--all switch.
2366
2366
2367 If a conflict is resolved manually, please note that the changes
2367 If a conflict is resolved manually, please note that the changes
2368 will be overwritten if the merge is retried with resolve. The
2368 will be overwritten if the merge is retried with resolve. The
2369 -m/--mark switch should be used to mark the file as resolved.
2369 -m/--mark switch should be used to mark the file as resolved.
2370
2370
2371 This command will also allow listing resolved files and manually
2371 This command will also allow listing resolved files and manually
2372 marking and unmarking files as resolved. All files must be marked
2372 marking and unmarking files as resolved. All files must be marked
2373 as resolved before the new commits are permitted.
2373 as resolved before the new commits are permitted.
2374
2374
2375 The codes used to show the status of files are:
2375 The codes used to show the status of files are:
2376 U = unresolved
2376 U = unresolved
2377 R = resolved
2377 R = resolved
2378 """
2378 """
2379
2379
2380 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2380 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2381
2381
2382 if (show and (mark or unmark)) or (mark and unmark):
2382 if (show and (mark or unmark)) or (mark and unmark):
2383 raise util.Abort(_("too many options specified"))
2383 raise util.Abort(_("too many options specified"))
2384 if pats and all:
2384 if pats and all:
2385 raise util.Abort(_("can't specify --all and patterns"))
2385 raise util.Abort(_("can't specify --all and patterns"))
2386 if not (all or pats or show or mark or unmark):
2386 if not (all or pats or show or mark or unmark):
2387 raise util.Abort(_('no files or directories specified; '
2387 raise util.Abort(_('no files or directories specified; '
2388 'use --all to remerge all files'))
2388 'use --all to remerge all files'))
2389
2389
2390 ms = merge_.mergestate(repo)
2390 ms = merge_.mergestate(repo)
2391 m = cmdutil.match(repo, pats, opts)
2391 m = cmdutil.match(repo, pats, opts)
2392
2392
2393 for f in ms:
2393 for f in ms:
2394 if m(f):
2394 if m(f):
2395 if show:
2395 if show:
2396 ui.write("%s %s\n" % (ms[f].upper(), f))
2396 ui.write("%s %s\n" % (ms[f].upper(), f))
2397 elif mark:
2397 elif mark:
2398 ms.mark(f, "r")
2398 ms.mark(f, "r")
2399 elif unmark:
2399 elif unmark:
2400 ms.mark(f, "u")
2400 ms.mark(f, "u")
2401 else:
2401 else:
2402 wctx = repo[None]
2402 wctx = repo[None]
2403 mctx = wctx.parents()[-1]
2403 mctx = wctx.parents()[-1]
2404
2404
2405 # backup pre-resolve (merge uses .orig for its own purposes)
2405 # backup pre-resolve (merge uses .orig for its own purposes)
2406 a = repo.wjoin(f)
2406 a = repo.wjoin(f)
2407 util.copyfile(a, a + ".resolve")
2407 util.copyfile(a, a + ".resolve")
2408
2408
2409 # resolve file
2409 # resolve file
2410 ms.resolve(f, wctx, mctx)
2410 ms.resolve(f, wctx, mctx)
2411
2411
2412 # replace filemerge's .orig file with our resolve file
2412 # replace filemerge's .orig file with our resolve file
2413 util.rename(a + ".resolve", a + ".orig")
2413 util.rename(a + ".resolve", a + ".orig")
2414
2414
2415 def revert(ui, repo, *pats, **opts):
2415 def revert(ui, repo, *pats, **opts):
2416 """restore individual files or directories to an earlier state
2416 """restore individual files or directories to an earlier state
2417
2417
2418 (Use update -r to check out earlier revisions, revert does not
2418 (Use update -r to check out earlier revisions, revert does not
2419 change the working directory parents.)
2419 change the working directory parents.)
2420
2420
2421 With no revision specified, revert the named files or directories
2421 With no revision specified, revert the named files or directories
2422 to the contents they had in the parent of the working directory.
2422 to the contents they had in the parent of the working directory.
2423 This restores the contents of the affected files to an unmodified
2423 This restores the contents of the affected files to an unmodified
2424 state and unschedules adds, removes, copies, and renames. If the
2424 state and unschedules adds, removes, copies, and renames. If the
2425 working directory has two parents, you must explicitly specify the
2425 working directory has two parents, you must explicitly specify the
2426 revision to revert to.
2426 revision to revert to.
2427
2427
2428 Using the -r/--rev option, revert the given files or directories
2428 Using the -r/--rev option, revert the given files or directories
2429 to their contents as of a specific revision. This can be helpful
2429 to their contents as of a specific revision. This can be helpful
2430 to "roll back" some or all of an earlier change. See 'hg help
2430 to "roll back" some or all of an earlier change. See 'hg help
2431 dates' for a list of formats valid for -d/--date.
2431 dates' for a list of formats valid for -d/--date.
2432
2432
2433 Revert modifies the working directory. It does not commit any
2433 Revert modifies the working directory. It does not commit any
2434 changes, or change the parent of the working directory. If you
2434 changes, or change the parent of the working directory. If you
2435 revert to a revision other than the parent of the working
2435 revert to a revision other than the parent of the working
2436 directory, the reverted files will thus appear modified
2436 directory, the reverted files will thus appear modified
2437 afterwards.
2437 afterwards.
2438
2438
2439 If a file has been deleted, it is restored. If the executable mode
2439 If a file has been deleted, it is restored. If the executable mode
2440 of a file was changed, it is reset.
2440 of a file was changed, it is reset.
2441
2441
2442 If names are given, all files matching the names are reverted.
2442 If names are given, all files matching the names are reverted.
2443 If no arguments are given, no files are reverted.
2443 If no arguments are given, no files are reverted.
2444
2444
2445 Modified files are saved with a .orig suffix before reverting.
2445 Modified files are saved with a .orig suffix before reverting.
2446 To disable these backups, use --no-backup.
2446 To disable these backups, use --no-backup.
2447 """
2447 """
2448
2448
2449 if opts["date"]:
2449 if opts["date"]:
2450 if opts["rev"]:
2450 if opts["rev"]:
2451 raise util.Abort(_("you can't specify a revision and a date"))
2451 raise util.Abort(_("you can't specify a revision and a date"))
2452 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2452 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2453
2453
2454 if not pats and not opts.get('all'):
2454 if not pats and not opts.get('all'):
2455 raise util.Abort(_('no files or directories specified; '
2455 raise util.Abort(_('no files or directories specified; '
2456 'use --all to revert the whole repo'))
2456 'use --all to revert the whole repo'))
2457
2457
2458 parent, p2 = repo.dirstate.parents()
2458 parent, p2 = repo.dirstate.parents()
2459 if not opts.get('rev') and p2 != nullid:
2459 if not opts.get('rev') and p2 != nullid:
2460 raise util.Abort(_('uncommitted merge - please provide a '
2460 raise util.Abort(_('uncommitted merge - please provide a '
2461 'specific revision'))
2461 'specific revision'))
2462 ctx = repo[opts.get('rev')]
2462 ctx = repo[opts.get('rev')]
2463 node = ctx.node()
2463 node = ctx.node()
2464 mf = ctx.manifest()
2464 mf = ctx.manifest()
2465 if node == parent:
2465 if node == parent:
2466 pmf = mf
2466 pmf = mf
2467 else:
2467 else:
2468 pmf = None
2468 pmf = None
2469
2469
2470 # need all matching names in dirstate and manifest of target rev,
2470 # need all matching names in dirstate and manifest of target rev,
2471 # so have to walk both. do not print errors if files exist in one
2471 # so have to walk both. do not print errors if files exist in one
2472 # but not other.
2472 # but not other.
2473
2473
2474 names = {}
2474 names = {}
2475
2475
2476 wlock = repo.wlock()
2476 wlock = repo.wlock()
2477 try:
2477 try:
2478 # walk dirstate.
2478 # walk dirstate.
2479
2479
2480 m = cmdutil.match(repo, pats, opts)
2480 m = cmdutil.match(repo, pats, opts)
2481 m.bad = lambda x,y: False
2481 m.bad = lambda x,y: False
2482 for abs in repo.walk(m):
2482 for abs in repo.walk(m):
2483 names[abs] = m.rel(abs), m.exact(abs)
2483 names[abs] = m.rel(abs), m.exact(abs)
2484
2484
2485 # walk target manifest.
2485 # walk target manifest.
2486
2486
2487 def badfn(path, msg):
2487 def badfn(path, msg):
2488 if path in names:
2488 if path in names:
2489 return False
2489 return False
2490 path_ = path + '/'
2490 path_ = path + '/'
2491 for f in names:
2491 for f in names:
2492 if f.startswith(path_):
2492 if f.startswith(path_):
2493 return False
2493 return False
2494 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2494 ui.warn("%s: %s\n" % (m.rel(path), msg))
2495 return False
2495 return False
2496
2496
2497 m = cmdutil.match(repo, pats, opts)
2497 m = cmdutil.match(repo, pats, opts)
2498 m.bad = badfn
2498 m.bad = badfn
2499 for abs in repo[node].walk(m):
2499 for abs in repo[node].walk(m):
2500 if abs not in names:
2500 if abs not in names:
2501 names[abs] = m.rel(abs), m.exact(abs)
2501 names[abs] = m.rel(abs), m.exact(abs)
2502
2502
2503 m = cmdutil.matchfiles(repo, names)
2503 m = cmdutil.matchfiles(repo, names)
2504 changes = repo.status(match=m)[:4]
2504 changes = repo.status(match=m)[:4]
2505 modified, added, removed, deleted = map(set, changes)
2505 modified, added, removed, deleted = map(set, changes)
2506
2506
2507 # if f is a rename, also revert the source
2507 # if f is a rename, also revert the source
2508 cwd = repo.getcwd()
2508 cwd = repo.getcwd()
2509 for f in added:
2509 for f in added:
2510 src = repo.dirstate.copied(f)
2510 src = repo.dirstate.copied(f)
2511 if src and src not in names and repo.dirstate[src] == 'r':
2511 if src and src not in names and repo.dirstate[src] == 'r':
2512 removed.add(src)
2512 removed.add(src)
2513 names[src] = (repo.pathto(src, cwd), True)
2513 names[src] = (repo.pathto(src, cwd), True)
2514
2514
2515 def removeforget(abs):
2515 def removeforget(abs):
2516 if repo.dirstate[abs] == 'a':
2516 if repo.dirstate[abs] == 'a':
2517 return _('forgetting %s\n')
2517 return _('forgetting %s\n')
2518 return _('removing %s\n')
2518 return _('removing %s\n')
2519
2519
2520 revert = ([], _('reverting %s\n'))
2520 revert = ([], _('reverting %s\n'))
2521 add = ([], _('adding %s\n'))
2521 add = ([], _('adding %s\n'))
2522 remove = ([], removeforget)
2522 remove = ([], removeforget)
2523 undelete = ([], _('undeleting %s\n'))
2523 undelete = ([], _('undeleting %s\n'))
2524
2524
2525 disptable = (
2525 disptable = (
2526 # dispatch table:
2526 # dispatch table:
2527 # file state
2527 # file state
2528 # action if in target manifest
2528 # action if in target manifest
2529 # action if not in target manifest
2529 # action if not in target manifest
2530 # make backup if in target manifest
2530 # make backup if in target manifest
2531 # make backup if not in target manifest
2531 # make backup if not in target manifest
2532 (modified, revert, remove, True, True),
2532 (modified, revert, remove, True, True),
2533 (added, revert, remove, True, False),
2533 (added, revert, remove, True, False),
2534 (removed, undelete, None, False, False),
2534 (removed, undelete, None, False, False),
2535 (deleted, revert, remove, False, False),
2535 (deleted, revert, remove, False, False),
2536 )
2536 )
2537
2537
2538 for abs, (rel, exact) in sorted(names.items()):
2538 for abs, (rel, exact) in sorted(names.items()):
2539 mfentry = mf.get(abs)
2539 mfentry = mf.get(abs)
2540 target = repo.wjoin(abs)
2540 target = repo.wjoin(abs)
2541 def handle(xlist, dobackup):
2541 def handle(xlist, dobackup):
2542 xlist[0].append(abs)
2542 xlist[0].append(abs)
2543 if dobackup and not opts.get('no_backup') and util.lexists(target):
2543 if dobackup and not opts.get('no_backup') and util.lexists(target):
2544 bakname = "%s.orig" % rel
2544 bakname = "%s.orig" % rel
2545 ui.note(_('saving current version of %s as %s\n') %
2545 ui.note(_('saving current version of %s as %s\n') %
2546 (rel, bakname))
2546 (rel, bakname))
2547 if not opts.get('dry_run'):
2547 if not opts.get('dry_run'):
2548 util.copyfile(target, bakname)
2548 util.copyfile(target, bakname)
2549 if ui.verbose or not exact:
2549 if ui.verbose or not exact:
2550 msg = xlist[1]
2550 msg = xlist[1]
2551 if not isinstance(msg, basestring):
2551 if not isinstance(msg, basestring):
2552 msg = msg(abs)
2552 msg = msg(abs)
2553 ui.status(msg % rel)
2553 ui.status(msg % rel)
2554 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2554 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2555 if abs not in table: continue
2555 if abs not in table: continue
2556 # file has changed in dirstate
2556 # file has changed in dirstate
2557 if mfentry:
2557 if mfentry:
2558 handle(hitlist, backuphit)
2558 handle(hitlist, backuphit)
2559 elif misslist is not None:
2559 elif misslist is not None:
2560 handle(misslist, backupmiss)
2560 handle(misslist, backupmiss)
2561 break
2561 break
2562 else:
2562 else:
2563 if abs not in repo.dirstate:
2563 if abs not in repo.dirstate:
2564 if mfentry:
2564 if mfentry:
2565 handle(add, True)
2565 handle(add, True)
2566 elif exact:
2566 elif exact:
2567 ui.warn(_('file not managed: %s\n') % rel)
2567 ui.warn(_('file not managed: %s\n') % rel)
2568 continue
2568 continue
2569 # file has not changed in dirstate
2569 # file has not changed in dirstate
2570 if node == parent:
2570 if node == parent:
2571 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2571 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2572 continue
2572 continue
2573 if pmf is None:
2573 if pmf is None:
2574 # only need parent manifest in this unlikely case,
2574 # only need parent manifest in this unlikely case,
2575 # so do not read by default
2575 # so do not read by default
2576 pmf = repo[parent].manifest()
2576 pmf = repo[parent].manifest()
2577 if abs in pmf:
2577 if abs in pmf:
2578 if mfentry:
2578 if mfentry:
2579 # if version of file is same in parent and target
2579 # if version of file is same in parent and target
2580 # manifests, do nothing
2580 # manifests, do nothing
2581 if (pmf[abs] != mfentry or
2581 if (pmf[abs] != mfentry or
2582 pmf.flags(abs) != mf.flags(abs)):
2582 pmf.flags(abs) != mf.flags(abs)):
2583 handle(revert, False)
2583 handle(revert, False)
2584 else:
2584 else:
2585 handle(remove, False)
2585 handle(remove, False)
2586
2586
2587 if not opts.get('dry_run'):
2587 if not opts.get('dry_run'):
2588 def checkout(f):
2588 def checkout(f):
2589 fc = ctx[f]
2589 fc = ctx[f]
2590 repo.wwrite(f, fc.data(), fc.flags())
2590 repo.wwrite(f, fc.data(), fc.flags())
2591
2591
2592 audit_path = util.path_auditor(repo.root)
2592 audit_path = util.path_auditor(repo.root)
2593 for f in remove[0]:
2593 for f in remove[0]:
2594 if repo.dirstate[f] == 'a':
2594 if repo.dirstate[f] == 'a':
2595 repo.dirstate.forget(f)
2595 repo.dirstate.forget(f)
2596 continue
2596 continue
2597 audit_path(f)
2597 audit_path(f)
2598 try:
2598 try:
2599 util.unlink(repo.wjoin(f))
2599 util.unlink(repo.wjoin(f))
2600 except OSError:
2600 except OSError:
2601 pass
2601 pass
2602 repo.dirstate.remove(f)
2602 repo.dirstate.remove(f)
2603
2603
2604 normal = None
2604 normal = None
2605 if node == parent:
2605 if node == parent:
2606 # We're reverting to our parent. If possible, we'd like status
2606 # We're reverting to our parent. If possible, we'd like status
2607 # to report the file as clean. We have to use normallookup for
2607 # to report the file as clean. We have to use normallookup for
2608 # merges to avoid losing information about merged/dirty files.
2608 # merges to avoid losing information about merged/dirty files.
2609 if p2 != nullid:
2609 if p2 != nullid:
2610 normal = repo.dirstate.normallookup
2610 normal = repo.dirstate.normallookup
2611 else:
2611 else:
2612 normal = repo.dirstate.normal
2612 normal = repo.dirstate.normal
2613 for f in revert[0]:
2613 for f in revert[0]:
2614 checkout(f)
2614 checkout(f)
2615 if normal:
2615 if normal:
2616 normal(f)
2616 normal(f)
2617
2617
2618 for f in add[0]:
2618 for f in add[0]:
2619 checkout(f)
2619 checkout(f)
2620 repo.dirstate.add(f)
2620 repo.dirstate.add(f)
2621
2621
2622 normal = repo.dirstate.normallookup
2622 normal = repo.dirstate.normallookup
2623 if node == parent and p2 == nullid:
2623 if node == parent and p2 == nullid:
2624 normal = repo.dirstate.normal
2624 normal = repo.dirstate.normal
2625 for f in undelete[0]:
2625 for f in undelete[0]:
2626 checkout(f)
2626 checkout(f)
2627 normal(f)
2627 normal(f)
2628
2628
2629 finally:
2629 finally:
2630 wlock.release()
2630 wlock.release()
2631
2631
2632 def rollback(ui, repo):
2632 def rollback(ui, repo):
2633 """roll back the last transaction
2633 """roll back the last transaction
2634
2634
2635 This command should be used with care. There is only one level of
2635 This command should be used with care. There is only one level of
2636 rollback, and there is no way to undo a rollback. It will also
2636 rollback, and there is no way to undo a rollback. It will also
2637 restore the dirstate at the time of the last transaction, losing
2637 restore the dirstate at the time of the last transaction, losing
2638 any dirstate changes since that time.
2638 any dirstate changes since that time.
2639
2639
2640 Transactions are used to encapsulate the effects of all commands
2640 Transactions are used to encapsulate the effects of all commands
2641 that create new changesets or propagate existing changesets into a
2641 that create new changesets or propagate existing changesets into a
2642 repository. For example, the following commands are transactional,
2642 repository. For example, the following commands are transactional,
2643 and their effects can be rolled back:
2643 and their effects can be rolled back:
2644
2644
2645 commit
2645 commit
2646 import
2646 import
2647 pull
2647 pull
2648 push (with this repository as destination)
2648 push (with this repository as destination)
2649 unbundle
2649 unbundle
2650
2650
2651 This command is not intended for use on public repositories. Once
2651 This command is not intended for use on public repositories. Once
2652 changes are visible for pull by other users, rolling a transaction
2652 changes are visible for pull by other users, rolling a transaction
2653 back locally is ineffective (someone else may already have pulled
2653 back locally is ineffective (someone else may already have pulled
2654 the changes). Furthermore, a race is possible with readers of the
2654 the changes). Furthermore, a race is possible with readers of the
2655 repository; for example an in-progress pull from the repository
2655 repository; for example an in-progress pull from the repository
2656 may fail if a rollback is performed.
2656 may fail if a rollback is performed.
2657 """
2657 """
2658 repo.rollback()
2658 repo.rollback()
2659
2659
2660 def root(ui, repo):
2660 def root(ui, repo):
2661 """print the root (top) of the current working directory
2661 """print the root (top) of the current working directory
2662
2662
2663 Print the root directory of the current repository.
2663 Print the root directory of the current repository.
2664 """
2664 """
2665 ui.write(repo.root + "\n")
2665 ui.write(repo.root + "\n")
2666
2666
2667 def serve(ui, repo, **opts):
2667 def serve(ui, repo, **opts):
2668 """export the repository via HTTP
2668 """export the repository via HTTP
2669
2669
2670 Start a local HTTP repository browser and pull server.
2670 Start a local HTTP repository browser and pull server.
2671
2671
2672 By default, the server logs accesses to stdout and errors to
2672 By default, the server logs accesses to stdout and errors to
2673 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2673 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2674 files.
2674 files.
2675 """
2675 """
2676
2676
2677 if opts["stdio"]:
2677 if opts["stdio"]:
2678 if repo is None:
2678 if repo is None:
2679 raise error.RepoError(_("There is no Mercurial repository here"
2679 raise error.RepoError(_("There is no Mercurial repository here"
2680 " (.hg not found)"))
2680 " (.hg not found)"))
2681 s = sshserver.sshserver(ui, repo)
2681 s = sshserver.sshserver(ui, repo)
2682 s.serve_forever()
2682 s.serve_forever()
2683
2683
2684 baseui = repo and repo.baseui or ui
2684 baseui = repo and repo.baseui or ui
2685 optlist = ("name templates style address port prefix ipv6"
2685 optlist = ("name templates style address port prefix ipv6"
2686 " accesslog errorlog webdir_conf certificate")
2686 " accesslog errorlog webdir_conf certificate")
2687 for o in optlist.split():
2687 for o in optlist.split():
2688 if opts[o]:
2688 if opts[o]:
2689 baseui.setconfig("web", o, str(opts[o]))
2689 baseui.setconfig("web", o, str(opts[o]))
2690 if (repo is not None) and (repo.ui != baseui):
2690 if (repo is not None) and (repo.ui != baseui):
2691 repo.ui.setconfig("web", o, str(opts[o]))
2691 repo.ui.setconfig("web", o, str(opts[o]))
2692
2692
2693 if repo is None and not ui.config("web", "webdir_conf"):
2693 if repo is None and not ui.config("web", "webdir_conf"):
2694 raise error.RepoError(_("There is no Mercurial repository here"
2694 raise error.RepoError(_("There is no Mercurial repository here"
2695 " (.hg not found)"))
2695 " (.hg not found)"))
2696
2696
2697 class service:
2697 class service:
2698 def init(self):
2698 def init(self):
2699 util.set_signal_handler()
2699 util.set_signal_handler()
2700 self.httpd = server.create_server(baseui, repo)
2700 self.httpd = server.create_server(baseui, repo)
2701
2701
2702 if not ui.verbose: return
2702 if not ui.verbose: return
2703
2703
2704 if self.httpd.prefix:
2704 if self.httpd.prefix:
2705 prefix = self.httpd.prefix.strip('/') + '/'
2705 prefix = self.httpd.prefix.strip('/') + '/'
2706 else:
2706 else:
2707 prefix = ''
2707 prefix = ''
2708
2708
2709 port = ':%d' % self.httpd.port
2709 port = ':%d' % self.httpd.port
2710 if port == ':80':
2710 if port == ':80':
2711 port = ''
2711 port = ''
2712
2712
2713 bindaddr = self.httpd.addr
2713 bindaddr = self.httpd.addr
2714 if bindaddr == '0.0.0.0':
2714 if bindaddr == '0.0.0.0':
2715 bindaddr = '*'
2715 bindaddr = '*'
2716 elif ':' in bindaddr: # IPv6
2716 elif ':' in bindaddr: # IPv6
2717 bindaddr = '[%s]' % bindaddr
2717 bindaddr = '[%s]' % bindaddr
2718
2718
2719 fqaddr = self.httpd.fqaddr
2719 fqaddr = self.httpd.fqaddr
2720 if ':' in fqaddr:
2720 if ':' in fqaddr:
2721 fqaddr = '[%s]' % fqaddr
2721 fqaddr = '[%s]' % fqaddr
2722 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2722 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2723 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2723 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2724
2724
2725 def run(self):
2725 def run(self):
2726 self.httpd.serve_forever()
2726 self.httpd.serve_forever()
2727
2727
2728 service = service()
2728 service = service()
2729
2729
2730 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2730 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2731
2731
2732 def status(ui, repo, *pats, **opts):
2732 def status(ui, repo, *pats, **opts):
2733 """show changed files in the working directory
2733 """show changed files in the working directory
2734
2734
2735 Show status of files in the repository. If names are given, only
2735 Show status of files in the repository. If names are given, only
2736 files that match are shown. Files that are clean or ignored or
2736 files that match are shown. Files that are clean or ignored or
2737 source of a copy/move operation, are not listed unless -c/--clean,
2737 source of a copy/move operation, are not listed unless -c/--clean,
2738 -i/--ignored, -C/--copies or -A/--all is given. Unless options
2738 -i/--ignored, -C/--copies or -A/--all is given. Unless options
2739 described with "show only ..." are given, the options -mardu are
2739 described with "show only ..." are given, the options -mardu are
2740 used.
2740 used.
2741
2741
2742 Option -q/--quiet hides untracked (unknown and ignored) files
2742 Option -q/--quiet hides untracked (unknown and ignored) files
2743 unless explicitly requested with -u/--unknown or -i/--ignored.
2743 unless explicitly requested with -u/--unknown or -i/--ignored.
2744
2744
2745 NOTE: status may appear to disagree with diff if permissions have
2745 NOTE: status may appear to disagree with diff if permissions have
2746 changed or a merge has occurred. The standard diff format does not
2746 changed or a merge has occurred. The standard diff format does not
2747 report permission changes and diff only reports changes relative
2747 report permission changes and diff only reports changes relative
2748 to one merge parent.
2748 to one merge parent.
2749
2749
2750 If one revision is given, it is used as the base revision.
2750 If one revision is given, it is used as the base revision.
2751 If two revisions are given, the difference between them is shown.
2751 If two revisions are given, the difference between them is shown.
2752
2752
2753 The codes used to show the status of files are:
2753 The codes used to show the status of files are:
2754 M = modified
2754 M = modified
2755 A = added
2755 A = added
2756 R = removed
2756 R = removed
2757 C = clean
2757 C = clean
2758 ! = missing (deleted by non-hg command, but still tracked)
2758 ! = missing (deleted by non-hg command, but still tracked)
2759 ? = not tracked
2759 ? = not tracked
2760 I = ignored
2760 I = ignored
2761 = the previous added file was copied from here
2761 = the previous added file was copied from here
2762 """
2762 """
2763
2763
2764 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2764 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2765 cwd = (pats and repo.getcwd()) or ''
2765 cwd = (pats and repo.getcwd()) or ''
2766 end = opts.get('print0') and '\0' or '\n'
2766 end = opts.get('print0') and '\0' or '\n'
2767 copy = {}
2767 copy = {}
2768 states = 'modified added removed deleted unknown ignored clean'.split()
2768 states = 'modified added removed deleted unknown ignored clean'.split()
2769 show = [k for k in states if opts.get(k)]
2769 show = [k for k in states if opts.get(k)]
2770 if opts.get('all'):
2770 if opts.get('all'):
2771 show += ui.quiet and (states[:4] + ['clean']) or states
2771 show += ui.quiet and (states[:4] + ['clean']) or states
2772 if not show:
2772 if not show:
2773 show = ui.quiet and states[:4] or states[:5]
2773 show = ui.quiet and states[:4] or states[:5]
2774
2774
2775 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2775 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2776 'ignored' in show, 'clean' in show, 'unknown' in show)
2776 'ignored' in show, 'clean' in show, 'unknown' in show)
2777 changestates = zip(states, 'MAR!?IC', stat)
2777 changestates = zip(states, 'MAR!?IC', stat)
2778
2778
2779 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2779 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2780 ctxn = repo[nullid]
2780 ctxn = repo[nullid]
2781 ctx1 = repo[node1]
2781 ctx1 = repo[node1]
2782 ctx2 = repo[node2]
2782 ctx2 = repo[node2]
2783 added = stat[1]
2783 added = stat[1]
2784 if node2 is None:
2784 if node2 is None:
2785 added = stat[0] + stat[1] # merged?
2785 added = stat[0] + stat[1] # merged?
2786
2786
2787 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2787 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2788 if k in added:
2788 if k in added:
2789 copy[k] = v
2789 copy[k] = v
2790 elif v in added:
2790 elif v in added:
2791 copy[v] = k
2791 copy[v] = k
2792
2792
2793 for state, char, files in changestates:
2793 for state, char, files in changestates:
2794 if state in show:
2794 if state in show:
2795 format = "%s %%s%s" % (char, end)
2795 format = "%s %%s%s" % (char, end)
2796 if opts.get('no_status'):
2796 if opts.get('no_status'):
2797 format = "%%s%s" % end
2797 format = "%%s%s" % end
2798
2798
2799 for f in files:
2799 for f in files:
2800 ui.write(format % repo.pathto(f, cwd))
2800 ui.write(format % repo.pathto(f, cwd))
2801 if f in copy:
2801 if f in copy:
2802 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2802 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2803
2803
2804 def tag(ui, repo, name1, *names, **opts):
2804 def tag(ui, repo, name1, *names, **opts):
2805 """add one or more tags for the current or given revision
2805 """add one or more tags for the current or given revision
2806
2806
2807 Name a particular revision using <name>.
2807 Name a particular revision using <name>.
2808
2808
2809 Tags are used to name particular revisions of the repository and are
2809 Tags are used to name particular revisions of the repository and are
2810 very useful to compare different revisions, to go back to significant
2810 very useful to compare different revisions, to go back to significant
2811 earlier versions or to mark branch points as releases, etc.
2811 earlier versions or to mark branch points as releases, etc.
2812
2812
2813 If no revision is given, the parent of the working directory is
2813 If no revision is given, the parent of the working directory is
2814 used, or tip if no revision is checked out.
2814 used, or tip if no revision is checked out.
2815
2815
2816 To facilitate version control, distribution, and merging of tags,
2816 To facilitate version control, distribution, and merging of tags,
2817 they are stored as a file named ".hgtags" which is managed
2817 they are stored as a file named ".hgtags" which is managed
2818 similarly to other project files and can be hand-edited if
2818 similarly to other project files and can be hand-edited if
2819 necessary. The file '.hg/localtags' is used for local tags (not
2819 necessary. The file '.hg/localtags' is used for local tags (not
2820 shared among repositories).
2820 shared among repositories).
2821
2821
2822 See 'hg help dates' for a list of formats valid for -d/--date.
2822 See 'hg help dates' for a list of formats valid for -d/--date.
2823 """
2823 """
2824
2824
2825 rev_ = "."
2825 rev_ = "."
2826 names = (name1,) + names
2826 names = (name1,) + names
2827 if len(names) != len(set(names)):
2827 if len(names) != len(set(names)):
2828 raise util.Abort(_('tag names must be unique'))
2828 raise util.Abort(_('tag names must be unique'))
2829 for n in names:
2829 for n in names:
2830 if n in ['tip', '.', 'null']:
2830 if n in ['tip', '.', 'null']:
2831 raise util.Abort(_('the name \'%s\' is reserved') % n)
2831 raise util.Abort(_('the name \'%s\' is reserved') % n)
2832 if opts.get('rev') and opts.get('remove'):
2832 if opts.get('rev') and opts.get('remove'):
2833 raise util.Abort(_("--rev and --remove are incompatible"))
2833 raise util.Abort(_("--rev and --remove are incompatible"))
2834 if opts.get('rev'):
2834 if opts.get('rev'):
2835 rev_ = opts['rev']
2835 rev_ = opts['rev']
2836 message = opts.get('message')
2836 message = opts.get('message')
2837 if opts.get('remove'):
2837 if opts.get('remove'):
2838 expectedtype = opts.get('local') and 'local' or 'global'
2838 expectedtype = opts.get('local') and 'local' or 'global'
2839 for n in names:
2839 for n in names:
2840 if not repo.tagtype(n):
2840 if not repo.tagtype(n):
2841 raise util.Abort(_('tag \'%s\' does not exist') % n)
2841 raise util.Abort(_('tag \'%s\' does not exist') % n)
2842 if repo.tagtype(n) != expectedtype:
2842 if repo.tagtype(n) != expectedtype:
2843 if expectedtype == 'global':
2843 if expectedtype == 'global':
2844 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2844 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2845 else:
2845 else:
2846 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2846 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2847 rev_ = nullid
2847 rev_ = nullid
2848 if not message:
2848 if not message:
2849 message = _('Removed tag %s') % ', '.join(names)
2849 message = _('Removed tag %s') % ', '.join(names)
2850 elif not opts.get('force'):
2850 elif not opts.get('force'):
2851 for n in names:
2851 for n in names:
2852 if n in repo.tags():
2852 if n in repo.tags():
2853 raise util.Abort(_('tag \'%s\' already exists '
2853 raise util.Abort(_('tag \'%s\' already exists '
2854 '(use -f to force)') % n)
2854 '(use -f to force)') % n)
2855 if not rev_ and repo.dirstate.parents()[1] != nullid:
2855 if not rev_ and repo.dirstate.parents()[1] != nullid:
2856 raise util.Abort(_('uncommitted merge - please provide a '
2856 raise util.Abort(_('uncommitted merge - please provide a '
2857 'specific revision'))
2857 'specific revision'))
2858 r = repo[rev_].node()
2858 r = repo[rev_].node()
2859
2859
2860 if not message:
2860 if not message:
2861 message = (_('Added tag %s for changeset %s') %
2861 message = (_('Added tag %s for changeset %s') %
2862 (', '.join(names), short(r)))
2862 (', '.join(names), short(r)))
2863
2863
2864 date = opts.get('date')
2864 date = opts.get('date')
2865 if date:
2865 if date:
2866 date = util.parsedate(date)
2866 date = util.parsedate(date)
2867
2867
2868 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2868 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2869
2869
2870 def tags(ui, repo):
2870 def tags(ui, repo):
2871 """list repository tags
2871 """list repository tags
2872
2872
2873 This lists both regular and local tags. When the -v/--verbose
2873 This lists both regular and local tags. When the -v/--verbose
2874 switch is used, a third column "local" is printed for local tags.
2874 switch is used, a third column "local" is printed for local tags.
2875 """
2875 """
2876
2876
2877 hexfunc = ui.debugflag and hex or short
2877 hexfunc = ui.debugflag and hex or short
2878 tagtype = ""
2878 tagtype = ""
2879
2879
2880 for t, n in reversed(repo.tagslist()):
2880 for t, n in reversed(repo.tagslist()):
2881 if ui.quiet:
2881 if ui.quiet:
2882 ui.write("%s\n" % t)
2882 ui.write("%s\n" % t)
2883 continue
2883 continue
2884
2884
2885 try:
2885 try:
2886 hn = hexfunc(n)
2886 hn = hexfunc(n)
2887 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2887 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2888 except error.LookupError:
2888 except error.LookupError:
2889 r = " ?:%s" % hn
2889 r = " ?:%s" % hn
2890 else:
2890 else:
2891 spaces = " " * (30 - encoding.colwidth(t))
2891 spaces = " " * (30 - encoding.colwidth(t))
2892 if ui.verbose:
2892 if ui.verbose:
2893 if repo.tagtype(t) == 'local':
2893 if repo.tagtype(t) == 'local':
2894 tagtype = " local"
2894 tagtype = " local"
2895 else:
2895 else:
2896 tagtype = ""
2896 tagtype = ""
2897 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2897 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2898
2898
2899 def tip(ui, repo, **opts):
2899 def tip(ui, repo, **opts):
2900 """show the tip revision
2900 """show the tip revision
2901
2901
2902 The tip revision (usually just called the tip) is the most
2902 The tip revision (usually just called the tip) is the most
2903 recently added changeset in the repository, the most recently
2903 recently added changeset in the repository, the most recently
2904 changed head.
2904 changed head.
2905
2905
2906 If you have just made a commit, that commit will be the tip. If
2906 If you have just made a commit, that commit will be the tip. If
2907 you have just pulled changes from another repository, the tip of
2907 you have just pulled changes from another repository, the tip of
2908 that repository becomes the current tip. The "tip" tag is special
2908 that repository becomes the current tip. The "tip" tag is special
2909 and cannot be renamed or assigned to a different changeset.
2909 and cannot be renamed or assigned to a different changeset.
2910 """
2910 """
2911 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2911 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2912
2912
2913 def unbundle(ui, repo, fname1, *fnames, **opts):
2913 def unbundle(ui, repo, fname1, *fnames, **opts):
2914 """apply one or more changegroup files
2914 """apply one or more changegroup files
2915
2915
2916 Apply one or more compressed changegroup files generated by the
2916 Apply one or more compressed changegroup files generated by the
2917 bundle command.
2917 bundle command.
2918 """
2918 """
2919 fnames = (fname1,) + fnames
2919 fnames = (fname1,) + fnames
2920
2920
2921 lock = repo.lock()
2921 lock = repo.lock()
2922 try:
2922 try:
2923 for fname in fnames:
2923 for fname in fnames:
2924 f = url.open(ui, fname)
2924 f = url.open(ui, fname)
2925 gen = changegroup.readbundle(f, fname)
2925 gen = changegroup.readbundle(f, fname)
2926 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2926 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2927 finally:
2927 finally:
2928 lock.release()
2928 lock.release()
2929
2929
2930 return postincoming(ui, repo, modheads, opts.get('update'), None)
2930 return postincoming(ui, repo, modheads, opts.get('update'), None)
2931
2931
2932 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2932 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2933 """update working directory
2933 """update working directory
2934
2934
2935 Update the repository's working directory to the specified
2935 Update the repository's working directory to the specified
2936 revision, or the tip of the current branch if none is specified.
2936 revision, or the tip of the current branch if none is specified.
2937 Use null as the revision to remove the working copy (like 'hg
2937 Use null as the revision to remove the working copy (like 'hg
2938 clone -U').
2938 clone -U').
2939
2939
2940 When the working directory contains no uncommitted changes, it
2940 When the working directory contains no uncommitted changes, it
2941 will be replaced by the state of the requested revision from the
2941 will be replaced by the state of the requested revision from the
2942 repository. When the requested revision is on a different branch,
2942 repository. When the requested revision is on a different branch,
2943 the working directory will additionally be switched to that
2943 the working directory will additionally be switched to that
2944 branch.
2944 branch.
2945
2945
2946 When there are uncommitted changes, use option -C/--clean to
2946 When there are uncommitted changes, use option -C/--clean to
2947 discard them, forcibly replacing the state of the working
2947 discard them, forcibly replacing the state of the working
2948 directory with the requested revision.
2948 directory with the requested revision.
2949
2949
2950 When there are uncommitted changes and option -C/--clean is not
2950 When there are uncommitted changes and option -C/--clean is not
2951 used, and the parent revision and requested revision are on the
2951 used, and the parent revision and requested revision are on the
2952 same branch, and one of them is an ancestor of the other, then the
2952 same branch, and one of them is an ancestor of the other, then the
2953 new working directory will contain the requested revision merged
2953 new working directory will contain the requested revision merged
2954 with the uncommitted changes. Otherwise, the update will fail with
2954 with the uncommitted changes. Otherwise, the update will fail with
2955 a suggestion to use 'merge' or 'update -C' instead.
2955 a suggestion to use 'merge' or 'update -C' instead.
2956
2956
2957 If you want to update just one file to an older revision, use
2957 If you want to update just one file to an older revision, use
2958 revert.
2958 revert.
2959
2959
2960 See 'hg help dates' for a list of formats valid for -d/--date.
2960 See 'hg help dates' for a list of formats valid for -d/--date.
2961 """
2961 """
2962 if rev and node:
2962 if rev and node:
2963 raise util.Abort(_("please specify just one revision"))
2963 raise util.Abort(_("please specify just one revision"))
2964
2964
2965 if not rev:
2965 if not rev:
2966 rev = node
2966 rev = node
2967
2967
2968 if date:
2968 if date:
2969 if rev:
2969 if rev:
2970 raise util.Abort(_("you can't specify a revision and a date"))
2970 raise util.Abort(_("you can't specify a revision and a date"))
2971 rev = cmdutil.finddate(ui, repo, date)
2971 rev = cmdutil.finddate(ui, repo, date)
2972
2972
2973 if clean:
2973 if clean:
2974 return hg.clean(repo, rev)
2974 return hg.clean(repo, rev)
2975 else:
2975 else:
2976 return hg.update(repo, rev)
2976 return hg.update(repo, rev)
2977
2977
2978 def verify(ui, repo):
2978 def verify(ui, repo):
2979 """verify the integrity of the repository
2979 """verify the integrity of the repository
2980
2980
2981 Verify the integrity of the current repository.
2981 Verify the integrity of the current repository.
2982
2982
2983 This will perform an extensive check of the repository's
2983 This will perform an extensive check of the repository's
2984 integrity, validating the hashes and checksums of each entry in
2984 integrity, validating the hashes and checksums of each entry in
2985 the changelog, manifest, and tracked files, as well as the
2985 the changelog, manifest, and tracked files, as well as the
2986 integrity of their crosslinks and indices.
2986 integrity of their crosslinks and indices.
2987 """
2987 """
2988 return hg.verify(repo)
2988 return hg.verify(repo)
2989
2989
2990 def version_(ui):
2990 def version_(ui):
2991 """output version and copyright information"""
2991 """output version and copyright information"""
2992 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2992 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2993 % util.version())
2993 % util.version())
2994 ui.status(_(
2994 ui.status(_(
2995 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
2995 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
2996 "This is free software; see the source for copying conditions. "
2996 "This is free software; see the source for copying conditions. "
2997 "There is NO\nwarranty; "
2997 "There is NO\nwarranty; "
2998 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2998 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2999 ))
2999 ))
3000
3000
3001 # Command options and aliases are listed here, alphabetically
3001 # Command options and aliases are listed here, alphabetically
3002
3002
3003 globalopts = [
3003 globalopts = [
3004 ('R', 'repository', '',
3004 ('R', 'repository', '',
3005 _('repository root directory or symbolic path name')),
3005 _('repository root directory or symbolic path name')),
3006 ('', 'cwd', '', _('change working directory')),
3006 ('', 'cwd', '', _('change working directory')),
3007 ('y', 'noninteractive', None,
3007 ('y', 'noninteractive', None,
3008 _('do not prompt, assume \'yes\' for any required answers')),
3008 _('do not prompt, assume \'yes\' for any required answers')),
3009 ('q', 'quiet', None, _('suppress output')),
3009 ('q', 'quiet', None, _('suppress output')),
3010 ('v', 'verbose', None, _('enable additional output')),
3010 ('v', 'verbose', None, _('enable additional output')),
3011 ('', 'config', [], _('set/override config option')),
3011 ('', 'config', [], _('set/override config option')),
3012 ('', 'debug', None, _('enable debugging output')),
3012 ('', 'debug', None, _('enable debugging output')),
3013 ('', 'debugger', None, _('start debugger')),
3013 ('', 'debugger', None, _('start debugger')),
3014 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3014 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3015 ('', 'encodingmode', encoding.encodingmode,
3015 ('', 'encodingmode', encoding.encodingmode,
3016 _('set the charset encoding mode')),
3016 _('set the charset encoding mode')),
3017 ('', 'traceback', None, _('print traceback on exception')),
3017 ('', 'traceback', None, _('print traceback on exception')),
3018 ('', 'time', None, _('time how long the command takes')),
3018 ('', 'time', None, _('time how long the command takes')),
3019 ('', 'profile', None, _('print command execution profile')),
3019 ('', 'profile', None, _('print command execution profile')),
3020 ('', 'version', None, _('output version information and exit')),
3020 ('', 'version', None, _('output version information and exit')),
3021 ('h', 'help', None, _('display help and exit')),
3021 ('h', 'help', None, _('display help and exit')),
3022 ]
3022 ]
3023
3023
3024 dryrunopts = [('n', 'dry-run', None,
3024 dryrunopts = [('n', 'dry-run', None,
3025 _('do not perform actions, just print output'))]
3025 _('do not perform actions, just print output'))]
3026
3026
3027 remoteopts = [
3027 remoteopts = [
3028 ('e', 'ssh', '', _('specify ssh command to use')),
3028 ('e', 'ssh', '', _('specify ssh command to use')),
3029 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3029 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3030 ]
3030 ]
3031
3031
3032 walkopts = [
3032 walkopts = [
3033 ('I', 'include', [], _('include names matching the given patterns')),
3033 ('I', 'include', [], _('include names matching the given patterns')),
3034 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3034 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3035 ]
3035 ]
3036
3036
3037 commitopts = [
3037 commitopts = [
3038 ('m', 'message', '', _('use <text> as commit message')),
3038 ('m', 'message', '', _('use <text> as commit message')),
3039 ('l', 'logfile', '', _('read commit message from <file>')),
3039 ('l', 'logfile', '', _('read commit message from <file>')),
3040 ]
3040 ]
3041
3041
3042 commitopts2 = [
3042 commitopts2 = [
3043 ('d', 'date', '', _('record datecode as commit date')),
3043 ('d', 'date', '', _('record datecode as commit date')),
3044 ('u', 'user', '', _('record the specified user as committer')),
3044 ('u', 'user', '', _('record the specified user as committer')),
3045 ]
3045 ]
3046
3046
3047 templateopts = [
3047 templateopts = [
3048 ('', 'style', '', _('display using template map file')),
3048 ('', 'style', '', _('display using template map file')),
3049 ('', 'template', '', _('display with template')),
3049 ('', 'template', '', _('display with template')),
3050 ]
3050 ]
3051
3051
3052 logopts = [
3052 logopts = [
3053 ('p', 'patch', None, _('show patch')),
3053 ('p', 'patch', None, _('show patch')),
3054 ('g', 'git', None, _('use git extended diff format')),
3054 ('g', 'git', None, _('use git extended diff format')),
3055 ('l', 'limit', '', _('limit number of changes displayed')),
3055 ('l', 'limit', '', _('limit number of changes displayed')),
3056 ('M', 'no-merges', None, _('do not show merges')),
3056 ('M', 'no-merges', None, _('do not show merges')),
3057 ] + templateopts
3057 ] + templateopts
3058
3058
3059 diffopts = [
3059 diffopts = [
3060 ('a', 'text', None, _('treat all files as text')),
3060 ('a', 'text', None, _('treat all files as text')),
3061 ('g', 'git', None, _('use git extended diff format')),
3061 ('g', 'git', None, _('use git extended diff format')),
3062 ('', 'nodates', None, _("don't include dates in diff headers"))
3062 ('', 'nodates', None, _("don't include dates in diff headers"))
3063 ]
3063 ]
3064
3064
3065 diffopts2 = [
3065 diffopts2 = [
3066 ('p', 'show-function', None, _('show which function each change is in')),
3066 ('p', 'show-function', None, _('show which function each change is in')),
3067 ('w', 'ignore-all-space', None,
3067 ('w', 'ignore-all-space', None,
3068 _('ignore white space when comparing lines')),
3068 _('ignore white space when comparing lines')),
3069 ('b', 'ignore-space-change', None,
3069 ('b', 'ignore-space-change', None,
3070 _('ignore changes in the amount of white space')),
3070 _('ignore changes in the amount of white space')),
3071 ('B', 'ignore-blank-lines', None,
3071 ('B', 'ignore-blank-lines', None,
3072 _('ignore changes whose lines are all blank')),
3072 _('ignore changes whose lines are all blank')),
3073 ('U', 'unified', '', _('number of lines of context to show'))
3073 ('U', 'unified', '', _('number of lines of context to show'))
3074 ]
3074 ]
3075
3075
3076 similarityopts = [
3076 similarityopts = [
3077 ('s', 'similarity', '',
3077 ('s', 'similarity', '',
3078 _('guess renamed files by similarity (0<=s<=100)'))
3078 _('guess renamed files by similarity (0<=s<=100)'))
3079 ]
3079 ]
3080
3080
3081 table = {
3081 table = {
3082 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3082 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3083 "addremove":
3083 "addremove":
3084 (addremove, similarityopts + walkopts + dryrunopts,
3084 (addremove, similarityopts + walkopts + dryrunopts,
3085 _('[OPTION]... [FILE]...')),
3085 _('[OPTION]... [FILE]...')),
3086 "^annotate|blame":
3086 "^annotate|blame":
3087 (annotate,
3087 (annotate,
3088 [('r', 'rev', '', _('annotate the specified revision')),
3088 [('r', 'rev', '', _('annotate the specified revision')),
3089 ('f', 'follow', None, _('follow file copies and renames')),
3089 ('f', 'follow', None, _('follow file copies and renames')),
3090 ('a', 'text', None, _('treat all files as text')),
3090 ('a', 'text', None, _('treat all files as text')),
3091 ('u', 'user', None, _('list the author (long with -v)')),
3091 ('u', 'user', None, _('list the author (long with -v)')),
3092 ('d', 'date', None, _('list the date (short with -q)')),
3092 ('d', 'date', None, _('list the date (short with -q)')),
3093 ('n', 'number', None, _('list the revision number (default)')),
3093 ('n', 'number', None, _('list the revision number (default)')),
3094 ('c', 'changeset', None, _('list the changeset')),
3094 ('c', 'changeset', None, _('list the changeset')),
3095 ('l', 'line-number', None,
3095 ('l', 'line-number', None,
3096 _('show line number at the first appearance'))
3096 _('show line number at the first appearance'))
3097 ] + walkopts,
3097 ] + walkopts,
3098 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3098 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3099 "archive":
3099 "archive":
3100 (archive,
3100 (archive,
3101 [('', 'no-decode', None, _('do not pass files through decoders')),
3101 [('', 'no-decode', None, _('do not pass files through decoders')),
3102 ('p', 'prefix', '', _('directory prefix for files in archive')),
3102 ('p', 'prefix', '', _('directory prefix for files in archive')),
3103 ('r', 'rev', '', _('revision to distribute')),
3103 ('r', 'rev', '', _('revision to distribute')),
3104 ('t', 'type', '', _('type of distribution to create')),
3104 ('t', 'type', '', _('type of distribution to create')),
3105 ] + walkopts,
3105 ] + walkopts,
3106 _('[OPTION]... DEST')),
3106 _('[OPTION]... DEST')),
3107 "backout":
3107 "backout":
3108 (backout,
3108 (backout,
3109 [('', 'merge', None,
3109 [('', 'merge', None,
3110 _('merge with old dirstate parent after backout')),
3110 _('merge with old dirstate parent after backout')),
3111 ('', 'parent', '', _('parent to choose when backing out merge')),
3111 ('', 'parent', '', _('parent to choose when backing out merge')),
3112 ('r', 'rev', '', _('revision to backout')),
3112 ('r', 'rev', '', _('revision to backout')),
3113 ] + walkopts + commitopts + commitopts2,
3113 ] + walkopts + commitopts + commitopts2,
3114 _('[OPTION]... [-r] REV')),
3114 _('[OPTION]... [-r] REV')),
3115 "bisect":
3115 "bisect":
3116 (bisect,
3116 (bisect,
3117 [('r', 'reset', False, _('reset bisect state')),
3117 [('r', 'reset', False, _('reset bisect state')),
3118 ('g', 'good', False, _('mark changeset good')),
3118 ('g', 'good', False, _('mark changeset good')),
3119 ('b', 'bad', False, _('mark changeset bad')),
3119 ('b', 'bad', False, _('mark changeset bad')),
3120 ('s', 'skip', False, _('skip testing changeset')),
3120 ('s', 'skip', False, _('skip testing changeset')),
3121 ('c', 'command', '', _('use command to check changeset state')),
3121 ('c', 'command', '', _('use command to check changeset state')),
3122 ('U', 'noupdate', False, _('do not update to target'))],
3122 ('U', 'noupdate', False, _('do not update to target'))],
3123 _("[-gbsr] [-c CMD] [REV]")),
3123 _("[-gbsr] [-c CMD] [REV]")),
3124 "branch":
3124 "branch":
3125 (branch,
3125 (branch,
3126 [('f', 'force', None,
3126 [('f', 'force', None,
3127 _('set branch name even if it shadows an existing branch')),
3127 _('set branch name even if it shadows an existing branch')),
3128 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3128 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3129 _('[-fC] [NAME]')),
3129 _('[-fC] [NAME]')),
3130 "branches":
3130 "branches":
3131 (branches,
3131 (branches,
3132 [('a', 'active', False,
3132 [('a', 'active', False,
3133 _('show only branches that have unmerged heads'))],
3133 _('show only branches that have unmerged heads'))],
3134 _('[-a]')),
3134 _('[-a]')),
3135 "bundle":
3135 "bundle":
3136 (bundle,
3136 (bundle,
3137 [('f', 'force', None,
3137 [('f', 'force', None,
3138 _('run even when remote repository is unrelated')),
3138 _('run even when remote repository is unrelated')),
3139 ('r', 'rev', [],
3139 ('r', 'rev', [],
3140 _('a changeset up to which you would like to bundle')),
3140 _('a changeset up to which you would like to bundle')),
3141 ('', 'base', [],
3141 ('', 'base', [],
3142 _('a base changeset to specify instead of a destination')),
3142 _('a base changeset to specify instead of a destination')),
3143 ('a', 'all', None, _('bundle all changesets in the repository')),
3143 ('a', 'all', None, _('bundle all changesets in the repository')),
3144 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3144 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3145 ] + remoteopts,
3145 ] + remoteopts,
3146 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3146 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3147 "cat":
3147 "cat":
3148 (cat,
3148 (cat,
3149 [('o', 'output', '', _('print output to file with formatted name')),
3149 [('o', 'output', '', _('print output to file with formatted name')),
3150 ('r', 'rev', '', _('print the given revision')),
3150 ('r', 'rev', '', _('print the given revision')),
3151 ('', 'decode', None, _('apply any matching decode filter')),
3151 ('', 'decode', None, _('apply any matching decode filter')),
3152 ] + walkopts,
3152 ] + walkopts,
3153 _('[OPTION]... FILE...')),
3153 _('[OPTION]... FILE...')),
3154 "^clone":
3154 "^clone":
3155 (clone,
3155 (clone,
3156 [('U', 'noupdate', None,
3156 [('U', 'noupdate', None,
3157 _('the clone will only contain a repository (no working copy)')),
3157 _('the clone will only contain a repository (no working copy)')),
3158 ('r', 'rev', [],
3158 ('r', 'rev', [],
3159 _('a changeset you would like to have after cloning')),
3159 _('a changeset you would like to have after cloning')),
3160 ('', 'pull', None, _('use pull protocol to copy metadata')),
3160 ('', 'pull', None, _('use pull protocol to copy metadata')),
3161 ('', 'uncompressed', None,
3161 ('', 'uncompressed', None,
3162 _('use uncompressed transfer (fast over LAN)')),
3162 _('use uncompressed transfer (fast over LAN)')),
3163 ] + remoteopts,
3163 ] + remoteopts,
3164 _('[OPTION]... SOURCE [DEST]')),
3164 _('[OPTION]... SOURCE [DEST]')),
3165 "^commit|ci":
3165 "^commit|ci":
3166 (commit,
3166 (commit,
3167 [('A', 'addremove', None,
3167 [('A', 'addremove', None,
3168 _('mark new/missing files as added/removed before committing')),
3168 _('mark new/missing files as added/removed before committing')),
3169 ('', 'close-branch', None,
3169 ('', 'close-branch', None,
3170 _('mark a branch as closed, hiding it from the branch list')),
3170 _('mark a branch as closed, hiding it from the branch list')),
3171 ] + walkopts + commitopts + commitopts2,
3171 ] + walkopts + commitopts + commitopts2,
3172 _('[OPTION]... [FILE]...')),
3172 _('[OPTION]... [FILE]...')),
3173 "copy|cp":
3173 "copy|cp":
3174 (copy,
3174 (copy,
3175 [('A', 'after', None, _('record a copy that has already occurred')),
3175 [('A', 'after', None, _('record a copy that has already occurred')),
3176 ('f', 'force', None,
3176 ('f', 'force', None,
3177 _('forcibly copy over an existing managed file')),
3177 _('forcibly copy over an existing managed file')),
3178 ] + walkopts + dryrunopts,
3178 ] + walkopts + dryrunopts,
3179 _('[OPTION]... [SOURCE]... DEST')),
3179 _('[OPTION]... [SOURCE]... DEST')),
3180 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3180 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3181 "debugcheckstate": (debugcheckstate, []),
3181 "debugcheckstate": (debugcheckstate, []),
3182 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3182 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3183 "debugcomplete":
3183 "debugcomplete":
3184 (debugcomplete,
3184 (debugcomplete,
3185 [('o', 'options', None, _('show the command options'))],
3185 [('o', 'options', None, _('show the command options'))],
3186 _('[-o] CMD')),
3186 _('[-o] CMD')),
3187 "debugdate":
3187 "debugdate":
3188 (debugdate,
3188 (debugdate,
3189 [('e', 'extended', None, _('try extended date formats'))],
3189 [('e', 'extended', None, _('try extended date formats'))],
3190 _('[-e] DATE [RANGE]')),
3190 _('[-e] DATE [RANGE]')),
3191 "debugdata": (debugdata, [], _('FILE REV')),
3191 "debugdata": (debugdata, [], _('FILE REV')),
3192 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3192 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3193 "debugindex": (debugindex, [], _('FILE')),
3193 "debugindex": (debugindex, [], _('FILE')),
3194 "debugindexdot": (debugindexdot, [], _('FILE')),
3194 "debugindexdot": (debugindexdot, [], _('FILE')),
3195 "debuginstall": (debuginstall, []),
3195 "debuginstall": (debuginstall, []),
3196 "debugrebuildstate":
3196 "debugrebuildstate":
3197 (debugrebuildstate,
3197 (debugrebuildstate,
3198 [('r', 'rev', '', _('revision to rebuild to'))],
3198 [('r', 'rev', '', _('revision to rebuild to'))],
3199 _('[-r REV] [REV]')),
3199 _('[-r REV] [REV]')),
3200 "debugrename":
3200 "debugrename":
3201 (debugrename,
3201 (debugrename,
3202 [('r', 'rev', '', _('revision to debug'))],
3202 [('r', 'rev', '', _('revision to debug'))],
3203 _('[-r REV] FILE')),
3203 _('[-r REV] FILE')),
3204 "debugsetparents":
3204 "debugsetparents":
3205 (debugsetparents, [], _('REV1 [REV2]')),
3205 (debugsetparents, [], _('REV1 [REV2]')),
3206 "debugstate":
3206 "debugstate":
3207 (debugstate,
3207 (debugstate,
3208 [('', 'nodates', None, _('do not display the saved mtime'))],
3208 [('', 'nodates', None, _('do not display the saved mtime'))],
3209 _('[OPTION]...')),
3209 _('[OPTION]...')),
3210 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3210 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3211 "^diff":
3211 "^diff":
3212 (diff,
3212 (diff,
3213 [('r', 'rev', [], _('revision')),
3213 [('r', 'rev', [], _('revision')),
3214 ('c', 'change', '', _('change made by revision'))
3214 ('c', 'change', '', _('change made by revision'))
3215 ] + diffopts + diffopts2 + walkopts,
3215 ] + diffopts + diffopts2 + walkopts,
3216 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3216 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3217 "^export":
3217 "^export":
3218 (export,
3218 (export,
3219 [('o', 'output', '', _('print output to file with formatted name')),
3219 [('o', 'output', '', _('print output to file with formatted name')),
3220 ('', 'switch-parent', None, _('diff against the second parent'))
3220 ('', 'switch-parent', None, _('diff against the second parent'))
3221 ] + diffopts,
3221 ] + diffopts,
3222 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3222 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3223 "grep":
3223 "grep":
3224 (grep,
3224 (grep,
3225 [('0', 'print0', None, _('end fields with NUL')),
3225 [('0', 'print0', None, _('end fields with NUL')),
3226 ('', 'all', None, _('print all revisions that match')),
3226 ('', 'all', None, _('print all revisions that match')),
3227 ('f', 'follow', None,
3227 ('f', 'follow', None,
3228 _('follow changeset history, or file history across copies and renames')),
3228 _('follow changeset history, or file history across copies and renames')),
3229 ('i', 'ignore-case', None, _('ignore case when matching')),
3229 ('i', 'ignore-case', None, _('ignore case when matching')),
3230 ('l', 'files-with-matches', None,
3230 ('l', 'files-with-matches', None,
3231 _('print only filenames and revisions that match')),
3231 _('print only filenames and revisions that match')),
3232 ('n', 'line-number', None, _('print matching line numbers')),
3232 ('n', 'line-number', None, _('print matching line numbers')),
3233 ('r', 'rev', [], _('search in given revision range')),
3233 ('r', 'rev', [], _('search in given revision range')),
3234 ('u', 'user', None, _('list the author (long with -v)')),
3234 ('u', 'user', None, _('list the author (long with -v)')),
3235 ('d', 'date', None, _('list the date (short with -q)')),
3235 ('d', 'date', None, _('list the date (short with -q)')),
3236 ] + walkopts,
3236 ] + walkopts,
3237 _('[OPTION]... PATTERN [FILE]...')),
3237 _('[OPTION]... PATTERN [FILE]...')),
3238 "heads":
3238 "heads":
3239 (heads,
3239 (heads,
3240 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3240 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3241 ('a', 'active', False,
3241 ('a', 'active', False,
3242 _('show only the active heads from open branches')),
3242 _('show only the active heads from open branches')),
3243 ] + templateopts,
3243 ] + templateopts,
3244 _('[-r REV] [REV]...')),
3244 _('[-r REV] [REV]...')),
3245 "help": (help_, [], _('[TOPIC]')),
3245 "help": (help_, [], _('[TOPIC]')),
3246 "identify|id":
3246 "identify|id":
3247 (identify,
3247 (identify,
3248 [('r', 'rev', '', _('identify the specified revision')),
3248 [('r', 'rev', '', _('identify the specified revision')),
3249 ('n', 'num', None, _('show local revision number')),
3249 ('n', 'num', None, _('show local revision number')),
3250 ('i', 'id', None, _('show global revision id')),
3250 ('i', 'id', None, _('show global revision id')),
3251 ('b', 'branch', None, _('show branch')),
3251 ('b', 'branch', None, _('show branch')),
3252 ('t', 'tags', None, _('show tags'))],
3252 ('t', 'tags', None, _('show tags'))],
3253 _('[-nibt] [-r REV] [SOURCE]')),
3253 _('[-nibt] [-r REV] [SOURCE]')),
3254 "import|patch":
3254 "import|patch":
3255 (import_,
3255 (import_,
3256 [('p', 'strip', 1,
3256 [('p', 'strip', 1,
3257 _('directory strip option for patch. This has the same '
3257 _('directory strip option for patch. This has the same '
3258 'meaning as the corresponding patch option')),
3258 'meaning as the corresponding patch option')),
3259 ('b', 'base', '', _('base path')),
3259 ('b', 'base', '', _('base path')),
3260 ('f', 'force', None,
3260 ('f', 'force', None,
3261 _('skip check for outstanding uncommitted changes')),
3261 _('skip check for outstanding uncommitted changes')),
3262 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3262 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3263 ('', 'exact', None,
3263 ('', 'exact', None,
3264 _('apply patch to the nodes from which it was generated')),
3264 _('apply patch to the nodes from which it was generated')),
3265 ('', 'import-branch', None,
3265 ('', 'import-branch', None,
3266 _('use any branch information in patch (implied by --exact)'))] +
3266 _('use any branch information in patch (implied by --exact)'))] +
3267 commitopts + commitopts2 + similarityopts,
3267 commitopts + commitopts2 + similarityopts,
3268 _('[OPTION]... PATCH...')),
3268 _('[OPTION]... PATCH...')),
3269 "incoming|in":
3269 "incoming|in":
3270 (incoming,
3270 (incoming,
3271 [('f', 'force', None,
3271 [('f', 'force', None,
3272 _('run even when remote repository is unrelated')),
3272 _('run even when remote repository is unrelated')),
3273 ('n', 'newest-first', None, _('show newest record first')),
3273 ('n', 'newest-first', None, _('show newest record first')),
3274 ('', 'bundle', '', _('file to store the bundles into')),
3274 ('', 'bundle', '', _('file to store the bundles into')),
3275 ('r', 'rev', [],
3275 ('r', 'rev', [],
3276 _('a specific revision up to which you would like to pull')),
3276 _('a specific revision up to which you would like to pull')),
3277 ] + logopts + remoteopts,
3277 ] + logopts + remoteopts,
3278 _('[-p] [-n] [-M] [-f] [-r REV]...'
3278 _('[-p] [-n] [-M] [-f] [-r REV]...'
3279 ' [--bundle FILENAME] [SOURCE]')),
3279 ' [--bundle FILENAME] [SOURCE]')),
3280 "^init":
3280 "^init":
3281 (init,
3281 (init,
3282 remoteopts,
3282 remoteopts,
3283 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3283 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3284 "locate":
3284 "locate":
3285 (locate,
3285 (locate,
3286 [('r', 'rev', '', _('search the repository as it stood at REV')),
3286 [('r', 'rev', '', _('search the repository as it stood at REV')),
3287 ('0', 'print0', None,
3287 ('0', 'print0', None,
3288 _('end filenames with NUL, for use with xargs')),
3288 _('end filenames with NUL, for use with xargs')),
3289 ('f', 'fullpath', None,
3289 ('f', 'fullpath', None,
3290 _('print complete paths from the filesystem root')),
3290 _('print complete paths from the filesystem root')),
3291 ] + walkopts,
3291 ] + walkopts,
3292 _('[OPTION]... [PATTERN]...')),
3292 _('[OPTION]... [PATTERN]...')),
3293 "^log|history":
3293 "^log|history":
3294 (log,
3294 (log,
3295 [('f', 'follow', None,
3295 [('f', 'follow', None,
3296 _('follow changeset history, or file history across copies and renames')),
3296 _('follow changeset history, or file history across copies and renames')),
3297 ('', 'follow-first', None,
3297 ('', 'follow-first', None,
3298 _('only follow the first parent of merge changesets')),
3298 _('only follow the first parent of merge changesets')),
3299 ('d', 'date', '', _('show revisions matching date spec')),
3299 ('d', 'date', '', _('show revisions matching date spec')),
3300 ('C', 'copies', None, _('show copied files')),
3300 ('C', 'copies', None, _('show copied files')),
3301 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3301 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3302 ('r', 'rev', [], _('show the specified revision or range')),
3302 ('r', 'rev', [], _('show the specified revision or range')),
3303 ('', 'removed', None, _('include revisions where files were removed')),
3303 ('', 'removed', None, _('include revisions where files were removed')),
3304 ('m', 'only-merges', None, _('show only merges')),
3304 ('m', 'only-merges', None, _('show only merges')),
3305 ('u', 'user', [], _('revisions committed by user')),
3305 ('u', 'user', [], _('revisions committed by user')),
3306 ('b', 'only-branch', [],
3306 ('b', 'only-branch', [],
3307 _('show only changesets within the given named branch')),
3307 _('show only changesets within the given named branch')),
3308 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3308 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3309 ] + logopts + walkopts,
3309 ] + logopts + walkopts,
3310 _('[OPTION]... [FILE]')),
3310 _('[OPTION]... [FILE]')),
3311 "manifest":
3311 "manifest":
3312 (manifest,
3312 (manifest,
3313 [('r', 'rev', '', _('revision to display'))],
3313 [('r', 'rev', '', _('revision to display'))],
3314 _('[-r REV]')),
3314 _('[-r REV]')),
3315 "^merge":
3315 "^merge":
3316 (merge,
3316 (merge,
3317 [('f', 'force', None, _('force a merge with outstanding changes')),
3317 [('f', 'force', None, _('force a merge with outstanding changes')),
3318 ('r', 'rev', '', _('revision to merge')),
3318 ('r', 'rev', '', _('revision to merge')),
3319 ('S', 'show', None,
3319 ('S', 'show', None,
3320 _('review revisions to merge (no merge is performed)'))],
3320 _('review revisions to merge (no merge is performed)'))],
3321 _('[-f] [[-r] REV]')),
3321 _('[-f] [[-r] REV]')),
3322 "outgoing|out":
3322 "outgoing|out":
3323 (outgoing,
3323 (outgoing,
3324 [('f', 'force', None,
3324 [('f', 'force', None,
3325 _('run even when remote repository is unrelated')),
3325 _('run even when remote repository is unrelated')),
3326 ('r', 'rev', [],
3326 ('r', 'rev', [],
3327 _('a specific revision up to which you would like to push')),
3327 _('a specific revision up to which you would like to push')),
3328 ('n', 'newest-first', None, _('show newest record first')),
3328 ('n', 'newest-first', None, _('show newest record first')),
3329 ] + logopts + remoteopts,
3329 ] + logopts + remoteopts,
3330 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3330 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3331 "^parents":
3331 "^parents":
3332 (parents,
3332 (parents,
3333 [('r', 'rev', '', _('show parents from the specified revision')),
3333 [('r', 'rev', '', _('show parents from the specified revision')),
3334 ] + templateopts,
3334 ] + templateopts,
3335 _('[-r REV] [FILE]')),
3335 _('[-r REV] [FILE]')),
3336 "paths": (paths, [], _('[NAME]')),
3336 "paths": (paths, [], _('[NAME]')),
3337 "^pull":
3337 "^pull":
3338 (pull,
3338 (pull,
3339 [('u', 'update', None,
3339 [('u', 'update', None,
3340 _('update to new tip if changesets were pulled')),
3340 _('update to new tip if changesets were pulled')),
3341 ('f', 'force', None,
3341 ('f', 'force', None,
3342 _('run even when remote repository is unrelated')),
3342 _('run even when remote repository is unrelated')),
3343 ('r', 'rev', [],
3343 ('r', 'rev', [],
3344 _('a specific revision up to which you would like to pull')),
3344 _('a specific revision up to which you would like to pull')),
3345 ] + remoteopts,
3345 ] + remoteopts,
3346 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3346 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3347 "^push":
3347 "^push":
3348 (push,
3348 (push,
3349 [('f', 'force', None, _('force push')),
3349 [('f', 'force', None, _('force push')),
3350 ('r', 'rev', [],
3350 ('r', 'rev', [],
3351 _('a specific revision up to which you would like to push')),
3351 _('a specific revision up to which you would like to push')),
3352 ] + remoteopts,
3352 ] + remoteopts,
3353 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3353 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3354 "recover": (recover, []),
3354 "recover": (recover, []),
3355 "^remove|rm":
3355 "^remove|rm":
3356 (remove,
3356 (remove,
3357 [('A', 'after', None, _('record delete for missing files')),
3357 [('A', 'after', None, _('record delete for missing files')),
3358 ('f', 'force', None,
3358 ('f', 'force', None,
3359 _('remove (and delete) file even if added or modified')),
3359 _('remove (and delete) file even if added or modified')),
3360 ] + walkopts,
3360 ] + walkopts,
3361 _('[OPTION]... FILE...')),
3361 _('[OPTION]... FILE...')),
3362 "rename|mv":
3362 "rename|mv":
3363 (rename,
3363 (rename,
3364 [('A', 'after', None, _('record a rename that has already occurred')),
3364 [('A', 'after', None, _('record a rename that has already occurred')),
3365 ('f', 'force', None,
3365 ('f', 'force', None,
3366 _('forcibly copy over an existing managed file')),
3366 _('forcibly copy over an existing managed file')),
3367 ] + walkopts + dryrunopts,
3367 ] + walkopts + dryrunopts,
3368 _('[OPTION]... SOURCE... DEST')),
3368 _('[OPTION]... SOURCE... DEST')),
3369 "resolve":
3369 "resolve":
3370 (resolve,
3370 (resolve,
3371 [('a', 'all', None, _('remerge all unresolved files')),
3371 [('a', 'all', None, _('remerge all unresolved files')),
3372 ('l', 'list', None, _('list state of files needing merge')),
3372 ('l', 'list', None, _('list state of files needing merge')),
3373 ('m', 'mark', None, _('mark files as resolved')),
3373 ('m', 'mark', None, _('mark files as resolved')),
3374 ('u', 'unmark', None, _('unmark files as resolved'))]
3374 ('u', 'unmark', None, _('unmark files as resolved'))]
3375 + walkopts,
3375 + walkopts,
3376 _('[OPTION]... [FILE]...')),
3376 _('[OPTION]... [FILE]...')),
3377 "revert":
3377 "revert":
3378 (revert,
3378 (revert,
3379 [('a', 'all', None, _('revert all changes when no arguments given')),
3379 [('a', 'all', None, _('revert all changes when no arguments given')),
3380 ('d', 'date', '', _('tipmost revision matching date')),
3380 ('d', 'date', '', _('tipmost revision matching date')),
3381 ('r', 'rev', '', _('revision to revert to')),
3381 ('r', 'rev', '', _('revision to revert to')),
3382 ('', 'no-backup', None, _('do not save backup copies of files')),
3382 ('', 'no-backup', None, _('do not save backup copies of files')),
3383 ] + walkopts + dryrunopts,
3383 ] + walkopts + dryrunopts,
3384 _('[OPTION]... [-r REV] [NAME]...')),
3384 _('[OPTION]... [-r REV] [NAME]...')),
3385 "rollback": (rollback, []),
3385 "rollback": (rollback, []),
3386 "root": (root, []),
3386 "root": (root, []),
3387 "^serve":
3387 "^serve":
3388 (serve,
3388 (serve,
3389 [('A', 'accesslog', '', _('name of access log file to write to')),
3389 [('A', 'accesslog', '', _('name of access log file to write to')),
3390 ('d', 'daemon', None, _('run server in background')),
3390 ('d', 'daemon', None, _('run server in background')),
3391 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3391 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3392 ('E', 'errorlog', '', _('name of error log file to write to')),
3392 ('E', 'errorlog', '', _('name of error log file to write to')),
3393 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3393 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3394 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3394 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3395 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3395 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3396 ('n', 'name', '',
3396 ('n', 'name', '',
3397 _('name to show in web pages (default: working directory)')),
3397 _('name to show in web pages (default: working directory)')),
3398 ('', 'webdir-conf', '', _('name of the webdir config file'
3398 ('', 'webdir-conf', '', _('name of the webdir config file'
3399 ' (serve more than one repository)')),
3399 ' (serve more than one repository)')),
3400 ('', 'pid-file', '', _('name of file to write process ID to')),
3400 ('', 'pid-file', '', _('name of file to write process ID to')),
3401 ('', 'stdio', None, _('for remote clients')),
3401 ('', 'stdio', None, _('for remote clients')),
3402 ('t', 'templates', '', _('web templates to use')),
3402 ('t', 'templates', '', _('web templates to use')),
3403 ('', 'style', '', _('template style to use')),
3403 ('', 'style', '', _('template style to use')),
3404 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3404 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3405 ('', 'certificate', '', _('SSL certificate file'))],
3405 ('', 'certificate', '', _('SSL certificate file'))],
3406 _('[OPTION]...')),
3406 _('[OPTION]...')),
3407 "showconfig|debugconfig":
3407 "showconfig|debugconfig":
3408 (showconfig,
3408 (showconfig,
3409 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3409 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3410 _('[-u] [NAME]...')),
3410 _('[-u] [NAME]...')),
3411 "^status|st":
3411 "^status|st":
3412 (status,
3412 (status,
3413 [('A', 'all', None, _('show status of all files')),
3413 [('A', 'all', None, _('show status of all files')),
3414 ('m', 'modified', None, _('show only modified files')),
3414 ('m', 'modified', None, _('show only modified files')),
3415 ('a', 'added', None, _('show only added files')),
3415 ('a', 'added', None, _('show only added files')),
3416 ('r', 'removed', None, _('show only removed files')),
3416 ('r', 'removed', None, _('show only removed files')),
3417 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3417 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3418 ('c', 'clean', None, _('show only files without changes')),
3418 ('c', 'clean', None, _('show only files without changes')),
3419 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3419 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3420 ('i', 'ignored', None, _('show only ignored files')),
3420 ('i', 'ignored', None, _('show only ignored files')),
3421 ('n', 'no-status', None, _('hide status prefix')),
3421 ('n', 'no-status', None, _('hide status prefix')),
3422 ('C', 'copies', None, _('show source of copied files')),
3422 ('C', 'copies', None, _('show source of copied files')),
3423 ('0', 'print0', None,
3423 ('0', 'print0', None,
3424 _('end filenames with NUL, for use with xargs')),
3424 _('end filenames with NUL, for use with xargs')),
3425 ('', 'rev', [], _('show difference from revision')),
3425 ('', 'rev', [], _('show difference from revision')),
3426 ] + walkopts,
3426 ] + walkopts,
3427 _('[OPTION]... [FILE]...')),
3427 _('[OPTION]... [FILE]...')),
3428 "tag":
3428 "tag":
3429 (tag,
3429 (tag,
3430 [('f', 'force', None, _('replace existing tag')),
3430 [('f', 'force', None, _('replace existing tag')),
3431 ('l', 'local', None, _('make the tag local')),
3431 ('l', 'local', None, _('make the tag local')),
3432 ('r', 'rev', '', _('revision to tag')),
3432 ('r', 'rev', '', _('revision to tag')),
3433 ('', 'remove', None, _('remove a tag')),
3433 ('', 'remove', None, _('remove a tag')),
3434 # -l/--local is already there, commitopts cannot be used
3434 # -l/--local is already there, commitopts cannot be used
3435 ('m', 'message', '', _('use <text> as commit message')),
3435 ('m', 'message', '', _('use <text> as commit message')),
3436 ] + commitopts2,
3436 ] + commitopts2,
3437 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3437 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3438 "tags": (tags, []),
3438 "tags": (tags, []),
3439 "tip":
3439 "tip":
3440 (tip,
3440 (tip,
3441 [('p', 'patch', None, _('show patch')),
3441 [('p', 'patch', None, _('show patch')),
3442 ('g', 'git', None, _('use git extended diff format')),
3442 ('g', 'git', None, _('use git extended diff format')),
3443 ] + templateopts,
3443 ] + templateopts,
3444 _('[-p]')),
3444 _('[-p]')),
3445 "unbundle":
3445 "unbundle":
3446 (unbundle,
3446 (unbundle,
3447 [('u', 'update', None,
3447 [('u', 'update', None,
3448 _('update to new tip if changesets were unbundled'))],
3448 _('update to new tip if changesets were unbundled'))],
3449 _('[-u] FILE...')),
3449 _('[-u] FILE...')),
3450 "^update|up|checkout|co":
3450 "^update|up|checkout|co":
3451 (update,
3451 (update,
3452 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3452 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3453 ('d', 'date', '', _('tipmost revision matching date')),
3453 ('d', 'date', '', _('tipmost revision matching date')),
3454 ('r', 'rev', '', _('revision'))],
3454 ('r', 'rev', '', _('revision'))],
3455 _('[-C] [-d DATE] [[-r] REV]')),
3455 _('[-C] [-d DATE] [[-r] REV]')),
3456 "verify": (verify, []),
3456 "verify": (verify, []),
3457 "version": (version_, []),
3457 "version": (version_, []),
3458 }
3458 }
3459
3459
3460 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3460 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3461 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3461 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3462 optionalrepo = ("identify paths serve showconfig debugancestor")
3462 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,222 +1,222 b''
1 # filemerge.py - file-level merge handling for Mercurial
1 # filemerge.py - file-level merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007, 2008 Matt Mackall <mpm@selenic.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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import short
8 from node import short
9 from i18n import _
9 from i18n import _
10 import util, simplemerge, match
10 import util, simplemerge, match
11 import os, tempfile, re, filecmp
11 import os, tempfile, re, filecmp
12
12
13 def _toolstr(ui, tool, part, default=""):
13 def _toolstr(ui, tool, part, default=""):
14 return ui.config("merge-tools", tool + "." + part, default)
14 return ui.config("merge-tools", tool + "." + part, default)
15
15
16 def _toolbool(ui, tool, part, default=False):
16 def _toolbool(ui, tool, part, default=False):
17 return ui.configbool("merge-tools", tool + "." + part, default)
17 return ui.configbool("merge-tools", tool + "." + part, default)
18
18
19 def _findtool(ui, tool):
19 def _findtool(ui, tool):
20 if tool in ("internal:fail", "internal:local", "internal:other"):
20 if tool in ("internal:fail", "internal:local", "internal:other"):
21 return tool
21 return tool
22 k = _toolstr(ui, tool, "regkey")
22 k = _toolstr(ui, tool, "regkey")
23 if k:
23 if k:
24 p = util.lookup_reg(k, _toolstr(ui, tool, "regname"))
24 p = util.lookup_reg(k, _toolstr(ui, tool, "regname"))
25 if p:
25 if p:
26 p = util.find_exe(p + _toolstr(ui, tool, "regappend"))
26 p = util.find_exe(p + _toolstr(ui, tool, "regappend"))
27 if p:
27 if p:
28 return p
28 return p
29 return util.find_exe(_toolstr(ui, tool, "executable", tool))
29 return util.find_exe(_toolstr(ui, tool, "executable", tool))
30
30
31 def _picktool(repo, ui, path, binary, symlink):
31 def _picktool(repo, ui, path, binary, symlink):
32 def check(tool, pat, symlink, binary):
32 def check(tool, pat, symlink, binary):
33 tmsg = tool
33 tmsg = tool
34 if pat:
34 if pat:
35 tmsg += " specified for " + pat
35 tmsg += " specified for " + pat
36 if not _findtool(ui, tool):
36 if not _findtool(ui, tool):
37 if pat: # explicitly requested tool deserves a warning
37 if pat: # explicitly requested tool deserves a warning
38 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
38 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
39 else: # configured but non-existing tools are more silent
39 else: # configured but non-existing tools are more silent
40 ui.note(_("couldn't find merge tool %s\n") % tmsg)
40 ui.note(_("couldn't find merge tool %s\n") % tmsg)
41 elif symlink and not _toolbool(ui, tool, "symlink"):
41 elif symlink and not _toolbool(ui, tool, "symlink"):
42 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
42 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
43 elif binary and not _toolbool(ui, tool, "binary"):
43 elif binary and not _toolbool(ui, tool, "binary"):
44 ui.warn(_("tool %s can't handle binary\n") % tmsg)
44 ui.warn(_("tool %s can't handle binary\n") % tmsg)
45 elif not util.gui() and _toolbool(ui, tool, "gui"):
45 elif not util.gui() and _toolbool(ui, tool, "gui"):
46 ui.warn(_("tool %s requires a GUI\n") % tmsg)
46 ui.warn(_("tool %s requires a GUI\n") % tmsg)
47 else:
47 else:
48 return True
48 return True
49 return False
49 return False
50
50
51 # HGMERGE takes precedence
51 # HGMERGE takes precedence
52 hgmerge = os.environ.get("HGMERGE")
52 hgmerge = os.environ.get("HGMERGE")
53 if hgmerge:
53 if hgmerge:
54 return (hgmerge, hgmerge)
54 return (hgmerge, hgmerge)
55
55
56 # then patterns
56 # then patterns
57 for pat, tool in ui.configitems("merge-patterns"):
57 for pat, tool in ui.configitems("merge-patterns"):
58 mf = match.match(repo.root, '', [pat])
58 mf = match.match(repo.root, '', [pat])
59 if mf(path) and check(tool, pat, symlink, False):
59 if mf(path) and check(tool, pat, symlink, False):
60 toolpath = _findtool(ui, tool)
60 toolpath = _findtool(ui, tool)
61 return (tool, '"' + toolpath + '"')
61 return (tool, '"' + toolpath + '"')
62
62
63 # then merge tools
63 # then merge tools
64 tools = {}
64 tools = {}
65 for k,v in ui.configitems("merge-tools"):
65 for k,v in ui.configitems("merge-tools"):
66 t = k.split('.')[0]
66 t = k.split('.')[0]
67 if t not in tools:
67 if t not in tools:
68 tools[t] = int(_toolstr(ui, t, "priority", "0"))
68 tools[t] = int(_toolstr(ui, t, "priority", "0"))
69 names = tools.keys()
69 names = tools.keys()
70 tools = sorted([(-p,t) for t,p in tools.items()])
70 tools = sorted([(-p,t) for t,p in tools.items()])
71 uimerge = ui.config("ui", "merge")
71 uimerge = ui.config("ui", "merge")
72 if uimerge:
72 if uimerge:
73 if uimerge not in names:
73 if uimerge not in names:
74 return (uimerge, uimerge)
74 return (uimerge, uimerge)
75 tools.insert(0, (None, uimerge)) # highest priority
75 tools.insert(0, (None, uimerge)) # highest priority
76 tools.append((None, "hgmerge")) # the old default, if found
76 tools.append((None, "hgmerge")) # the old default, if found
77 for p,t in tools:
77 for p,t in tools:
78 if check(t, None, symlink, binary):
78 if check(t, None, symlink, binary):
79 toolpath = _findtool(ui, t)
79 toolpath = _findtool(ui, t)
80 return (t, '"' + toolpath + '"')
80 return (t, '"' + toolpath + '"')
81 # internal merge as last resort
81 # internal merge as last resort
82 return (not (symlink or binary) and "internal:merge" or None, None)
82 return (not (symlink or binary) and "internal:merge" or None, None)
83
83
84 def _eoltype(data):
84 def _eoltype(data):
85 "Guess the EOL type of a file"
85 "Guess the EOL type of a file"
86 if '\0' in data: # binary
86 if '\0' in data: # binary
87 return None
87 return None
88 if '\r\n' in data: # Windows
88 if '\r\n' in data: # Windows
89 return '\r\n'
89 return '\r\n'
90 if '\r' in data: # Old Mac
90 if '\r' in data: # Old Mac
91 return '\r'
91 return '\r'
92 if '\n' in data: # UNIX
92 if '\n' in data: # UNIX
93 return '\n'
93 return '\n'
94 return None # unknown
94 return None # unknown
95
95
96 def _matcheol(file, origfile):
96 def _matcheol(file, origfile):
97 "Convert EOL markers in a file to match origfile"
97 "Convert EOL markers in a file to match origfile"
98 tostyle = _eoltype(open(origfile, "rb").read())
98 tostyle = _eoltype(open(origfile, "rb").read())
99 if tostyle:
99 if tostyle:
100 data = open(file, "rb").read()
100 data = open(file, "rb").read()
101 style = _eoltype(data)
101 style = _eoltype(data)
102 if style:
102 if style:
103 newdata = data.replace(style, tostyle)
103 newdata = data.replace(style, tostyle)
104 if newdata != data:
104 if newdata != data:
105 open(file, "wb").write(newdata)
105 open(file, "wb").write(newdata)
106
106
107 def filemerge(repo, mynode, orig, fcd, fco, fca):
107 def filemerge(repo, mynode, orig, fcd, fco, fca):
108 """perform a 3-way merge in the working directory
108 """perform a 3-way merge in the working directory
109
109
110 mynode = parent node before merge
110 mynode = parent node before merge
111 orig = original local filename before merge
111 orig = original local filename before merge
112 fco = other file context
112 fco = other file context
113 fca = ancestor file context
113 fca = ancestor file context
114 fcd = local file context for current/destination file
114 fcd = local file context for current/destination file
115 """
115 """
116
116
117 def temp(prefix, ctx):
117 def temp(prefix, ctx):
118 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
118 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
119 (fd, name) = tempfile.mkstemp(prefix=pre)
119 (fd, name) = tempfile.mkstemp(prefix=pre)
120 data = repo.wwritedata(ctx.path(), ctx.data())
120 data = repo.wwritedata(ctx.path(), ctx.data())
121 f = os.fdopen(fd, "wb")
121 f = os.fdopen(fd, "wb")
122 f.write(data)
122 f.write(data)
123 f.close()
123 f.close()
124 return name
124 return name
125
125
126 def isbin(ctx):
126 def isbin(ctx):
127 try:
127 try:
128 return util.binary(ctx.data())
128 return util.binary(ctx.data())
129 except IOError:
129 except IOError:
130 return False
130 return False
131
131
132 if not fco.cmp(fcd.data()): # files identical?
132 if not fco.cmp(fcd.data()): # files identical?
133 return None
133 return None
134
134
135 ui = repo.ui
135 ui = repo.ui
136 fd = fcd.path()
136 fd = fcd.path()
137 binary = isbin(fcd) or isbin(fco) or isbin(fca)
137 binary = isbin(fcd) or isbin(fco) or isbin(fca)
138 symlink = 'l' in fcd.flags() + fco.flags()
138 symlink = 'l' in fcd.flags() + fco.flags()
139 tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
139 tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
140 ui.debug(_("picked tool '%s' for %s (binary %s symlink %s)\n") %
140 ui.debug(_("picked tool '%s' for %s (binary %s symlink %s)\n") %
141 (tool, fd, binary, symlink))
141 (tool, fd, binary, symlink))
142
142
143 if not tool:
143 if not tool:
144 tool = "internal:local"
144 tool = "internal:local"
145 if ui.prompt(_(" no tool found to merge %s\n"
145 if ui.prompt(_(" no tool found to merge %s\n"
146 "keep (l)ocal or take (o)ther?") % fd,
146 "keep (l)ocal or take (o)ther?") % fd,
147 (_("&Local"), _("&Other")), _("l")) != _("l"):
147 (_("&Local"), _("&Other")), _("l")) != _("l"):
148 tool = "internal:other"
148 tool = "internal:other"
149 if tool == "internal:local":
149 if tool == "internal:local":
150 return 0
150 return 0
151 if tool == "internal:other":
151 if tool == "internal:other":
152 repo.wwrite(fd, fco.data(), fco.flags())
152 repo.wwrite(fd, fco.data(), fco.flags())
153 return 0
153 return 0
154 if tool == "internal:fail":
154 if tool == "internal:fail":
155 return 1
155 return 1
156
156
157 # do the actual merge
157 # do the actual merge
158 a = repo.wjoin(fd)
158 a = repo.wjoin(fd)
159 b = temp("base", fca)
159 b = temp("base", fca)
160 c = temp("other", fco)
160 c = temp("other", fco)
161 out = ""
161 out = ""
162 back = a + ".orig"
162 back = a + ".orig"
163 util.copyfile(a, back)
163 util.copyfile(a, back)
164
164
165 if orig != fco.path():
165 if orig != fco.path():
166 repo.ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
166 ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
167 else:
167 else:
168 repo.ui.status(_("merging %s\n") % fd)
168 ui.status(_("merging %s\n") % fd)
169
169
170 repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcd, fco, fca))
170 ui.debug(_("my %s other %s ancestor %s\n") % (fcd, fco, fca))
171
171
172 # do we attempt to simplemerge first?
172 # do we attempt to simplemerge first?
173 if _toolbool(ui, tool, "premerge", not (binary or symlink)):
173 if _toolbool(ui, tool, "premerge", not (binary or symlink)):
174 r = simplemerge.simplemerge(ui, a, b, c, quiet=True)
174 r = simplemerge.simplemerge(ui, a, b, c, quiet=True)
175 if not r:
175 if not r:
176 ui.debug(_(" premerge successful\n"))
176 ui.debug(_(" premerge successful\n"))
177 os.unlink(back)
177 os.unlink(back)
178 os.unlink(b)
178 os.unlink(b)
179 os.unlink(c)
179 os.unlink(c)
180 return 0
180 return 0
181 util.copyfile(back, a) # restore from backup and try again
181 util.copyfile(back, a) # restore from backup and try again
182
182
183 env = dict(HG_FILE=fd,
183 env = dict(HG_FILE=fd,
184 HG_MY_NODE=short(mynode),
184 HG_MY_NODE=short(mynode),
185 HG_OTHER_NODE=str(fco.changectx()),
185 HG_OTHER_NODE=str(fco.changectx()),
186 HG_MY_ISLINK='l' in fcd.flags(),
186 HG_MY_ISLINK='l' in fcd.flags(),
187 HG_OTHER_ISLINK='l' in fco.flags(),
187 HG_OTHER_ISLINK='l' in fco.flags(),
188 HG_BASE_ISLINK='l' in fca.flags())
188 HG_BASE_ISLINK='l' in fca.flags())
189
189
190 if tool == "internal:merge":
190 if tool == "internal:merge":
191 r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other'])
191 r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other'])
192 else:
192 else:
193 args = _toolstr(ui, tool, "args", '$local $base $other')
193 args = _toolstr(ui, tool, "args", '$local $base $other')
194 if "$output" in args:
194 if "$output" in args:
195 out, a = a, back # read input from backup, write to original
195 out, a = a, back # read input from backup, write to original
196 replace = dict(local=a, base=b, other=c, output=out)
196 replace = dict(local=a, base=b, other=c, output=out)
197 args = re.sub("\$(local|base|other|output)",
197 args = re.sub("\$(local|base|other|output)",
198 lambda x: '"%s"' % replace[x.group()[1:]], args)
198 lambda x: '"%s"' % replace[x.group()[1:]], args)
199 r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env)
199 r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env)
200
200
201 if not r and _toolbool(ui, tool, "checkconflicts"):
201 if not r and _toolbool(ui, tool, "checkconflicts"):
202 if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data()):
202 if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data()):
203 r = 1
203 r = 1
204
204
205 if not r and _toolbool(ui, tool, "checkchanged"):
205 if not r and _toolbool(ui, tool, "checkchanged"):
206 if filecmp.cmp(repo.wjoin(fd), back):
206 if filecmp.cmp(repo.wjoin(fd), back):
207 if ui.prompt(_(" output file %s appears unchanged\n"
207 if ui.prompt(_(" output file %s appears unchanged\n"
208 "was merge successful (yn)?") % fd,
208 "was merge successful (yn)?") % fd,
209 (_("&Yes"), _("&No")), _("n")) != _("y"):
209 (_("&Yes"), _("&No")), _("n")) != _("y"):
210 r = 1
210 r = 1
211
211
212 if _toolbool(ui, tool, "fixeol"):
212 if _toolbool(ui, tool, "fixeol"):
213 _matcheol(repo.wjoin(fd), back)
213 _matcheol(repo.wjoin(fd), back)
214
214
215 if r:
215 if r:
216 repo.ui.warn(_("merging %s failed!\n") % fd)
216 ui.warn(_("merging %s failed!\n") % fd)
217 else:
217 else:
218 os.unlink(back)
218 os.unlink(back)
219
219
220 os.unlink(b)
220 os.unlink(b)
221 os.unlink(c)
221 os.unlink(c)
222 return r
222 return r
General Comments 0
You need to be logged in to leave comments. Login now