##// END OF EJS Templates
match: stop passing files through commitfunc
Matt Mackall -
r6600:b822a379 default
parent child Browse files
Show More
@@ -1,527 +1,528 b''
1 # record.py
1 # record.py
2 #
2 #
3 # Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
3 # Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of
5 # This software may be used and distributed according to the terms of
6 # the GNU General Public License, incorporated herein by reference.
6 # the GNU General Public License, incorporated herein by reference.
7
7
8 '''interactive change selection during commit or qrefresh'''
8 '''interactive change selection during commit or qrefresh'''
9
9
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 from mercurial import cmdutil, commands, extensions, hg, mdiff, patch
11 from mercurial import cmdutil, commands, extensions, hg, mdiff, patch
12 from mercurial import util
12 from mercurial import util
13 import copy, cStringIO, errno, operator, os, re, tempfile
13 import copy, cStringIO, errno, operator, os, re, tempfile
14
14
15 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
15 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
16
16
17 def scanpatch(fp):
17 def scanpatch(fp):
18 """like patch.iterhunks, but yield different events
18 """like patch.iterhunks, but yield different events
19
19
20 - ('file', [header_lines + fromfile + tofile])
20 - ('file', [header_lines + fromfile + tofile])
21 - ('context', [context_lines])
21 - ('context', [context_lines])
22 - ('hunk', [hunk_lines])
22 - ('hunk', [hunk_lines])
23 - ('range', (-start,len, +start,len, diffp))
23 - ('range', (-start,len, +start,len, diffp))
24 """
24 """
25 lr = patch.linereader(fp)
25 lr = patch.linereader(fp)
26
26
27 def scanwhile(first, p):
27 def scanwhile(first, p):
28 """scan lr while predicate holds"""
28 """scan lr while predicate holds"""
29 lines = [first]
29 lines = [first]
30 while True:
30 while True:
31 line = lr.readline()
31 line = lr.readline()
32 if not line:
32 if not line:
33 break
33 break
34 if p(line):
34 if p(line):
35 lines.append(line)
35 lines.append(line)
36 else:
36 else:
37 lr.push(line)
37 lr.push(line)
38 break
38 break
39 return lines
39 return lines
40
40
41 while True:
41 while True:
42 line = lr.readline()
42 line = lr.readline()
43 if not line:
43 if not line:
44 break
44 break
45 if line.startswith('diff --git a/'):
45 if line.startswith('diff --git a/'):
46 def notheader(line):
46 def notheader(line):
47 s = line.split(None, 1)
47 s = line.split(None, 1)
48 return not s or s[0] not in ('---', 'diff')
48 return not s or s[0] not in ('---', 'diff')
49 header = scanwhile(line, notheader)
49 header = scanwhile(line, notheader)
50 fromfile = lr.readline()
50 fromfile = lr.readline()
51 if fromfile.startswith('---'):
51 if fromfile.startswith('---'):
52 tofile = lr.readline()
52 tofile = lr.readline()
53 header += [fromfile, tofile]
53 header += [fromfile, tofile]
54 else:
54 else:
55 lr.push(fromfile)
55 lr.push(fromfile)
56 yield 'file', header
56 yield 'file', header
57 elif line[0] == ' ':
57 elif line[0] == ' ':
58 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
58 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
59 elif line[0] in '-+':
59 elif line[0] in '-+':
60 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
60 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
61 else:
61 else:
62 m = lines_re.match(line)
62 m = lines_re.match(line)
63 if m:
63 if m:
64 yield 'range', m.groups()
64 yield 'range', m.groups()
65 else:
65 else:
66 raise patch.PatchError('unknown patch content: %r' % line)
66 raise patch.PatchError('unknown patch content: %r' % line)
67
67
68 class header(object):
68 class header(object):
69 """patch header
69 """patch header
70
70
71 XXX shoudn't we move this to mercurial/patch.py ?
71 XXX shoudn't we move this to mercurial/patch.py ?
72 """
72 """
73 diff_re = re.compile('diff --git a/(.*) b/(.*)$')
73 diff_re = re.compile('diff --git a/(.*) b/(.*)$')
74 allhunks_re = re.compile('(?:index|new file|deleted file) ')
74 allhunks_re = re.compile('(?:index|new file|deleted file) ')
75 pretty_re = re.compile('(?:new file|deleted file) ')
75 pretty_re = re.compile('(?:new file|deleted file) ')
76 special_re = re.compile('(?:index|new|deleted|copy|rename) ')
76 special_re = re.compile('(?:index|new|deleted|copy|rename) ')
77
77
78 def __init__(self, header):
78 def __init__(self, header):
79 self.header = header
79 self.header = header
80 self.hunks = []
80 self.hunks = []
81
81
82 def binary(self):
82 def binary(self):
83 for h in self.header:
83 for h in self.header:
84 if h.startswith('index '):
84 if h.startswith('index '):
85 return True
85 return True
86
86
87 def pretty(self, fp):
87 def pretty(self, fp):
88 for h in self.header:
88 for h in self.header:
89 if h.startswith('index '):
89 if h.startswith('index '):
90 fp.write(_('this modifies a binary file (all or nothing)\n'))
90 fp.write(_('this modifies a binary file (all or nothing)\n'))
91 break
91 break
92 if self.pretty_re.match(h):
92 if self.pretty_re.match(h):
93 fp.write(h)
93 fp.write(h)
94 if self.binary():
94 if self.binary():
95 fp.write(_('this is a binary file\n'))
95 fp.write(_('this is a binary file\n'))
96 break
96 break
97 if h.startswith('---'):
97 if h.startswith('---'):
98 fp.write(_('%d hunks, %d lines changed\n') %
98 fp.write(_('%d hunks, %d lines changed\n') %
99 (len(self.hunks),
99 (len(self.hunks),
100 sum([h.added + h.removed for h in self.hunks])))
100 sum([h.added + h.removed for h in self.hunks])))
101 break
101 break
102 fp.write(h)
102 fp.write(h)
103
103
104 def write(self, fp):
104 def write(self, fp):
105 fp.write(''.join(self.header))
105 fp.write(''.join(self.header))
106
106
107 def allhunks(self):
107 def allhunks(self):
108 for h in self.header:
108 for h in self.header:
109 if self.allhunks_re.match(h):
109 if self.allhunks_re.match(h):
110 return True
110 return True
111
111
112 def files(self):
112 def files(self):
113 fromfile, tofile = self.diff_re.match(self.header[0]).groups()
113 fromfile, tofile = self.diff_re.match(self.header[0]).groups()
114 if fromfile == tofile:
114 if fromfile == tofile:
115 return [fromfile]
115 return [fromfile]
116 return [fromfile, tofile]
116 return [fromfile, tofile]
117
117
118 def filename(self):
118 def filename(self):
119 return self.files()[-1]
119 return self.files()[-1]
120
120
121 def __repr__(self):
121 def __repr__(self):
122 return '<header %s>' % (' '.join(map(repr, self.files())))
122 return '<header %s>' % (' '.join(map(repr, self.files())))
123
123
124 def special(self):
124 def special(self):
125 for h in self.header:
125 for h in self.header:
126 if self.special_re.match(h):
126 if self.special_re.match(h):
127 return True
127 return True
128
128
129 def countchanges(hunk):
129 def countchanges(hunk):
130 """hunk -> (n+,n-)"""
130 """hunk -> (n+,n-)"""
131 add = len([h for h in hunk if h[0] == '+'])
131 add = len([h for h in hunk if h[0] == '+'])
132 rem = len([h for h in hunk if h[0] == '-'])
132 rem = len([h for h in hunk if h[0] == '-'])
133 return add, rem
133 return add, rem
134
134
135 class hunk(object):
135 class hunk(object):
136 """patch hunk
136 """patch hunk
137
137
138 XXX shouldn't we merge this with patch.hunk ?
138 XXX shouldn't we merge this with patch.hunk ?
139 """
139 """
140 maxcontext = 3
140 maxcontext = 3
141
141
142 def __init__(self, header, fromline, toline, proc, before, hunk, after):
142 def __init__(self, header, fromline, toline, proc, before, hunk, after):
143 def trimcontext(number, lines):
143 def trimcontext(number, lines):
144 delta = len(lines) - self.maxcontext
144 delta = len(lines) - self.maxcontext
145 if False and delta > 0:
145 if False and delta > 0:
146 return number + delta, lines[:self.maxcontext]
146 return number + delta, lines[:self.maxcontext]
147 return number, lines
147 return number, lines
148
148
149 self.header = header
149 self.header = header
150 self.fromline, self.before = trimcontext(fromline, before)
150 self.fromline, self.before = trimcontext(fromline, before)
151 self.toline, self.after = trimcontext(toline, after)
151 self.toline, self.after = trimcontext(toline, after)
152 self.proc = proc
152 self.proc = proc
153 self.hunk = hunk
153 self.hunk = hunk
154 self.added, self.removed = countchanges(self.hunk)
154 self.added, self.removed = countchanges(self.hunk)
155
155
156 def write(self, fp):
156 def write(self, fp):
157 delta = len(self.before) + len(self.after)
157 delta = len(self.before) + len(self.after)
158 fromlen = delta + self.removed
158 fromlen = delta + self.removed
159 tolen = delta + self.added
159 tolen = delta + self.added
160 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
160 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
161 (self.fromline, fromlen, self.toline, tolen,
161 (self.fromline, fromlen, self.toline, tolen,
162 self.proc and (' ' + self.proc)))
162 self.proc and (' ' + self.proc)))
163 fp.write(''.join(self.before + self.hunk + self.after))
163 fp.write(''.join(self.before + self.hunk + self.after))
164
164
165 pretty = write
165 pretty = write
166
166
167 def filename(self):
167 def filename(self):
168 return self.header.filename()
168 return self.header.filename()
169
169
170 def __repr__(self):
170 def __repr__(self):
171 return '<hunk %r@%d>' % (self.filename(), self.fromline)
171 return '<hunk %r@%d>' % (self.filename(), self.fromline)
172
172
173 def parsepatch(fp):
173 def parsepatch(fp):
174 """patch -> [] of hunks """
174 """patch -> [] of hunks """
175 class parser(object):
175 class parser(object):
176 """patch parsing state machine"""
176 """patch parsing state machine"""
177 def __init__(self):
177 def __init__(self):
178 self.fromline = 0
178 self.fromline = 0
179 self.toline = 0
179 self.toline = 0
180 self.proc = ''
180 self.proc = ''
181 self.header = None
181 self.header = None
182 self.context = []
182 self.context = []
183 self.before = []
183 self.before = []
184 self.hunk = []
184 self.hunk = []
185 self.stream = []
185 self.stream = []
186
186
187 def addrange(self, (fromstart, fromend, tostart, toend, proc)):
187 def addrange(self, (fromstart, fromend, tostart, toend, proc)):
188 self.fromline = int(fromstart)
188 self.fromline = int(fromstart)
189 self.toline = int(tostart)
189 self.toline = int(tostart)
190 self.proc = proc
190 self.proc = proc
191
191
192 def addcontext(self, context):
192 def addcontext(self, context):
193 if self.hunk:
193 if self.hunk:
194 h = hunk(self.header, self.fromline, self.toline, self.proc,
194 h = hunk(self.header, self.fromline, self.toline, self.proc,
195 self.before, self.hunk, context)
195 self.before, self.hunk, context)
196 self.header.hunks.append(h)
196 self.header.hunks.append(h)
197 self.stream.append(h)
197 self.stream.append(h)
198 self.fromline += len(self.before) + h.removed
198 self.fromline += len(self.before) + h.removed
199 self.toline += len(self.before) + h.added
199 self.toline += len(self.before) + h.added
200 self.before = []
200 self.before = []
201 self.hunk = []
201 self.hunk = []
202 self.proc = ''
202 self.proc = ''
203 self.context = context
203 self.context = context
204
204
205 def addhunk(self, hunk):
205 def addhunk(self, hunk):
206 if self.context:
206 if self.context:
207 self.before = self.context
207 self.before = self.context
208 self.context = []
208 self.context = []
209 self.hunk = data
209 self.hunk = data
210
210
211 def newfile(self, hdr):
211 def newfile(self, hdr):
212 self.addcontext([])
212 self.addcontext([])
213 h = header(hdr)
213 h = header(hdr)
214 self.stream.append(h)
214 self.stream.append(h)
215 self.header = h
215 self.header = h
216
216
217 def finished(self):
217 def finished(self):
218 self.addcontext([])
218 self.addcontext([])
219 return self.stream
219 return self.stream
220
220
221 transitions = {
221 transitions = {
222 'file': {'context': addcontext,
222 'file': {'context': addcontext,
223 'file': newfile,
223 'file': newfile,
224 'hunk': addhunk,
224 'hunk': addhunk,
225 'range': addrange},
225 'range': addrange},
226 'context': {'file': newfile,
226 'context': {'file': newfile,
227 'hunk': addhunk,
227 'hunk': addhunk,
228 'range': addrange},
228 'range': addrange},
229 'hunk': {'context': addcontext,
229 'hunk': {'context': addcontext,
230 'file': newfile,
230 'file': newfile,
231 'range': addrange},
231 'range': addrange},
232 'range': {'context': addcontext,
232 'range': {'context': addcontext,
233 'hunk': addhunk},
233 'hunk': addhunk},
234 }
234 }
235
235
236 p = parser()
236 p = parser()
237
237
238 state = 'context'
238 state = 'context'
239 for newstate, data in scanpatch(fp):
239 for newstate, data in scanpatch(fp):
240 try:
240 try:
241 p.transitions[state][newstate](p, data)
241 p.transitions[state][newstate](p, data)
242 except KeyError:
242 except KeyError:
243 raise patch.PatchError('unhandled transition: %s -> %s' %
243 raise patch.PatchError('unhandled transition: %s -> %s' %
244 (state, newstate))
244 (state, newstate))
245 state = newstate
245 state = newstate
246 return p.finished()
246 return p.finished()
247
247
248 def filterpatch(ui, chunks):
248 def filterpatch(ui, chunks):
249 """Interactively filter patch chunks into applied-only chunks"""
249 """Interactively filter patch chunks into applied-only chunks"""
250 chunks = list(chunks)
250 chunks = list(chunks)
251 chunks.reverse()
251 chunks.reverse()
252 seen = {}
252 seen = {}
253 def consumefile():
253 def consumefile():
254 """fetch next portion from chunks until a 'header' is seen
254 """fetch next portion from chunks until a 'header' is seen
255 NB: header == new-file mark
255 NB: header == new-file mark
256 """
256 """
257 consumed = []
257 consumed = []
258 while chunks:
258 while chunks:
259 if isinstance(chunks[-1], header):
259 if isinstance(chunks[-1], header):
260 break
260 break
261 else:
261 else:
262 consumed.append(chunks.pop())
262 consumed.append(chunks.pop())
263 return consumed
263 return consumed
264
264
265 resp_all = [None] # this two are changed from inside prompt,
265 resp_all = [None] # this two are changed from inside prompt,
266 resp_file = [None] # so can't be usual variables
266 resp_file = [None] # so can't be usual variables
267 applied = {} # 'filename' -> [] of chunks
267 applied = {} # 'filename' -> [] of chunks
268 def prompt(query):
268 def prompt(query):
269 """prompt query, and process base inputs
269 """prompt query, and process base inputs
270
270
271 - y/n for the rest of file
271 - y/n for the rest of file
272 - y/n for the rest
272 - y/n for the rest
273 - ? (help)
273 - ? (help)
274 - q (quit)
274 - q (quit)
275
275
276 else, input is returned to the caller.
276 else, input is returned to the caller.
277 """
277 """
278 if resp_all[0] is not None:
278 if resp_all[0] is not None:
279 return resp_all[0]
279 return resp_all[0]
280 if resp_file[0] is not None:
280 if resp_file[0] is not None:
281 return resp_file[0]
281 return resp_file[0]
282 while True:
282 while True:
283 r = (ui.prompt(query + _(' [Ynsfdaq?] '), '(?i)[Ynsfdaq?]?$')
283 r = (ui.prompt(query + _(' [Ynsfdaq?] '), '(?i)[Ynsfdaq?]?$')
284 or 'y').lower()
284 or 'y').lower()
285 if r == '?':
285 if r == '?':
286 c = record.__doc__.find('y - record this change')
286 c = record.__doc__.find('y - record this change')
287 for l in record.__doc__[c:].splitlines():
287 for l in record.__doc__[c:].splitlines():
288 if l: ui.write(_(l.strip()), '\n')
288 if l: ui.write(_(l.strip()), '\n')
289 continue
289 continue
290 elif r == 's':
290 elif r == 's':
291 r = resp_file[0] = 'n'
291 r = resp_file[0] = 'n'
292 elif r == 'f':
292 elif r == 'f':
293 r = resp_file[0] = 'y'
293 r = resp_file[0] = 'y'
294 elif r == 'd':
294 elif r == 'd':
295 r = resp_all[0] = 'n'
295 r = resp_all[0] = 'n'
296 elif r == 'a':
296 elif r == 'a':
297 r = resp_all[0] = 'y'
297 r = resp_all[0] = 'y'
298 elif r == 'q':
298 elif r == 'q':
299 raise util.Abort(_('user quit'))
299 raise util.Abort(_('user quit'))
300 return r
300 return r
301 while chunks:
301 while chunks:
302 chunk = chunks.pop()
302 chunk = chunks.pop()
303 if isinstance(chunk, header):
303 if isinstance(chunk, header):
304 # new-file mark
304 # new-file mark
305 resp_file = [None]
305 resp_file = [None]
306 fixoffset = 0
306 fixoffset = 0
307 hdr = ''.join(chunk.header)
307 hdr = ''.join(chunk.header)
308 if hdr in seen:
308 if hdr in seen:
309 consumefile()
309 consumefile()
310 continue
310 continue
311 seen[hdr] = True
311 seen[hdr] = True
312 if resp_all[0] is None:
312 if resp_all[0] is None:
313 chunk.pretty(ui)
313 chunk.pretty(ui)
314 r = prompt(_('examine changes to %s?') %
314 r = prompt(_('examine changes to %s?') %
315 _(' and ').join(map(repr, chunk.files())))
315 _(' and ').join(map(repr, chunk.files())))
316 if r == 'y':
316 if r == 'y':
317 applied[chunk.filename()] = [chunk]
317 applied[chunk.filename()] = [chunk]
318 if chunk.allhunks():
318 if chunk.allhunks():
319 applied[chunk.filename()] += consumefile()
319 applied[chunk.filename()] += consumefile()
320 else:
320 else:
321 consumefile()
321 consumefile()
322 else:
322 else:
323 # new hunk
323 # new hunk
324 if resp_file[0] is None and resp_all[0] is None:
324 if resp_file[0] is None and resp_all[0] is None:
325 chunk.pretty(ui)
325 chunk.pretty(ui)
326 r = prompt(_('record this change to %r?') %
326 r = prompt(_('record this change to %r?') %
327 chunk.filename())
327 chunk.filename())
328 if r == 'y':
328 if r == 'y':
329 if fixoffset:
329 if fixoffset:
330 chunk = copy.copy(chunk)
330 chunk = copy.copy(chunk)
331 chunk.toline += fixoffset
331 chunk.toline += fixoffset
332 applied[chunk.filename()].append(chunk)
332 applied[chunk.filename()].append(chunk)
333 else:
333 else:
334 fixoffset += chunk.removed - chunk.added
334 fixoffset += chunk.removed - chunk.added
335 return reduce(operator.add, [h for h in applied.itervalues()
335 return reduce(operator.add, [h for h in applied.itervalues()
336 if h[0].special() or len(h) > 1], [])
336 if h[0].special() or len(h) > 1], [])
337
337
338 def record(ui, repo, *pats, **opts):
338 def record(ui, repo, *pats, **opts):
339 '''interactively select changes to commit
339 '''interactively select changes to commit
340
340
341 If a list of files is omitted, all changes reported by "hg status"
341 If a list of files is omitted, all changes reported by "hg status"
342 will be candidates for recording.
342 will be candidates for recording.
343
343
344 See 'hg help dates' for a list of formats valid for -d/--date.
344 See 'hg help dates' for a list of formats valid for -d/--date.
345
345
346 You will be prompted for whether to record changes to each
346 You will be prompted for whether to record changes to each
347 modified file, and for files with multiple changes, for each
347 modified file, and for files with multiple changes, for each
348 change to use. For each query, the following responses are
348 change to use. For each query, the following responses are
349 possible:
349 possible:
350
350
351 y - record this change
351 y - record this change
352 n - skip this change
352 n - skip this change
353
353
354 s - skip remaining changes to this file
354 s - skip remaining changes to this file
355 f - record remaining changes to this file
355 f - record remaining changes to this file
356
356
357 d - done, skip remaining changes and files
357 d - done, skip remaining changes and files
358 a - record all changes to all remaining files
358 a - record all changes to all remaining files
359 q - quit, recording no changes
359 q - quit, recording no changes
360
360
361 ? - display help'''
361 ? - display help'''
362
362
363 def record_committer(ui, repo, pats, opts):
363 def record_committer(ui, repo, pats, opts):
364 commands.commit(ui, repo, *pats, **opts)
364 commands.commit(ui, repo, *pats, **opts)
365
365
366 dorecord(ui, repo, record_committer, *pats, **opts)
366 dorecord(ui, repo, record_committer, *pats, **opts)
367
367
368
368
369 def qrecord(ui, repo, patch, *pats, **opts):
369 def qrecord(ui, repo, patch, *pats, **opts):
370 '''interactively record a new patch
370 '''interactively record a new patch
371
371
372 see 'hg help qnew' & 'hg help record' for more information and usage
372 see 'hg help qnew' & 'hg help record' for more information and usage
373 '''
373 '''
374
374
375 try:
375 try:
376 mq = extensions.find('mq')
376 mq = extensions.find('mq')
377 except KeyError:
377 except KeyError:
378 raise util.Abort(_("'mq' extension not loaded"))
378 raise util.Abort(_("'mq' extension not loaded"))
379
379
380 def qrecord_committer(ui, repo, pats, opts):
380 def qrecord_committer(ui, repo, pats, opts):
381 mq.new(ui, repo, patch, *pats, **opts)
381 mq.new(ui, repo, patch, *pats, **opts)
382
382
383 opts = opts.copy()
383 opts = opts.copy()
384 opts['force'] = True # always 'qnew -f'
384 opts['force'] = True # always 'qnew -f'
385 dorecord(ui, repo, qrecord_committer, *pats, **opts)
385 dorecord(ui, repo, qrecord_committer, *pats, **opts)
386
386
387
387
388 def dorecord(ui, repo, committer, *pats, **opts):
388 def dorecord(ui, repo, committer, *pats, **opts):
389 if not ui.interactive:
389 if not ui.interactive:
390 raise util.Abort(_('running non-interactively, use commit instead'))
390 raise util.Abort(_('running non-interactively, use commit instead'))
391
391
392 def recordfunc(ui, repo, files, message, match, opts):
392 def recordfunc(ui, repo, message, match, opts):
393 """This is generic record driver.
393 """This is generic record driver.
394
394
395 It's job is to interactively filter local changes, and accordingly
395 It's job is to interactively filter local changes, and accordingly
396 prepare working dir into a state, where the job can be delegated to
396 prepare working dir into a state, where the job can be delegated to
397 non-interactive commit command such as 'commit' or 'qrefresh'.
397 non-interactive commit command such as 'commit' or 'qrefresh'.
398
398
399 After the actual job is done by non-interactive command, working dir
399 After the actual job is done by non-interactive command, working dir
400 state is restored to original.
400 state is restored to original.
401
401
402 In the end we'll record intresting changes, and everything else will be
402 In the end we'll record intresting changes, and everything else will be
403 left in place, so the user can continue his work.
403 left in place, so the user can continue his work.
404 """
404 """
405 if files:
405 if match.files():
406 changes = None
406 changes = None
407 else:
407 else:
408 changes = repo.status(files=files, match=match)[:5]
408 changes = repo.status(match=match)[:5]
409 modified, added, removed = changes[:3]
409 modified, added, removed = changes[:3]
410 files = modified + added + removed
410 match = cmdutil.matchfiles(repo, modified + added + removed)
411 diffopts = mdiff.diffopts(git=True, nodates=True)
411 diffopts = mdiff.diffopts(git=True, nodates=True)
412 fp = cStringIO.StringIO()
412 fp = cStringIO.StringIO()
413 patch.diff(repo, repo.dirstate.parents()[0], files=files,
413 patch.diff(repo, repo.dirstate.parents()[0], files=match.files(),
414 match=match, changes=changes, opts=diffopts, fp=fp)
414 match=match, changes=changes, opts=diffopts, fp=fp)
415 fp.seek(0)
415 fp.seek(0)
416
416
417 # 1. filter patch, so we have intending-to apply subset of it
417 # 1. filter patch, so we have intending-to apply subset of it
418 chunks = filterpatch(ui, parsepatch(fp))
418 chunks = filterpatch(ui, parsepatch(fp))
419 del fp
419 del fp
420
420
421 contenders = {}
421 contenders = {}
422 for h in chunks:
422 for h in chunks:
423 try: contenders.update(dict.fromkeys(h.files()))
423 try: contenders.update(dict.fromkeys(h.files()))
424 except AttributeError: pass
424 except AttributeError: pass
425
425
426 newfiles = [f for f in files if f in contenders]
426 newfiles = [f for f in match.files() if f in contenders]
427
427
428 if not newfiles:
428 if not newfiles:
429 ui.status(_('no changes to record\n'))
429 ui.status(_('no changes to record\n'))
430 return 0
430 return 0
431
431
432 if changes is None:
432 if changes is None:
433 changes = repo.status(files=newfiles, match=match)[:5]
433 match = cmdutil.matchfiles(repo, newfiles)
434 changes = repo.status(files=match.files(), match=match)[:5]
434 modified = dict.fromkeys(changes[0])
435 modified = dict.fromkeys(changes[0])
435
436
436 # 2. backup changed files, so we can restore them in the end
437 # 2. backup changed files, so we can restore them in the end
437 backups = {}
438 backups = {}
438 backupdir = repo.join('record-backups')
439 backupdir = repo.join('record-backups')
439 try:
440 try:
440 os.mkdir(backupdir)
441 os.mkdir(backupdir)
441 except OSError, err:
442 except OSError, err:
442 if err.errno != errno.EEXIST:
443 if err.errno != errno.EEXIST:
443 raise
444 raise
444 try:
445 try:
445 # backup continues
446 # backup continues
446 for f in newfiles:
447 for f in newfiles:
447 if f not in modified:
448 if f not in modified:
448 continue
449 continue
449 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
450 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
450 dir=backupdir)
451 dir=backupdir)
451 os.close(fd)
452 os.close(fd)
452 ui.debug('backup %r as %r\n' % (f, tmpname))
453 ui.debug('backup %r as %r\n' % (f, tmpname))
453 util.copyfile(repo.wjoin(f), tmpname)
454 util.copyfile(repo.wjoin(f), tmpname)
454 backups[f] = tmpname
455 backups[f] = tmpname
455
456
456 fp = cStringIO.StringIO()
457 fp = cStringIO.StringIO()
457 for c in chunks:
458 for c in chunks:
458 if c.filename() in backups:
459 if c.filename() in backups:
459 c.write(fp)
460 c.write(fp)
460 dopatch = fp.tell()
461 dopatch = fp.tell()
461 fp.seek(0)
462 fp.seek(0)
462
463
463 # 3a. apply filtered patch to clean repo (clean)
464 # 3a. apply filtered patch to clean repo (clean)
464 if backups:
465 if backups:
465 hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
466 hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
466
467
467 # 3b. (apply)
468 # 3b. (apply)
468 if dopatch:
469 if dopatch:
469 ui.debug('applying patch\n')
470 ui.debug('applying patch\n')
470 ui.debug(fp.getvalue())
471 ui.debug(fp.getvalue())
471 patch.internalpatch(fp, ui, 1, repo.root)
472 patch.internalpatch(fp, ui, 1, repo.root)
472 del fp
473 del fp
473
474
474 # 4. We prepared working directory according to filtered patch.
475 # 4. We prepared working directory according to filtered patch.
475 # Now is the time to delegate the job to commit/qrefresh or the like!
476 # Now is the time to delegate the job to commit/qrefresh or the like!
476
477
477 # it is important to first chdir to repo root -- we'll call a
478 # it is important to first chdir to repo root -- we'll call a
478 # highlevel command with list of pathnames relative to repo root
479 # highlevel command with list of pathnames relative to repo root
479 cwd = os.getcwd()
480 cwd = os.getcwd()
480 os.chdir(repo.root)
481 os.chdir(repo.root)
481 try:
482 try:
482 committer(ui, repo, newfiles, opts)
483 committer(ui, repo, newfiles, opts)
483 finally:
484 finally:
484 os.chdir(cwd)
485 os.chdir(cwd)
485
486
486 return 0
487 return 0
487 finally:
488 finally:
488 # 5. finally restore backed-up files
489 # 5. finally restore backed-up files
489 try:
490 try:
490 for realname, tmpname in backups.iteritems():
491 for realname, tmpname in backups.iteritems():
491 ui.debug('restoring %r to %r\n' % (tmpname, realname))
492 ui.debug('restoring %r to %r\n' % (tmpname, realname))
492 util.copyfile(tmpname, repo.wjoin(realname))
493 util.copyfile(tmpname, repo.wjoin(realname))
493 os.unlink(tmpname)
494 os.unlink(tmpname)
494 os.rmdir(backupdir)
495 os.rmdir(backupdir)
495 except OSError:
496 except OSError:
496 pass
497 pass
497 return cmdutil.commit(ui, repo, recordfunc, pats, opts)
498 return cmdutil.commit(ui, repo, recordfunc, pats, opts)
498
499
499 cmdtable = {
500 cmdtable = {
500 "record":
501 "record":
501 (record,
502 (record,
502
503
503 # add commit options
504 # add commit options
504 commands.table['^commit|ci'][1],
505 commands.table['^commit|ci'][1],
505
506
506 _('hg record [OPTION]... [FILE]...')),
507 _('hg record [OPTION]... [FILE]...')),
507 }
508 }
508
509
509
510
510 def extsetup():
511 def extsetup():
511 try:
512 try:
512 mq = extensions.find('mq')
513 mq = extensions.find('mq')
513 except KeyError:
514 except KeyError:
514 return
515 return
515
516
516 qcmdtable = {
517 qcmdtable = {
517 "qrecord":
518 "qrecord":
518 (qrecord,
519 (qrecord,
519
520
520 # add qnew options, except '--force'
521 # add qnew options, except '--force'
521 [opt for opt in mq.cmdtable['qnew'][1] if opt[1] != 'force'],
522 [opt for opt in mq.cmdtable['qnew'][1] if opt[1] != 'force'],
522
523
523 _('hg qrecord [OPTION]... PATCH [FILE]...')),
524 _('hg qrecord [OPTION]... PATCH [FILE]...')),
524 }
525 }
525
526
526 cmdtable.update(qcmdtable)
527 cmdtable.update(qcmdtable)
527
528
@@ -1,1188 +1,1188 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, bisect, stat
10 import os, sys, bisect, stat
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno
12 import match as _match
12 import match as _match
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 class UnknownCommand(Exception):
16 class UnknownCommand(Exception):
17 """Exception raised if command is not in the command table."""
17 """Exception raised if command is not in the command table."""
18 class AmbiguousCommand(Exception):
18 class AmbiguousCommand(Exception):
19 """Exception raised if command shortcut matches more than one command."""
19 """Exception raised if command shortcut matches more than one command."""
20
20
21 def findpossible(ui, cmd, table):
21 def findpossible(ui, cmd, table):
22 """
22 """
23 Return cmd -> (aliases, command table entry)
23 Return cmd -> (aliases, command table entry)
24 for each matching command.
24 for each matching command.
25 Return debug commands (or their aliases) only if no normal command matches.
25 Return debug commands (or their aliases) only if no normal command matches.
26 """
26 """
27 choice = {}
27 choice = {}
28 debugchoice = {}
28 debugchoice = {}
29 for e in table.keys():
29 for e in table.keys():
30 aliases = e.lstrip("^").split("|")
30 aliases = e.lstrip("^").split("|")
31 found = None
31 found = None
32 if cmd in aliases:
32 if cmd in aliases:
33 found = cmd
33 found = cmd
34 elif not ui.config("ui", "strict"):
34 elif not ui.config("ui", "strict"):
35 for a in aliases:
35 for a in aliases:
36 if a.startswith(cmd):
36 if a.startswith(cmd):
37 found = a
37 found = a
38 break
38 break
39 if found is not None:
39 if found is not None:
40 if aliases[0].startswith("debug") or found.startswith("debug"):
40 if aliases[0].startswith("debug") or found.startswith("debug"):
41 debugchoice[found] = (aliases, table[e])
41 debugchoice[found] = (aliases, table[e])
42 else:
42 else:
43 choice[found] = (aliases, table[e])
43 choice[found] = (aliases, table[e])
44
44
45 if not choice and debugchoice:
45 if not choice and debugchoice:
46 choice = debugchoice
46 choice = debugchoice
47
47
48 return choice
48 return choice
49
49
50 def findcmd(ui, cmd, table):
50 def findcmd(ui, cmd, table):
51 """Return (aliases, command table entry) for command string."""
51 """Return (aliases, command table entry) for command string."""
52 choice = findpossible(ui, cmd, table)
52 choice = findpossible(ui, cmd, table)
53
53
54 if cmd in choice:
54 if cmd in choice:
55 return choice[cmd]
55 return choice[cmd]
56
56
57 if len(choice) > 1:
57 if len(choice) > 1:
58 clist = choice.keys()
58 clist = choice.keys()
59 clist.sort()
59 clist.sort()
60 raise AmbiguousCommand(cmd, clist)
60 raise AmbiguousCommand(cmd, clist)
61
61
62 if choice:
62 if choice:
63 return choice.values()[0]
63 return choice.values()[0]
64
64
65 raise UnknownCommand(cmd)
65 raise UnknownCommand(cmd)
66
66
67 def bail_if_changed(repo):
67 def bail_if_changed(repo):
68 if repo.dirstate.parents()[1] != nullid:
68 if repo.dirstate.parents()[1] != nullid:
69 raise util.Abort(_('outstanding uncommitted merge'))
69 raise util.Abort(_('outstanding uncommitted merge'))
70 modified, added, removed, deleted = repo.status()[:4]
70 modified, added, removed, deleted = repo.status()[:4]
71 if modified or added or removed or deleted:
71 if modified or added or removed or deleted:
72 raise util.Abort(_("outstanding uncommitted changes"))
72 raise util.Abort(_("outstanding uncommitted changes"))
73
73
74 def logmessage(opts):
74 def logmessage(opts):
75 """ get the log message according to -m and -l option """
75 """ get the log message according to -m and -l option """
76 message = opts['message']
76 message = opts['message']
77 logfile = opts['logfile']
77 logfile = opts['logfile']
78
78
79 if message and logfile:
79 if message and logfile:
80 raise util.Abort(_('options --message and --logfile are mutually '
80 raise util.Abort(_('options --message and --logfile are mutually '
81 'exclusive'))
81 'exclusive'))
82 if not message and logfile:
82 if not message and logfile:
83 try:
83 try:
84 if logfile == '-':
84 if logfile == '-':
85 message = sys.stdin.read()
85 message = sys.stdin.read()
86 else:
86 else:
87 message = open(logfile).read()
87 message = open(logfile).read()
88 except IOError, inst:
88 except IOError, inst:
89 raise util.Abort(_("can't read commit message '%s': %s") %
89 raise util.Abort(_("can't read commit message '%s': %s") %
90 (logfile, inst.strerror))
90 (logfile, inst.strerror))
91 return message
91 return message
92
92
93 def loglimit(opts):
93 def loglimit(opts):
94 """get the log limit according to option -l/--limit"""
94 """get the log limit according to option -l/--limit"""
95 limit = opts.get('limit')
95 limit = opts.get('limit')
96 if limit:
96 if limit:
97 try:
97 try:
98 limit = int(limit)
98 limit = int(limit)
99 except ValueError:
99 except ValueError:
100 raise util.Abort(_('limit must be a positive integer'))
100 raise util.Abort(_('limit must be a positive integer'))
101 if limit <= 0: raise util.Abort(_('limit must be positive'))
101 if limit <= 0: raise util.Abort(_('limit must be positive'))
102 else:
102 else:
103 limit = sys.maxint
103 limit = sys.maxint
104 return limit
104 return limit
105
105
106 def setremoteconfig(ui, opts):
106 def setremoteconfig(ui, opts):
107 "copy remote options to ui tree"
107 "copy remote options to ui tree"
108 if opts.get('ssh'):
108 if opts.get('ssh'):
109 ui.setconfig("ui", "ssh", opts['ssh'])
109 ui.setconfig("ui", "ssh", opts['ssh'])
110 if opts.get('remotecmd'):
110 if opts.get('remotecmd'):
111 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
111 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
112
112
113 def revpair(repo, revs):
113 def revpair(repo, revs):
114 '''return pair of nodes, given list of revisions. second item can
114 '''return pair of nodes, given list of revisions. second item can
115 be None, meaning use working dir.'''
115 be None, meaning use working dir.'''
116
116
117 def revfix(repo, val, defval):
117 def revfix(repo, val, defval):
118 if not val and val != 0 and defval is not None:
118 if not val and val != 0 and defval is not None:
119 val = defval
119 val = defval
120 return repo.lookup(val)
120 return repo.lookup(val)
121
121
122 if not revs:
122 if not revs:
123 return repo.dirstate.parents()[0], None
123 return repo.dirstate.parents()[0], None
124 end = None
124 end = None
125 if len(revs) == 1:
125 if len(revs) == 1:
126 if revrangesep in revs[0]:
126 if revrangesep in revs[0]:
127 start, end = revs[0].split(revrangesep, 1)
127 start, end = revs[0].split(revrangesep, 1)
128 start = revfix(repo, start, 0)
128 start = revfix(repo, start, 0)
129 end = revfix(repo, end, repo.changelog.count() - 1)
129 end = revfix(repo, end, repo.changelog.count() - 1)
130 else:
130 else:
131 start = revfix(repo, revs[0], None)
131 start = revfix(repo, revs[0], None)
132 elif len(revs) == 2:
132 elif len(revs) == 2:
133 if revrangesep in revs[0] or revrangesep in revs[1]:
133 if revrangesep in revs[0] or revrangesep in revs[1]:
134 raise util.Abort(_('too many revisions specified'))
134 raise util.Abort(_('too many revisions specified'))
135 start = revfix(repo, revs[0], None)
135 start = revfix(repo, revs[0], None)
136 end = revfix(repo, revs[1], None)
136 end = revfix(repo, revs[1], None)
137 else:
137 else:
138 raise util.Abort(_('too many revisions specified'))
138 raise util.Abort(_('too many revisions specified'))
139 return start, end
139 return start, end
140
140
141 def revrange(repo, revs):
141 def revrange(repo, revs):
142 """Yield revision as strings from a list of revision specifications."""
142 """Yield revision as strings from a list of revision specifications."""
143
143
144 def revfix(repo, val, defval):
144 def revfix(repo, val, defval):
145 if not val and val != 0 and defval is not None:
145 if not val and val != 0 and defval is not None:
146 return defval
146 return defval
147 return repo.changelog.rev(repo.lookup(val))
147 return repo.changelog.rev(repo.lookup(val))
148
148
149 seen, l = {}, []
149 seen, l = {}, []
150 for spec in revs:
150 for spec in revs:
151 if revrangesep in spec:
151 if revrangesep in spec:
152 start, end = spec.split(revrangesep, 1)
152 start, end = spec.split(revrangesep, 1)
153 start = revfix(repo, start, 0)
153 start = revfix(repo, start, 0)
154 end = revfix(repo, end, repo.changelog.count() - 1)
154 end = revfix(repo, end, repo.changelog.count() - 1)
155 step = start > end and -1 or 1
155 step = start > end and -1 or 1
156 for rev in xrange(start, end+step, step):
156 for rev in xrange(start, end+step, step):
157 if rev in seen:
157 if rev in seen:
158 continue
158 continue
159 seen[rev] = 1
159 seen[rev] = 1
160 l.append(rev)
160 l.append(rev)
161 else:
161 else:
162 rev = revfix(repo, spec, None)
162 rev = revfix(repo, spec, None)
163 if rev in seen:
163 if rev in seen:
164 continue
164 continue
165 seen[rev] = 1
165 seen[rev] = 1
166 l.append(rev)
166 l.append(rev)
167
167
168 return l
168 return l
169
169
170 def make_filename(repo, pat, node,
170 def make_filename(repo, pat, node,
171 total=None, seqno=None, revwidth=None, pathname=None):
171 total=None, seqno=None, revwidth=None, pathname=None):
172 node_expander = {
172 node_expander = {
173 'H': lambda: hex(node),
173 'H': lambda: hex(node),
174 'R': lambda: str(repo.changelog.rev(node)),
174 'R': lambda: str(repo.changelog.rev(node)),
175 'h': lambda: short(node),
175 'h': lambda: short(node),
176 }
176 }
177 expander = {
177 expander = {
178 '%': lambda: '%',
178 '%': lambda: '%',
179 'b': lambda: os.path.basename(repo.root),
179 'b': lambda: os.path.basename(repo.root),
180 }
180 }
181
181
182 try:
182 try:
183 if node:
183 if node:
184 expander.update(node_expander)
184 expander.update(node_expander)
185 if node:
185 if node:
186 expander['r'] = (lambda:
186 expander['r'] = (lambda:
187 str(repo.changelog.rev(node)).zfill(revwidth or 0))
187 str(repo.changelog.rev(node)).zfill(revwidth or 0))
188 if total is not None:
188 if total is not None:
189 expander['N'] = lambda: str(total)
189 expander['N'] = lambda: str(total)
190 if seqno is not None:
190 if seqno is not None:
191 expander['n'] = lambda: str(seqno)
191 expander['n'] = lambda: str(seqno)
192 if total is not None and seqno is not None:
192 if total is not None and seqno is not None:
193 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
193 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
194 if pathname is not None:
194 if pathname is not None:
195 expander['s'] = lambda: os.path.basename(pathname)
195 expander['s'] = lambda: os.path.basename(pathname)
196 expander['d'] = lambda: os.path.dirname(pathname) or '.'
196 expander['d'] = lambda: os.path.dirname(pathname) or '.'
197 expander['p'] = lambda: pathname
197 expander['p'] = lambda: pathname
198
198
199 newname = []
199 newname = []
200 patlen = len(pat)
200 patlen = len(pat)
201 i = 0
201 i = 0
202 while i < patlen:
202 while i < patlen:
203 c = pat[i]
203 c = pat[i]
204 if c == '%':
204 if c == '%':
205 i += 1
205 i += 1
206 c = pat[i]
206 c = pat[i]
207 c = expander[c]()
207 c = expander[c]()
208 newname.append(c)
208 newname.append(c)
209 i += 1
209 i += 1
210 return ''.join(newname)
210 return ''.join(newname)
211 except KeyError, inst:
211 except KeyError, inst:
212 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
212 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
213 inst.args[0])
213 inst.args[0])
214
214
215 def make_file(repo, pat, node=None,
215 def make_file(repo, pat, node=None,
216 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
216 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
217 if not pat or pat == '-':
217 if not pat or pat == '-':
218 return 'w' in mode and sys.stdout or sys.stdin
218 return 'w' in mode and sys.stdout or sys.stdin
219 if hasattr(pat, 'write') and 'w' in mode:
219 if hasattr(pat, 'write') and 'w' in mode:
220 return pat
220 return pat
221 if hasattr(pat, 'read') and 'r' in mode:
221 if hasattr(pat, 'read') and 'r' in mode:
222 return pat
222 return pat
223 return open(make_filename(repo, pat, node, total, seqno, revwidth,
223 return open(make_filename(repo, pat, node, total, seqno, revwidth,
224 pathname),
224 pathname),
225 mode)
225 mode)
226
226
227 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
227 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
228 if not globbed and default == 'relpath':
228 if not globbed and default == 'relpath':
229 pats = util.expand_glob(pats or [])
229 pats = util.expand_glob(pats or [])
230 m = _match.match(repo.root, repo.getcwd(), pats,
230 m = _match.match(repo.root, repo.getcwd(), pats,
231 opts.get('include'), opts.get('exclude'), default)
231 opts.get('include'), opts.get('exclude'), default)
232 def badfn(f, msg):
232 def badfn(f, msg):
233 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
233 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
234 return False
234 return False
235 m.bad = badfn
235 m.bad = badfn
236 return m
236 return m
237
237
238 def matchall(repo):
238 def matchall(repo):
239 return _match.always(repo.root, repo.getcwd())
239 return _match.always(repo.root, repo.getcwd())
240
240
241 def matchfiles(repo, files):
241 def matchfiles(repo, files):
242 return _match.exact(repo.root, repo.getcwd(), files)
242 return _match.exact(repo.root, repo.getcwd(), files)
243
243
244 def findrenames(repo, added=None, removed=None, threshold=0.5):
244 def findrenames(repo, added=None, removed=None, threshold=0.5):
245 '''find renamed files -- yields (before, after, score) tuples'''
245 '''find renamed files -- yields (before, after, score) tuples'''
246 if added is None or removed is None:
246 if added is None or removed is None:
247 added, removed = repo.status()[1:3]
247 added, removed = repo.status()[1:3]
248 ctx = repo.changectx()
248 ctx = repo.changectx()
249 for a in added:
249 for a in added:
250 aa = repo.wread(a)
250 aa = repo.wread(a)
251 bestname, bestscore = None, threshold
251 bestname, bestscore = None, threshold
252 for r in removed:
252 for r in removed:
253 rr = ctx.filectx(r).data()
253 rr = ctx.filectx(r).data()
254
254
255 # bdiff.blocks() returns blocks of matching lines
255 # bdiff.blocks() returns blocks of matching lines
256 # count the number of bytes in each
256 # count the number of bytes in each
257 equal = 0
257 equal = 0
258 alines = mdiff.splitnewlines(aa)
258 alines = mdiff.splitnewlines(aa)
259 matches = bdiff.blocks(aa, rr)
259 matches = bdiff.blocks(aa, rr)
260 for x1,x2,y1,y2 in matches:
260 for x1,x2,y1,y2 in matches:
261 for line in alines[x1:x2]:
261 for line in alines[x1:x2]:
262 equal += len(line)
262 equal += len(line)
263
263
264 lengths = len(aa) + len(rr)
264 lengths = len(aa) + len(rr)
265 if lengths:
265 if lengths:
266 myscore = equal*2.0 / lengths
266 myscore = equal*2.0 / lengths
267 if myscore >= bestscore:
267 if myscore >= bestscore:
268 bestname, bestscore = r, myscore
268 bestname, bestscore = r, myscore
269 if bestname:
269 if bestname:
270 yield bestname, a, bestscore
270 yield bestname, a, bestscore
271
271
272 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
272 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
273 if dry_run is None:
273 if dry_run is None:
274 dry_run = opts.get('dry_run')
274 dry_run = opts.get('dry_run')
275 if similarity is None:
275 if similarity is None:
276 similarity = float(opts.get('similarity') or 0)
276 similarity = float(opts.get('similarity') or 0)
277 add, remove = [], []
277 add, remove = [], []
278 mapping = {}
278 mapping = {}
279 m = match(repo, pats, opts)
279 m = match(repo, pats, opts)
280 for abs in repo.walk(m):
280 for abs in repo.walk(m):
281 target = repo.wjoin(abs)
281 target = repo.wjoin(abs)
282 rel = m.rel(abs)
282 rel = m.rel(abs)
283 exact = m.exact(abs)
283 exact = m.exact(abs)
284 if abs not in repo.dirstate:
284 if abs not in repo.dirstate:
285 add.append(abs)
285 add.append(abs)
286 mapping[abs] = rel, m.exact(abs)
286 mapping[abs] = rel, m.exact(abs)
287 if repo.ui.verbose or not exact:
287 if repo.ui.verbose or not exact:
288 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
288 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
289 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
289 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
290 or (os.path.isdir(target) and not os.path.islink(target))):
290 or (os.path.isdir(target) and not os.path.islink(target))):
291 remove.append(abs)
291 remove.append(abs)
292 mapping[abs] = rel, exact
292 mapping[abs] = rel, exact
293 if repo.ui.verbose or not exact:
293 if repo.ui.verbose or not exact:
294 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
294 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
295 if not dry_run:
295 if not dry_run:
296 repo.remove(remove)
296 repo.remove(remove)
297 repo.add(add)
297 repo.add(add)
298 if similarity > 0:
298 if similarity > 0:
299 for old, new, score in findrenames(repo, add, remove, similarity):
299 for old, new, score in findrenames(repo, add, remove, similarity):
300 oldrel, oldexact = mapping[old]
300 oldrel, oldexact = mapping[old]
301 newrel, newexact = mapping[new]
301 newrel, newexact = mapping[new]
302 if repo.ui.verbose or not oldexact or not newexact:
302 if repo.ui.verbose or not oldexact or not newexact:
303 repo.ui.status(_('recording removal of %s as rename to %s '
303 repo.ui.status(_('recording removal of %s as rename to %s '
304 '(%d%% similar)\n') %
304 '(%d%% similar)\n') %
305 (oldrel, newrel, score * 100))
305 (oldrel, newrel, score * 100))
306 if not dry_run:
306 if not dry_run:
307 repo.copy(old, new)
307 repo.copy(old, new)
308
308
309 def copy(ui, repo, pats, opts, rename=False):
309 def copy(ui, repo, pats, opts, rename=False):
310 # called with the repo lock held
310 # called with the repo lock held
311 #
311 #
312 # hgsep => pathname that uses "/" to separate directories
312 # hgsep => pathname that uses "/" to separate directories
313 # ossep => pathname that uses os.sep to separate directories
313 # ossep => pathname that uses os.sep to separate directories
314 cwd = repo.getcwd()
314 cwd = repo.getcwd()
315 targets = {}
315 targets = {}
316 after = opts.get("after")
316 after = opts.get("after")
317 dryrun = opts.get("dry_run")
317 dryrun = opts.get("dry_run")
318
318
319 def walkpat(pat):
319 def walkpat(pat):
320 srcs = []
320 srcs = []
321 m = match(repo, [pat], opts, globbed=True)
321 m = match(repo, [pat], opts, globbed=True)
322 for abs in repo.walk(m):
322 for abs in repo.walk(m):
323 state = repo.dirstate[abs]
323 state = repo.dirstate[abs]
324 rel = m.rel(abs)
324 rel = m.rel(abs)
325 exact = m.exact(abs)
325 exact = m.exact(abs)
326 if state in '?r':
326 if state in '?r':
327 if exact and state == '?':
327 if exact and state == '?':
328 ui.warn(_('%s: not copying - file is not managed\n') % rel)
328 ui.warn(_('%s: not copying - file is not managed\n') % rel)
329 if exact and state == 'r':
329 if exact and state == 'r':
330 ui.warn(_('%s: not copying - file has been marked for'
330 ui.warn(_('%s: not copying - file has been marked for'
331 ' remove\n') % rel)
331 ' remove\n') % rel)
332 continue
332 continue
333 # abs: hgsep
333 # abs: hgsep
334 # rel: ossep
334 # rel: ossep
335 srcs.append((abs, rel, exact))
335 srcs.append((abs, rel, exact))
336 return srcs
336 return srcs
337
337
338 # abssrc: hgsep
338 # abssrc: hgsep
339 # relsrc: ossep
339 # relsrc: ossep
340 # otarget: ossep
340 # otarget: ossep
341 def copyfile(abssrc, relsrc, otarget, exact):
341 def copyfile(abssrc, relsrc, otarget, exact):
342 abstarget = util.canonpath(repo.root, cwd, otarget)
342 abstarget = util.canonpath(repo.root, cwd, otarget)
343 reltarget = repo.pathto(abstarget, cwd)
343 reltarget = repo.pathto(abstarget, cwd)
344 target = repo.wjoin(abstarget)
344 target = repo.wjoin(abstarget)
345 src = repo.wjoin(abssrc)
345 src = repo.wjoin(abssrc)
346 state = repo.dirstate[abstarget]
346 state = repo.dirstate[abstarget]
347
347
348 # check for collisions
348 # check for collisions
349 prevsrc = targets.get(abstarget)
349 prevsrc = targets.get(abstarget)
350 if prevsrc is not None:
350 if prevsrc is not None:
351 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
351 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
352 (reltarget, repo.pathto(abssrc, cwd),
352 (reltarget, repo.pathto(abssrc, cwd),
353 repo.pathto(prevsrc, cwd)))
353 repo.pathto(prevsrc, cwd)))
354 return
354 return
355
355
356 # check for overwrites
356 # check for overwrites
357 exists = os.path.exists(target)
357 exists = os.path.exists(target)
358 if (not after and exists or after and state in 'mn'):
358 if (not after and exists or after and state in 'mn'):
359 if not opts['force']:
359 if not opts['force']:
360 ui.warn(_('%s: not overwriting - file exists\n') %
360 ui.warn(_('%s: not overwriting - file exists\n') %
361 reltarget)
361 reltarget)
362 return
362 return
363
363
364 if after:
364 if after:
365 if not exists:
365 if not exists:
366 return
366 return
367 elif not dryrun:
367 elif not dryrun:
368 try:
368 try:
369 if exists:
369 if exists:
370 os.unlink(target)
370 os.unlink(target)
371 targetdir = os.path.dirname(target) or '.'
371 targetdir = os.path.dirname(target) or '.'
372 if not os.path.isdir(targetdir):
372 if not os.path.isdir(targetdir):
373 os.makedirs(targetdir)
373 os.makedirs(targetdir)
374 util.copyfile(src, target)
374 util.copyfile(src, target)
375 except IOError, inst:
375 except IOError, inst:
376 if inst.errno == errno.ENOENT:
376 if inst.errno == errno.ENOENT:
377 ui.warn(_('%s: deleted in working copy\n') % relsrc)
377 ui.warn(_('%s: deleted in working copy\n') % relsrc)
378 else:
378 else:
379 ui.warn(_('%s: cannot copy - %s\n') %
379 ui.warn(_('%s: cannot copy - %s\n') %
380 (relsrc, inst.strerror))
380 (relsrc, inst.strerror))
381 return True # report a failure
381 return True # report a failure
382
382
383 if ui.verbose or not exact:
383 if ui.verbose or not exact:
384 action = rename and "moving" or "copying"
384 action = rename and "moving" or "copying"
385 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
385 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
386
386
387 targets[abstarget] = abssrc
387 targets[abstarget] = abssrc
388
388
389 # fix up dirstate
389 # fix up dirstate
390 origsrc = repo.dirstate.copied(abssrc) or abssrc
390 origsrc = repo.dirstate.copied(abssrc) or abssrc
391 if abstarget == origsrc: # copying back a copy?
391 if abstarget == origsrc: # copying back a copy?
392 if state not in 'mn' and not dryrun:
392 if state not in 'mn' and not dryrun:
393 repo.dirstate.normallookup(abstarget)
393 repo.dirstate.normallookup(abstarget)
394 else:
394 else:
395 if repo.dirstate[origsrc] == 'a':
395 if repo.dirstate[origsrc] == 'a':
396 if not ui.quiet:
396 if not ui.quiet:
397 ui.warn(_("%s has not been committed yet, so no copy "
397 ui.warn(_("%s has not been committed yet, so no copy "
398 "data will be stored for %s.\n")
398 "data will be stored for %s.\n")
399 % (repo.pathto(origsrc, cwd), reltarget))
399 % (repo.pathto(origsrc, cwd), reltarget))
400 if abstarget not in repo.dirstate and not dryrun:
400 if abstarget not in repo.dirstate and not dryrun:
401 repo.add([abstarget])
401 repo.add([abstarget])
402 elif not dryrun:
402 elif not dryrun:
403 repo.copy(origsrc, abstarget)
403 repo.copy(origsrc, abstarget)
404
404
405 if rename and not dryrun:
405 if rename and not dryrun:
406 repo.remove([abssrc], not after)
406 repo.remove([abssrc], not after)
407
407
408 # pat: ossep
408 # pat: ossep
409 # dest ossep
409 # dest ossep
410 # srcs: list of (hgsep, hgsep, ossep, bool)
410 # srcs: list of (hgsep, hgsep, ossep, bool)
411 # return: function that takes hgsep and returns ossep
411 # return: function that takes hgsep and returns ossep
412 def targetpathfn(pat, dest, srcs):
412 def targetpathfn(pat, dest, srcs):
413 if os.path.isdir(pat):
413 if os.path.isdir(pat):
414 abspfx = util.canonpath(repo.root, cwd, pat)
414 abspfx = util.canonpath(repo.root, cwd, pat)
415 abspfx = util.localpath(abspfx)
415 abspfx = util.localpath(abspfx)
416 if destdirexists:
416 if destdirexists:
417 striplen = len(os.path.split(abspfx)[0])
417 striplen = len(os.path.split(abspfx)[0])
418 else:
418 else:
419 striplen = len(abspfx)
419 striplen = len(abspfx)
420 if striplen:
420 if striplen:
421 striplen += len(os.sep)
421 striplen += len(os.sep)
422 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
422 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
423 elif destdirexists:
423 elif destdirexists:
424 res = lambda p: os.path.join(dest,
424 res = lambda p: os.path.join(dest,
425 os.path.basename(util.localpath(p)))
425 os.path.basename(util.localpath(p)))
426 else:
426 else:
427 res = lambda p: dest
427 res = lambda p: dest
428 return res
428 return res
429
429
430 # pat: ossep
430 # pat: ossep
431 # dest ossep
431 # dest ossep
432 # srcs: list of (hgsep, hgsep, ossep, bool)
432 # srcs: list of (hgsep, hgsep, ossep, bool)
433 # return: function that takes hgsep and returns ossep
433 # return: function that takes hgsep and returns ossep
434 def targetpathafterfn(pat, dest, srcs):
434 def targetpathafterfn(pat, dest, srcs):
435 if util.patkind(pat, None)[0]:
435 if util.patkind(pat, None)[0]:
436 # a mercurial pattern
436 # a mercurial pattern
437 res = lambda p: os.path.join(dest,
437 res = lambda p: os.path.join(dest,
438 os.path.basename(util.localpath(p)))
438 os.path.basename(util.localpath(p)))
439 else:
439 else:
440 abspfx = util.canonpath(repo.root, cwd, pat)
440 abspfx = util.canonpath(repo.root, cwd, pat)
441 if len(abspfx) < len(srcs[0][0]):
441 if len(abspfx) < len(srcs[0][0]):
442 # A directory. Either the target path contains the last
442 # A directory. Either the target path contains the last
443 # component of the source path or it does not.
443 # component of the source path or it does not.
444 def evalpath(striplen):
444 def evalpath(striplen):
445 score = 0
445 score = 0
446 for s in srcs:
446 for s in srcs:
447 t = os.path.join(dest, util.localpath(s[0])[striplen:])
447 t = os.path.join(dest, util.localpath(s[0])[striplen:])
448 if os.path.exists(t):
448 if os.path.exists(t):
449 score += 1
449 score += 1
450 return score
450 return score
451
451
452 abspfx = util.localpath(abspfx)
452 abspfx = util.localpath(abspfx)
453 striplen = len(abspfx)
453 striplen = len(abspfx)
454 if striplen:
454 if striplen:
455 striplen += len(os.sep)
455 striplen += len(os.sep)
456 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
456 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
457 score = evalpath(striplen)
457 score = evalpath(striplen)
458 striplen1 = len(os.path.split(abspfx)[0])
458 striplen1 = len(os.path.split(abspfx)[0])
459 if striplen1:
459 if striplen1:
460 striplen1 += len(os.sep)
460 striplen1 += len(os.sep)
461 if evalpath(striplen1) > score:
461 if evalpath(striplen1) > score:
462 striplen = striplen1
462 striplen = striplen1
463 res = lambda p: os.path.join(dest,
463 res = lambda p: os.path.join(dest,
464 util.localpath(p)[striplen:])
464 util.localpath(p)[striplen:])
465 else:
465 else:
466 # a file
466 # a file
467 if destdirexists:
467 if destdirexists:
468 res = lambda p: os.path.join(dest,
468 res = lambda p: os.path.join(dest,
469 os.path.basename(util.localpath(p)))
469 os.path.basename(util.localpath(p)))
470 else:
470 else:
471 res = lambda p: dest
471 res = lambda p: dest
472 return res
472 return res
473
473
474
474
475 pats = util.expand_glob(pats)
475 pats = util.expand_glob(pats)
476 if not pats:
476 if not pats:
477 raise util.Abort(_('no source or destination specified'))
477 raise util.Abort(_('no source or destination specified'))
478 if len(pats) == 1:
478 if len(pats) == 1:
479 raise util.Abort(_('no destination specified'))
479 raise util.Abort(_('no destination specified'))
480 dest = pats.pop()
480 dest = pats.pop()
481 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
481 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
482 if not destdirexists:
482 if not destdirexists:
483 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
483 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
484 raise util.Abort(_('with multiple sources, destination must be an '
484 raise util.Abort(_('with multiple sources, destination must be an '
485 'existing directory'))
485 'existing directory'))
486 if util.endswithsep(dest):
486 if util.endswithsep(dest):
487 raise util.Abort(_('destination %s is not a directory') % dest)
487 raise util.Abort(_('destination %s is not a directory') % dest)
488
488
489 tfn = targetpathfn
489 tfn = targetpathfn
490 if after:
490 if after:
491 tfn = targetpathafterfn
491 tfn = targetpathafterfn
492 copylist = []
492 copylist = []
493 for pat in pats:
493 for pat in pats:
494 srcs = walkpat(pat)
494 srcs = walkpat(pat)
495 if not srcs:
495 if not srcs:
496 continue
496 continue
497 copylist.append((tfn(pat, dest, srcs), srcs))
497 copylist.append((tfn(pat, dest, srcs), srcs))
498 if not copylist:
498 if not copylist:
499 raise util.Abort(_('no files to copy'))
499 raise util.Abort(_('no files to copy'))
500
500
501 errors = 0
501 errors = 0
502 for targetpath, srcs in copylist:
502 for targetpath, srcs in copylist:
503 for abssrc, relsrc, exact in srcs:
503 for abssrc, relsrc, exact in srcs:
504 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
504 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
505 errors += 1
505 errors += 1
506
506
507 if errors:
507 if errors:
508 ui.warn(_('(consider using --after)\n'))
508 ui.warn(_('(consider using --after)\n'))
509
509
510 return errors
510 return errors
511
511
512 def service(opts, parentfn=None, initfn=None, runfn=None):
512 def service(opts, parentfn=None, initfn=None, runfn=None):
513 '''Run a command as a service.'''
513 '''Run a command as a service.'''
514
514
515 if opts['daemon'] and not opts['daemon_pipefds']:
515 if opts['daemon'] and not opts['daemon_pipefds']:
516 rfd, wfd = os.pipe()
516 rfd, wfd = os.pipe()
517 args = sys.argv[:]
517 args = sys.argv[:]
518 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
518 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
519 # Don't pass --cwd to the child process, because we've already
519 # Don't pass --cwd to the child process, because we've already
520 # changed directory.
520 # changed directory.
521 for i in xrange(1,len(args)):
521 for i in xrange(1,len(args)):
522 if args[i].startswith('--cwd='):
522 if args[i].startswith('--cwd='):
523 del args[i]
523 del args[i]
524 break
524 break
525 elif args[i].startswith('--cwd'):
525 elif args[i].startswith('--cwd'):
526 del args[i:i+2]
526 del args[i:i+2]
527 break
527 break
528 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
528 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
529 args[0], args)
529 args[0], args)
530 os.close(wfd)
530 os.close(wfd)
531 os.read(rfd, 1)
531 os.read(rfd, 1)
532 if parentfn:
532 if parentfn:
533 return parentfn(pid)
533 return parentfn(pid)
534 else:
534 else:
535 os._exit(0)
535 os._exit(0)
536
536
537 if initfn:
537 if initfn:
538 initfn()
538 initfn()
539
539
540 if opts['pid_file']:
540 if opts['pid_file']:
541 fp = open(opts['pid_file'], 'w')
541 fp = open(opts['pid_file'], 'w')
542 fp.write(str(os.getpid()) + '\n')
542 fp.write(str(os.getpid()) + '\n')
543 fp.close()
543 fp.close()
544
544
545 if opts['daemon_pipefds']:
545 if opts['daemon_pipefds']:
546 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
546 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
547 os.close(rfd)
547 os.close(rfd)
548 try:
548 try:
549 os.setsid()
549 os.setsid()
550 except AttributeError:
550 except AttributeError:
551 pass
551 pass
552 os.write(wfd, 'y')
552 os.write(wfd, 'y')
553 os.close(wfd)
553 os.close(wfd)
554 sys.stdout.flush()
554 sys.stdout.flush()
555 sys.stderr.flush()
555 sys.stderr.flush()
556 fd = os.open(util.nulldev, os.O_RDWR)
556 fd = os.open(util.nulldev, os.O_RDWR)
557 if fd != 0: os.dup2(fd, 0)
557 if fd != 0: os.dup2(fd, 0)
558 if fd != 1: os.dup2(fd, 1)
558 if fd != 1: os.dup2(fd, 1)
559 if fd != 2: os.dup2(fd, 2)
559 if fd != 2: os.dup2(fd, 2)
560 if fd not in (0, 1, 2): os.close(fd)
560 if fd not in (0, 1, 2): os.close(fd)
561
561
562 if runfn:
562 if runfn:
563 return runfn()
563 return runfn()
564
564
565 class changeset_printer(object):
565 class changeset_printer(object):
566 '''show changeset information when templating not requested.'''
566 '''show changeset information when templating not requested.'''
567
567
568 def __init__(self, ui, repo, patch, buffered):
568 def __init__(self, ui, repo, patch, buffered):
569 self.ui = ui
569 self.ui = ui
570 self.repo = repo
570 self.repo = repo
571 self.buffered = buffered
571 self.buffered = buffered
572 self.patch = patch
572 self.patch = patch
573 self.header = {}
573 self.header = {}
574 self.hunk = {}
574 self.hunk = {}
575 self.lastheader = None
575 self.lastheader = None
576
576
577 def flush(self, rev):
577 def flush(self, rev):
578 if rev in self.header:
578 if rev in self.header:
579 h = self.header[rev]
579 h = self.header[rev]
580 if h != self.lastheader:
580 if h != self.lastheader:
581 self.lastheader = h
581 self.lastheader = h
582 self.ui.write(h)
582 self.ui.write(h)
583 del self.header[rev]
583 del self.header[rev]
584 if rev in self.hunk:
584 if rev in self.hunk:
585 self.ui.write(self.hunk[rev])
585 self.ui.write(self.hunk[rev])
586 del self.hunk[rev]
586 del self.hunk[rev]
587 return 1
587 return 1
588 return 0
588 return 0
589
589
590 def show(self, rev=0, changenode=None, copies=(), **props):
590 def show(self, rev=0, changenode=None, copies=(), **props):
591 if self.buffered:
591 if self.buffered:
592 self.ui.pushbuffer()
592 self.ui.pushbuffer()
593 self._show(rev, changenode, copies, props)
593 self._show(rev, changenode, copies, props)
594 self.hunk[rev] = self.ui.popbuffer()
594 self.hunk[rev] = self.ui.popbuffer()
595 else:
595 else:
596 self._show(rev, changenode, copies, props)
596 self._show(rev, changenode, copies, props)
597
597
598 def _show(self, rev, changenode, copies, props):
598 def _show(self, rev, changenode, copies, props):
599 '''show a single changeset or file revision'''
599 '''show a single changeset or file revision'''
600 log = self.repo.changelog
600 log = self.repo.changelog
601 if changenode is None:
601 if changenode is None:
602 changenode = log.node(rev)
602 changenode = log.node(rev)
603 elif not rev:
603 elif not rev:
604 rev = log.rev(changenode)
604 rev = log.rev(changenode)
605
605
606 if self.ui.quiet:
606 if self.ui.quiet:
607 self.ui.write("%d:%s\n" % (rev, short(changenode)))
607 self.ui.write("%d:%s\n" % (rev, short(changenode)))
608 return
608 return
609
609
610 changes = log.read(changenode)
610 changes = log.read(changenode)
611 date = util.datestr(changes[2])
611 date = util.datestr(changes[2])
612 extra = changes[5]
612 extra = changes[5]
613 branch = extra.get("branch")
613 branch = extra.get("branch")
614
614
615 hexfunc = self.ui.debugflag and hex or short
615 hexfunc = self.ui.debugflag and hex or short
616
616
617 parents = [(p, hexfunc(log.node(p)))
617 parents = [(p, hexfunc(log.node(p)))
618 for p in self._meaningful_parentrevs(log, rev)]
618 for p in self._meaningful_parentrevs(log, rev)]
619
619
620 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
620 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
621
621
622 # don't show the default branch name
622 # don't show the default branch name
623 if branch != 'default':
623 if branch != 'default':
624 branch = util.tolocal(branch)
624 branch = util.tolocal(branch)
625 self.ui.write(_("branch: %s\n") % branch)
625 self.ui.write(_("branch: %s\n") % branch)
626 for tag in self.repo.nodetags(changenode):
626 for tag in self.repo.nodetags(changenode):
627 self.ui.write(_("tag: %s\n") % tag)
627 self.ui.write(_("tag: %s\n") % tag)
628 for parent in parents:
628 for parent in parents:
629 self.ui.write(_("parent: %d:%s\n") % parent)
629 self.ui.write(_("parent: %d:%s\n") % parent)
630
630
631 if self.ui.debugflag:
631 if self.ui.debugflag:
632 self.ui.write(_("manifest: %d:%s\n") %
632 self.ui.write(_("manifest: %d:%s\n") %
633 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
633 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
634 self.ui.write(_("user: %s\n") % changes[1])
634 self.ui.write(_("user: %s\n") % changes[1])
635 self.ui.write(_("date: %s\n") % date)
635 self.ui.write(_("date: %s\n") % date)
636
636
637 if self.ui.debugflag:
637 if self.ui.debugflag:
638 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
638 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
639 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
639 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
640 files):
640 files):
641 if value:
641 if value:
642 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
642 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
643 elif changes[3] and self.ui.verbose:
643 elif changes[3] and self.ui.verbose:
644 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
644 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
645 if copies and self.ui.verbose:
645 if copies and self.ui.verbose:
646 copies = ['%s (%s)' % c for c in copies]
646 copies = ['%s (%s)' % c for c in copies]
647 self.ui.write(_("copies: %s\n") % ' '.join(copies))
647 self.ui.write(_("copies: %s\n") % ' '.join(copies))
648
648
649 if extra and self.ui.debugflag:
649 if extra and self.ui.debugflag:
650 extraitems = extra.items()
650 extraitems = extra.items()
651 extraitems.sort()
651 extraitems.sort()
652 for key, value in extraitems:
652 for key, value in extraitems:
653 self.ui.write(_("extra: %s=%s\n")
653 self.ui.write(_("extra: %s=%s\n")
654 % (key, value.encode('string_escape')))
654 % (key, value.encode('string_escape')))
655
655
656 description = changes[4].strip()
656 description = changes[4].strip()
657 if description:
657 if description:
658 if self.ui.verbose:
658 if self.ui.verbose:
659 self.ui.write(_("description:\n"))
659 self.ui.write(_("description:\n"))
660 self.ui.write(description)
660 self.ui.write(description)
661 self.ui.write("\n\n")
661 self.ui.write("\n\n")
662 else:
662 else:
663 self.ui.write(_("summary: %s\n") %
663 self.ui.write(_("summary: %s\n") %
664 description.splitlines()[0])
664 description.splitlines()[0])
665 self.ui.write("\n")
665 self.ui.write("\n")
666
666
667 self.showpatch(changenode)
667 self.showpatch(changenode)
668
668
669 def showpatch(self, node):
669 def showpatch(self, node):
670 if self.patch:
670 if self.patch:
671 prev = self.repo.changelog.parents(node)[0]
671 prev = self.repo.changelog.parents(node)[0]
672 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
672 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
673 opts=patch.diffopts(self.ui))
673 opts=patch.diffopts(self.ui))
674 self.ui.write("\n")
674 self.ui.write("\n")
675
675
676 def _meaningful_parentrevs(self, log, rev):
676 def _meaningful_parentrevs(self, log, rev):
677 """Return list of meaningful (or all if debug) parentrevs for rev.
677 """Return list of meaningful (or all if debug) parentrevs for rev.
678
678
679 For merges (two non-nullrev revisions) both parents are meaningful.
679 For merges (two non-nullrev revisions) both parents are meaningful.
680 Otherwise the first parent revision is considered meaningful if it
680 Otherwise the first parent revision is considered meaningful if it
681 is not the preceding revision.
681 is not the preceding revision.
682 """
682 """
683 parents = log.parentrevs(rev)
683 parents = log.parentrevs(rev)
684 if not self.ui.debugflag and parents[1] == nullrev:
684 if not self.ui.debugflag and parents[1] == nullrev:
685 if parents[0] >= rev - 1:
685 if parents[0] >= rev - 1:
686 parents = []
686 parents = []
687 else:
687 else:
688 parents = [parents[0]]
688 parents = [parents[0]]
689 return parents
689 return parents
690
690
691
691
692 class changeset_templater(changeset_printer):
692 class changeset_templater(changeset_printer):
693 '''format changeset information.'''
693 '''format changeset information.'''
694
694
695 def __init__(self, ui, repo, patch, mapfile, buffered):
695 def __init__(self, ui, repo, patch, mapfile, buffered):
696 changeset_printer.__init__(self, ui, repo, patch, buffered)
696 changeset_printer.__init__(self, ui, repo, patch, buffered)
697 filters = templatefilters.filters.copy()
697 filters = templatefilters.filters.copy()
698 filters['formatnode'] = (ui.debugflag and (lambda x: x)
698 filters['formatnode'] = (ui.debugflag and (lambda x: x)
699 or (lambda x: x[:12]))
699 or (lambda x: x[:12]))
700 self.t = templater.templater(mapfile, filters,
700 self.t = templater.templater(mapfile, filters,
701 cache={
701 cache={
702 'parent': '{rev}:{node|formatnode} ',
702 'parent': '{rev}:{node|formatnode} ',
703 'manifest': '{rev}:{node|formatnode}',
703 'manifest': '{rev}:{node|formatnode}',
704 'filecopy': '{name} ({source})'})
704 'filecopy': '{name} ({source})'})
705
705
706 def use_template(self, t):
706 def use_template(self, t):
707 '''set template string to use'''
707 '''set template string to use'''
708 self.t.cache['changeset'] = t
708 self.t.cache['changeset'] = t
709
709
710 def _show(self, rev, changenode, copies, props):
710 def _show(self, rev, changenode, copies, props):
711 '''show a single changeset or file revision'''
711 '''show a single changeset or file revision'''
712 log = self.repo.changelog
712 log = self.repo.changelog
713 if changenode is None:
713 if changenode is None:
714 changenode = log.node(rev)
714 changenode = log.node(rev)
715 elif not rev:
715 elif not rev:
716 rev = log.rev(changenode)
716 rev = log.rev(changenode)
717
717
718 changes = log.read(changenode)
718 changes = log.read(changenode)
719
719
720 def showlist(name, values, plural=None, **args):
720 def showlist(name, values, plural=None, **args):
721 '''expand set of values.
721 '''expand set of values.
722 name is name of key in template map.
722 name is name of key in template map.
723 values is list of strings or dicts.
723 values is list of strings or dicts.
724 plural is plural of name, if not simply name + 's'.
724 plural is plural of name, if not simply name + 's'.
725
725
726 expansion works like this, given name 'foo'.
726 expansion works like this, given name 'foo'.
727
727
728 if values is empty, expand 'no_foos'.
728 if values is empty, expand 'no_foos'.
729
729
730 if 'foo' not in template map, return values as a string,
730 if 'foo' not in template map, return values as a string,
731 joined by space.
731 joined by space.
732
732
733 expand 'start_foos'.
733 expand 'start_foos'.
734
734
735 for each value, expand 'foo'. if 'last_foo' in template
735 for each value, expand 'foo'. if 'last_foo' in template
736 map, expand it instead of 'foo' for last key.
736 map, expand it instead of 'foo' for last key.
737
737
738 expand 'end_foos'.
738 expand 'end_foos'.
739 '''
739 '''
740 if plural: names = plural
740 if plural: names = plural
741 else: names = name + 's'
741 else: names = name + 's'
742 if not values:
742 if not values:
743 noname = 'no_' + names
743 noname = 'no_' + names
744 if noname in self.t:
744 if noname in self.t:
745 yield self.t(noname, **args)
745 yield self.t(noname, **args)
746 return
746 return
747 if name not in self.t:
747 if name not in self.t:
748 if isinstance(values[0], str):
748 if isinstance(values[0], str):
749 yield ' '.join(values)
749 yield ' '.join(values)
750 else:
750 else:
751 for v in values:
751 for v in values:
752 yield dict(v, **args)
752 yield dict(v, **args)
753 return
753 return
754 startname = 'start_' + names
754 startname = 'start_' + names
755 if startname in self.t:
755 if startname in self.t:
756 yield self.t(startname, **args)
756 yield self.t(startname, **args)
757 vargs = args.copy()
757 vargs = args.copy()
758 def one(v, tag=name):
758 def one(v, tag=name):
759 try:
759 try:
760 vargs.update(v)
760 vargs.update(v)
761 except (AttributeError, ValueError):
761 except (AttributeError, ValueError):
762 try:
762 try:
763 for a, b in v:
763 for a, b in v:
764 vargs[a] = b
764 vargs[a] = b
765 except ValueError:
765 except ValueError:
766 vargs[name] = v
766 vargs[name] = v
767 return self.t(tag, **vargs)
767 return self.t(tag, **vargs)
768 lastname = 'last_' + name
768 lastname = 'last_' + name
769 if lastname in self.t:
769 if lastname in self.t:
770 last = values.pop()
770 last = values.pop()
771 else:
771 else:
772 last = None
772 last = None
773 for v in values:
773 for v in values:
774 yield one(v)
774 yield one(v)
775 if last is not None:
775 if last is not None:
776 yield one(last, tag=lastname)
776 yield one(last, tag=lastname)
777 endname = 'end_' + names
777 endname = 'end_' + names
778 if endname in self.t:
778 if endname in self.t:
779 yield self.t(endname, **args)
779 yield self.t(endname, **args)
780
780
781 def showbranches(**args):
781 def showbranches(**args):
782 branch = changes[5].get("branch")
782 branch = changes[5].get("branch")
783 if branch != 'default':
783 if branch != 'default':
784 branch = util.tolocal(branch)
784 branch = util.tolocal(branch)
785 return showlist('branch', [branch], plural='branches', **args)
785 return showlist('branch', [branch], plural='branches', **args)
786
786
787 def showparents(**args):
787 def showparents(**args):
788 parents = [[('rev', p), ('node', hex(log.node(p)))]
788 parents = [[('rev', p), ('node', hex(log.node(p)))]
789 for p in self._meaningful_parentrevs(log, rev)]
789 for p in self._meaningful_parentrevs(log, rev)]
790 return showlist('parent', parents, **args)
790 return showlist('parent', parents, **args)
791
791
792 def showtags(**args):
792 def showtags(**args):
793 return showlist('tag', self.repo.nodetags(changenode), **args)
793 return showlist('tag', self.repo.nodetags(changenode), **args)
794
794
795 def showextras(**args):
795 def showextras(**args):
796 extras = changes[5].items()
796 extras = changes[5].items()
797 extras.sort()
797 extras.sort()
798 for key, value in extras:
798 for key, value in extras:
799 args = args.copy()
799 args = args.copy()
800 args.update(dict(key=key, value=value))
800 args.update(dict(key=key, value=value))
801 yield self.t('extra', **args)
801 yield self.t('extra', **args)
802
802
803 def showcopies(**args):
803 def showcopies(**args):
804 c = [{'name': x[0], 'source': x[1]} for x in copies]
804 c = [{'name': x[0], 'source': x[1]} for x in copies]
805 return showlist('file_copy', c, plural='file_copies', **args)
805 return showlist('file_copy', c, plural='file_copies', **args)
806
806
807 files = []
807 files = []
808 def getfiles():
808 def getfiles():
809 if not files:
809 if not files:
810 files[:] = self.repo.status(
810 files[:] = self.repo.status(
811 log.parents(changenode)[0], changenode)[:3]
811 log.parents(changenode)[0], changenode)[:3]
812 return files
812 return files
813 def showfiles(**args):
813 def showfiles(**args):
814 return showlist('file', changes[3], **args)
814 return showlist('file', changes[3], **args)
815 def showmods(**args):
815 def showmods(**args):
816 return showlist('file_mod', getfiles()[0], **args)
816 return showlist('file_mod', getfiles()[0], **args)
817 def showadds(**args):
817 def showadds(**args):
818 return showlist('file_add', getfiles()[1], **args)
818 return showlist('file_add', getfiles()[1], **args)
819 def showdels(**args):
819 def showdels(**args):
820 return showlist('file_del', getfiles()[2], **args)
820 return showlist('file_del', getfiles()[2], **args)
821 def showmanifest(**args):
821 def showmanifest(**args):
822 args = args.copy()
822 args = args.copy()
823 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
823 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
824 node=hex(changes[0])))
824 node=hex(changes[0])))
825 return self.t('manifest', **args)
825 return self.t('manifest', **args)
826
826
827 defprops = {
827 defprops = {
828 'author': changes[1],
828 'author': changes[1],
829 'branches': showbranches,
829 'branches': showbranches,
830 'date': changes[2],
830 'date': changes[2],
831 'desc': changes[4].strip(),
831 'desc': changes[4].strip(),
832 'file_adds': showadds,
832 'file_adds': showadds,
833 'file_dels': showdels,
833 'file_dels': showdels,
834 'file_mods': showmods,
834 'file_mods': showmods,
835 'files': showfiles,
835 'files': showfiles,
836 'file_copies': showcopies,
836 'file_copies': showcopies,
837 'manifest': showmanifest,
837 'manifest': showmanifest,
838 'node': hex(changenode),
838 'node': hex(changenode),
839 'parents': showparents,
839 'parents': showparents,
840 'rev': rev,
840 'rev': rev,
841 'tags': showtags,
841 'tags': showtags,
842 'extras': showextras,
842 'extras': showextras,
843 }
843 }
844 props = props.copy()
844 props = props.copy()
845 props.update(defprops)
845 props.update(defprops)
846
846
847 try:
847 try:
848 if self.ui.debugflag and 'header_debug' in self.t:
848 if self.ui.debugflag and 'header_debug' in self.t:
849 key = 'header_debug'
849 key = 'header_debug'
850 elif self.ui.quiet and 'header_quiet' in self.t:
850 elif self.ui.quiet and 'header_quiet' in self.t:
851 key = 'header_quiet'
851 key = 'header_quiet'
852 elif self.ui.verbose and 'header_verbose' in self.t:
852 elif self.ui.verbose and 'header_verbose' in self.t:
853 key = 'header_verbose'
853 key = 'header_verbose'
854 elif 'header' in self.t:
854 elif 'header' in self.t:
855 key = 'header'
855 key = 'header'
856 else:
856 else:
857 key = ''
857 key = ''
858 if key:
858 if key:
859 h = templater.stringify(self.t(key, **props))
859 h = templater.stringify(self.t(key, **props))
860 if self.buffered:
860 if self.buffered:
861 self.header[rev] = h
861 self.header[rev] = h
862 else:
862 else:
863 self.ui.write(h)
863 self.ui.write(h)
864 if self.ui.debugflag and 'changeset_debug' in self.t:
864 if self.ui.debugflag and 'changeset_debug' in self.t:
865 key = 'changeset_debug'
865 key = 'changeset_debug'
866 elif self.ui.quiet and 'changeset_quiet' in self.t:
866 elif self.ui.quiet and 'changeset_quiet' in self.t:
867 key = 'changeset_quiet'
867 key = 'changeset_quiet'
868 elif self.ui.verbose and 'changeset_verbose' in self.t:
868 elif self.ui.verbose and 'changeset_verbose' in self.t:
869 key = 'changeset_verbose'
869 key = 'changeset_verbose'
870 else:
870 else:
871 key = 'changeset'
871 key = 'changeset'
872 self.ui.write(templater.stringify(self.t(key, **props)))
872 self.ui.write(templater.stringify(self.t(key, **props)))
873 self.showpatch(changenode)
873 self.showpatch(changenode)
874 except KeyError, inst:
874 except KeyError, inst:
875 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
875 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
876 inst.args[0]))
876 inst.args[0]))
877 except SyntaxError, inst:
877 except SyntaxError, inst:
878 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
878 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
879
879
880 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
880 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
881 """show one changeset using template or regular display.
881 """show one changeset using template or regular display.
882
882
883 Display format will be the first non-empty hit of:
883 Display format will be the first non-empty hit of:
884 1. option 'template'
884 1. option 'template'
885 2. option 'style'
885 2. option 'style'
886 3. [ui] setting 'logtemplate'
886 3. [ui] setting 'logtemplate'
887 4. [ui] setting 'style'
887 4. [ui] setting 'style'
888 If all of these values are either the unset or the empty string,
888 If all of these values are either the unset or the empty string,
889 regular display via changeset_printer() is done.
889 regular display via changeset_printer() is done.
890 """
890 """
891 # options
891 # options
892 patch = False
892 patch = False
893 if opts.get('patch'):
893 if opts.get('patch'):
894 patch = matchfn or matchall(repo)
894 patch = matchfn or matchall(repo)
895
895
896 tmpl = opts.get('template')
896 tmpl = opts.get('template')
897 mapfile = None
897 mapfile = None
898 if tmpl:
898 if tmpl:
899 tmpl = templater.parsestring(tmpl, quoted=False)
899 tmpl = templater.parsestring(tmpl, quoted=False)
900 else:
900 else:
901 mapfile = opts.get('style')
901 mapfile = opts.get('style')
902 # ui settings
902 # ui settings
903 if not mapfile:
903 if not mapfile:
904 tmpl = ui.config('ui', 'logtemplate')
904 tmpl = ui.config('ui', 'logtemplate')
905 if tmpl:
905 if tmpl:
906 tmpl = templater.parsestring(tmpl)
906 tmpl = templater.parsestring(tmpl)
907 else:
907 else:
908 mapfile = ui.config('ui', 'style')
908 mapfile = ui.config('ui', 'style')
909
909
910 if tmpl or mapfile:
910 if tmpl or mapfile:
911 if mapfile:
911 if mapfile:
912 if not os.path.split(mapfile)[0]:
912 if not os.path.split(mapfile)[0]:
913 mapname = (templater.templatepath('map-cmdline.' + mapfile)
913 mapname = (templater.templatepath('map-cmdline.' + mapfile)
914 or templater.templatepath(mapfile))
914 or templater.templatepath(mapfile))
915 if mapname: mapfile = mapname
915 if mapname: mapfile = mapname
916 try:
916 try:
917 t = changeset_templater(ui, repo, patch, mapfile, buffered)
917 t = changeset_templater(ui, repo, patch, mapfile, buffered)
918 except SyntaxError, inst:
918 except SyntaxError, inst:
919 raise util.Abort(inst.args[0])
919 raise util.Abort(inst.args[0])
920 if tmpl: t.use_template(tmpl)
920 if tmpl: t.use_template(tmpl)
921 return t
921 return t
922 return changeset_printer(ui, repo, patch, buffered)
922 return changeset_printer(ui, repo, patch, buffered)
923
923
924 def finddate(ui, repo, date):
924 def finddate(ui, repo, date):
925 """Find the tipmost changeset that matches the given date spec"""
925 """Find the tipmost changeset that matches the given date spec"""
926 df = util.matchdate(date)
926 df = util.matchdate(date)
927 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
927 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
928 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
928 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
929 results = {}
929 results = {}
930 for st, rev, fns in changeiter:
930 for st, rev, fns in changeiter:
931 if st == 'add':
931 if st == 'add':
932 d = get(rev)[2]
932 d = get(rev)[2]
933 if df(d[0]):
933 if df(d[0]):
934 results[rev] = d
934 results[rev] = d
935 elif st == 'iter':
935 elif st == 'iter':
936 if rev in results:
936 if rev in results:
937 ui.status("Found revision %s from %s\n" %
937 ui.status("Found revision %s from %s\n" %
938 (rev, util.datestr(results[rev])))
938 (rev, util.datestr(results[rev])))
939 return str(rev)
939 return str(rev)
940
940
941 raise util.Abort(_("revision matching date not found"))
941 raise util.Abort(_("revision matching date not found"))
942
942
943 def walkchangerevs(ui, repo, pats, change, opts):
943 def walkchangerevs(ui, repo, pats, change, opts):
944 '''Iterate over files and the revs they changed in.
944 '''Iterate over files and the revs they changed in.
945
945
946 Callers most commonly need to iterate backwards over the history
946 Callers most commonly need to iterate backwards over the history
947 it is interested in. Doing so has awful (quadratic-looking)
947 it is interested in. Doing so has awful (quadratic-looking)
948 performance, so we use iterators in a "windowed" way.
948 performance, so we use iterators in a "windowed" way.
949
949
950 We walk a window of revisions in the desired order. Within the
950 We walk a window of revisions in the desired order. Within the
951 window, we first walk forwards to gather data, then in the desired
951 window, we first walk forwards to gather data, then in the desired
952 order (usually backwards) to display it.
952 order (usually backwards) to display it.
953
953
954 This function returns an (iterator, matchfn) tuple. The iterator
954 This function returns an (iterator, matchfn) tuple. The iterator
955 yields 3-tuples. They will be of one of the following forms:
955 yields 3-tuples. They will be of one of the following forms:
956
956
957 "window", incrementing, lastrev: stepping through a window,
957 "window", incrementing, lastrev: stepping through a window,
958 positive if walking forwards through revs, last rev in the
958 positive if walking forwards through revs, last rev in the
959 sequence iterated over - use to reset state for the current window
959 sequence iterated over - use to reset state for the current window
960
960
961 "add", rev, fns: out-of-order traversal of the given file names
961 "add", rev, fns: out-of-order traversal of the given file names
962 fns, which changed during revision rev - use to gather data for
962 fns, which changed during revision rev - use to gather data for
963 possible display
963 possible display
964
964
965 "iter", rev, None: in-order traversal of the revs earlier iterated
965 "iter", rev, None: in-order traversal of the revs earlier iterated
966 over with "add" - use to display data'''
966 over with "add" - use to display data'''
967
967
968 def increasing_windows(start, end, windowsize=8, sizelimit=512):
968 def increasing_windows(start, end, windowsize=8, sizelimit=512):
969 if start < end:
969 if start < end:
970 while start < end:
970 while start < end:
971 yield start, min(windowsize, end-start)
971 yield start, min(windowsize, end-start)
972 start += windowsize
972 start += windowsize
973 if windowsize < sizelimit:
973 if windowsize < sizelimit:
974 windowsize *= 2
974 windowsize *= 2
975 else:
975 else:
976 while start > end:
976 while start > end:
977 yield start, min(windowsize, start-end-1)
977 yield start, min(windowsize, start-end-1)
978 start -= windowsize
978 start -= windowsize
979 if windowsize < sizelimit:
979 if windowsize < sizelimit:
980 windowsize *= 2
980 windowsize *= 2
981
981
982 m = match(repo, pats, opts)
982 m = match(repo, pats, opts)
983 follow = opts.get('follow') or opts.get('follow_first')
983 follow = opts.get('follow') or opts.get('follow_first')
984
984
985 if repo.changelog.count() == 0:
985 if repo.changelog.count() == 0:
986 return [], m
986 return [], m
987
987
988 if follow:
988 if follow:
989 defrange = '%s:0' % repo.changectx().rev()
989 defrange = '%s:0' % repo.changectx().rev()
990 else:
990 else:
991 defrange = '-1:0'
991 defrange = '-1:0'
992 revs = revrange(repo, opts['rev'] or [defrange])
992 revs = revrange(repo, opts['rev'] or [defrange])
993 wanted = {}
993 wanted = {}
994 slowpath = m.anypats() or opts.get('removed')
994 slowpath = m.anypats() or opts.get('removed')
995 fncache = {}
995 fncache = {}
996
996
997 if not slowpath and not m.files():
997 if not slowpath and not m.files():
998 # No files, no patterns. Display all revs.
998 # No files, no patterns. Display all revs.
999 wanted = dict.fromkeys(revs)
999 wanted = dict.fromkeys(revs)
1000 copies = []
1000 copies = []
1001 if not slowpath:
1001 if not slowpath:
1002 # Only files, no patterns. Check the history of each file.
1002 # Only files, no patterns. Check the history of each file.
1003 def filerevgen(filelog, node):
1003 def filerevgen(filelog, node):
1004 cl_count = repo.changelog.count()
1004 cl_count = repo.changelog.count()
1005 if node is None:
1005 if node is None:
1006 last = filelog.count() - 1
1006 last = filelog.count() - 1
1007 else:
1007 else:
1008 last = filelog.rev(node)
1008 last = filelog.rev(node)
1009 for i, window in increasing_windows(last, nullrev):
1009 for i, window in increasing_windows(last, nullrev):
1010 revs = []
1010 revs = []
1011 for j in xrange(i - window, i + 1):
1011 for j in xrange(i - window, i + 1):
1012 n = filelog.node(j)
1012 n = filelog.node(j)
1013 revs.append((filelog.linkrev(n),
1013 revs.append((filelog.linkrev(n),
1014 follow and filelog.renamed(n)))
1014 follow and filelog.renamed(n)))
1015 revs.reverse()
1015 revs.reverse()
1016 for rev in revs:
1016 for rev in revs:
1017 # only yield rev for which we have the changelog, it can
1017 # only yield rev for which we have the changelog, it can
1018 # happen while doing "hg log" during a pull or commit
1018 # happen while doing "hg log" during a pull or commit
1019 if rev[0] < cl_count:
1019 if rev[0] < cl_count:
1020 yield rev
1020 yield rev
1021 def iterfiles():
1021 def iterfiles():
1022 for filename in m.files():
1022 for filename in m.files():
1023 yield filename, None
1023 yield filename, None
1024 for filename_node in copies:
1024 for filename_node in copies:
1025 yield filename_node
1025 yield filename_node
1026 minrev, maxrev = min(revs), max(revs)
1026 minrev, maxrev = min(revs), max(revs)
1027 for file_, node in iterfiles():
1027 for file_, node in iterfiles():
1028 filelog = repo.file(file_)
1028 filelog = repo.file(file_)
1029 if filelog.count() == 0:
1029 if filelog.count() == 0:
1030 if node is None:
1030 if node is None:
1031 # A zero count may be a directory or deleted file, so
1031 # A zero count may be a directory or deleted file, so
1032 # try to find matching entries on the slow path.
1032 # try to find matching entries on the slow path.
1033 slowpath = True
1033 slowpath = True
1034 break
1034 break
1035 else:
1035 else:
1036 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1036 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1037 % (file_, short(node)))
1037 % (file_, short(node)))
1038 continue
1038 continue
1039 for rev, copied in filerevgen(filelog, node):
1039 for rev, copied in filerevgen(filelog, node):
1040 if rev <= maxrev:
1040 if rev <= maxrev:
1041 if rev < minrev:
1041 if rev < minrev:
1042 break
1042 break
1043 fncache.setdefault(rev, [])
1043 fncache.setdefault(rev, [])
1044 fncache[rev].append(file_)
1044 fncache[rev].append(file_)
1045 wanted[rev] = 1
1045 wanted[rev] = 1
1046 if follow and copied:
1046 if follow and copied:
1047 copies.append(copied)
1047 copies.append(copied)
1048 if slowpath:
1048 if slowpath:
1049 if follow:
1049 if follow:
1050 raise util.Abort(_('can only follow copies/renames for explicit '
1050 raise util.Abort(_('can only follow copies/renames for explicit '
1051 'file names'))
1051 'file names'))
1052
1052
1053 # The slow path checks files modified in every changeset.
1053 # The slow path checks files modified in every changeset.
1054 def changerevgen():
1054 def changerevgen():
1055 for i, window in increasing_windows(repo.changelog.count()-1,
1055 for i, window in increasing_windows(repo.changelog.count()-1,
1056 nullrev):
1056 nullrev):
1057 for j in xrange(i - window, i + 1):
1057 for j in xrange(i - window, i + 1):
1058 yield j, change(j)[3]
1058 yield j, change(j)[3]
1059
1059
1060 for rev, changefiles in changerevgen():
1060 for rev, changefiles in changerevgen():
1061 matches = filter(m, changefiles)
1061 matches = filter(m, changefiles)
1062 if matches:
1062 if matches:
1063 fncache[rev] = matches
1063 fncache[rev] = matches
1064 wanted[rev] = 1
1064 wanted[rev] = 1
1065
1065
1066 class followfilter:
1066 class followfilter:
1067 def __init__(self, onlyfirst=False):
1067 def __init__(self, onlyfirst=False):
1068 self.startrev = nullrev
1068 self.startrev = nullrev
1069 self.roots = []
1069 self.roots = []
1070 self.onlyfirst = onlyfirst
1070 self.onlyfirst = onlyfirst
1071
1071
1072 def match(self, rev):
1072 def match(self, rev):
1073 def realparents(rev):
1073 def realparents(rev):
1074 if self.onlyfirst:
1074 if self.onlyfirst:
1075 return repo.changelog.parentrevs(rev)[0:1]
1075 return repo.changelog.parentrevs(rev)[0:1]
1076 else:
1076 else:
1077 return filter(lambda x: x != nullrev,
1077 return filter(lambda x: x != nullrev,
1078 repo.changelog.parentrevs(rev))
1078 repo.changelog.parentrevs(rev))
1079
1079
1080 if self.startrev == nullrev:
1080 if self.startrev == nullrev:
1081 self.startrev = rev
1081 self.startrev = rev
1082 return True
1082 return True
1083
1083
1084 if rev > self.startrev:
1084 if rev > self.startrev:
1085 # forward: all descendants
1085 # forward: all descendants
1086 if not self.roots:
1086 if not self.roots:
1087 self.roots.append(self.startrev)
1087 self.roots.append(self.startrev)
1088 for parent in realparents(rev):
1088 for parent in realparents(rev):
1089 if parent in self.roots:
1089 if parent in self.roots:
1090 self.roots.append(rev)
1090 self.roots.append(rev)
1091 return True
1091 return True
1092 else:
1092 else:
1093 # backwards: all parents
1093 # backwards: all parents
1094 if not self.roots:
1094 if not self.roots:
1095 self.roots.extend(realparents(self.startrev))
1095 self.roots.extend(realparents(self.startrev))
1096 if rev in self.roots:
1096 if rev in self.roots:
1097 self.roots.remove(rev)
1097 self.roots.remove(rev)
1098 self.roots.extend(realparents(rev))
1098 self.roots.extend(realparents(rev))
1099 return True
1099 return True
1100
1100
1101 return False
1101 return False
1102
1102
1103 # it might be worthwhile to do this in the iterator if the rev range
1103 # it might be worthwhile to do this in the iterator if the rev range
1104 # is descending and the prune args are all within that range
1104 # is descending and the prune args are all within that range
1105 for rev in opts.get('prune', ()):
1105 for rev in opts.get('prune', ()):
1106 rev = repo.changelog.rev(repo.lookup(rev))
1106 rev = repo.changelog.rev(repo.lookup(rev))
1107 ff = followfilter()
1107 ff = followfilter()
1108 stop = min(revs[0], revs[-1])
1108 stop = min(revs[0], revs[-1])
1109 for x in xrange(rev, stop-1, -1):
1109 for x in xrange(rev, stop-1, -1):
1110 if ff.match(x) and x in wanted:
1110 if ff.match(x) and x in wanted:
1111 del wanted[x]
1111 del wanted[x]
1112
1112
1113 def iterate():
1113 def iterate():
1114 if follow and not m.files():
1114 if follow and not m.files():
1115 ff = followfilter(onlyfirst=opts.get('follow_first'))
1115 ff = followfilter(onlyfirst=opts.get('follow_first'))
1116 def want(rev):
1116 def want(rev):
1117 if ff.match(rev) and rev in wanted:
1117 if ff.match(rev) and rev in wanted:
1118 return True
1118 return True
1119 return False
1119 return False
1120 else:
1120 else:
1121 def want(rev):
1121 def want(rev):
1122 return rev in wanted
1122 return rev in wanted
1123
1123
1124 for i, window in increasing_windows(0, len(revs)):
1124 for i, window in increasing_windows(0, len(revs)):
1125 yield 'window', revs[0] < revs[-1], revs[-1]
1125 yield 'window', revs[0] < revs[-1], revs[-1]
1126 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1126 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1127 srevs = list(nrevs)
1127 srevs = list(nrevs)
1128 srevs.sort()
1128 srevs.sort()
1129 for rev in srevs:
1129 for rev in srevs:
1130 fns = fncache.get(rev)
1130 fns = fncache.get(rev)
1131 if not fns:
1131 if not fns:
1132 def fns_generator():
1132 def fns_generator():
1133 for f in change(rev)[3]:
1133 for f in change(rev)[3]:
1134 if m(f):
1134 if m(f):
1135 yield f
1135 yield f
1136 fns = fns_generator()
1136 fns = fns_generator()
1137 yield 'add', rev, fns
1137 yield 'add', rev, fns
1138 for rev in nrevs:
1138 for rev in nrevs:
1139 yield 'iter', rev, None
1139 yield 'iter', rev, None
1140 return iterate(), m
1140 return iterate(), m
1141
1141
1142 def commit(ui, repo, commitfunc, pats, opts):
1142 def commit(ui, repo, commitfunc, pats, opts):
1143 '''commit the specified files or all outstanding changes'''
1143 '''commit the specified files or all outstanding changes'''
1144 date = opts.get('date')
1144 date = opts.get('date')
1145 if date:
1145 if date:
1146 opts['date'] = util.parsedate(date)
1146 opts['date'] = util.parsedate(date)
1147 message = logmessage(opts)
1147 message = logmessage(opts)
1148
1148
1149 # extract addremove carefully -- this function can be called from a command
1149 # extract addremove carefully -- this function can be called from a command
1150 # that doesn't support addremove
1150 # that doesn't support addremove
1151 if opts.get('addremove'):
1151 if opts.get('addremove'):
1152 addremove(repo, pats, opts)
1152 addremove(repo, pats, opts)
1153
1153
1154 m = match(repo, pats, opts)
1154 m = match(repo, pats, opts)
1155 if pats:
1155 if pats:
1156 status = repo.status(files=m.files(), match=m)
1156 status = repo.status(files=m.files(), match=m)
1157 modified, added, removed, deleted, unknown = status[:5]
1157 modified, added, removed, deleted, unknown = status[:5]
1158 files = modified + added + removed
1158 files = modified + added + removed
1159 slist = None
1159 slist = None
1160 for f in m.files():
1160 for f in m.files():
1161 if f == '.':
1161 if f == '.':
1162 continue
1162 continue
1163 if f not in files:
1163 if f not in files:
1164 rf = repo.wjoin(f)
1164 rf = repo.wjoin(f)
1165 rel = repo.pathto(f)
1165 rel = repo.pathto(f)
1166 try:
1166 try:
1167 mode = os.lstat(rf)[stat.ST_MODE]
1167 mode = os.lstat(rf)[stat.ST_MODE]
1168 except OSError:
1168 except OSError:
1169 raise util.Abort(_("file %s not found!") % rel)
1169 raise util.Abort(_("file %s not found!") % rel)
1170 if stat.S_ISDIR(mode):
1170 if stat.S_ISDIR(mode):
1171 name = f + '/'
1171 name = f + '/'
1172 if slist is None:
1172 if slist is None:
1173 slist = list(files)
1173 slist = list(files)
1174 slist.sort()
1174 slist.sort()
1175 i = bisect.bisect(slist, name)
1175 i = bisect.bisect(slist, name)
1176 if i >= len(slist) or not slist[i].startswith(name):
1176 if i >= len(slist) or not slist[i].startswith(name):
1177 raise util.Abort(_("no match under directory %s!")
1177 raise util.Abort(_("no match under directory %s!")
1178 % rel)
1178 % rel)
1179 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1179 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1180 raise util.Abort(_("can't commit %s: "
1180 raise util.Abort(_("can't commit %s: "
1181 "unsupported file type!") % rel)
1181 "unsupported file type!") % rel)
1182 elif f not in repo.dirstate:
1182 elif f not in repo.dirstate:
1183 raise util.Abort(_("file %s not tracked!") % rel)
1183 raise util.Abort(_("file %s not tracked!") % rel)
1184 m = matchfiles(repo, files)
1184 m = matchfiles(repo, files)
1185 try:
1185 try:
1186 return commitfunc(ui, repo, m.files(), message, m, opts)
1186 return commitfunc(ui, repo, message, m, opts)
1187 except ValueError, inst:
1187 except ValueError, inst:
1188 raise util.Abort(str(inst))
1188 raise util.Abort(str(inst))
@@ -1,3328 +1,3328 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from repo import RepoError, NoCapability
9 from repo import RepoError, NoCapability
10 from i18n import _
10 from i18n import _
11 import os, re, sys, urllib
11 import os, re, sys, urllib
12 import hg, util, revlog, bundlerepo, extensions, copies
12 import hg, util, revlog, bundlerepo, extensions, copies
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import version, socket
14 import version, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
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 repository.
23 Schedule files to be version controlled and added to the repository.
24
24
25 The files will be added to the repository at the next commit. To
25 The files will be added to the repository at the next commit. To
26 undo an add before that, see hg revert.
26 undo an add before that, see hg revert.
27
27
28 If no names are given, add all files in the repository.
28 If no names are given, add all files in the repository.
29 """
29 """
30
30
31 rejected = None
31 rejected = None
32 exacts = {}
32 exacts = {}
33 names = []
33 names = []
34 m = cmdutil.match(repo, pats, opts)
34 m = cmdutil.match(repo, pats, opts)
35 m.bad = lambda x,y: True
35 m.bad = lambda x,y: True
36 for abs in repo.walk(m):
36 for abs in repo.walk(m):
37 if m.exact(abs):
37 if m.exact(abs):
38 if ui.verbose:
38 if ui.verbose:
39 ui.status(_('adding %s\n') % m.rel(abs))
39 ui.status(_('adding %s\n') % m.rel(abs))
40 names.append(abs)
40 names.append(abs)
41 exacts[abs] = 1
41 exacts[abs] = 1
42 elif abs not in repo.dirstate:
42 elif abs not in repo.dirstate:
43 ui.status(_('adding %s\n') % m.rel(abs))
43 ui.status(_('adding %s\n') % m.rel(abs))
44 names.append(abs)
44 names.append(abs)
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 rejected = repo.add(names)
46 rejected = repo.add(names)
47 rejected = [p for p in rejected if p in exacts]
47 rejected = [p for p in rejected if p in exacts]
48 return rejected and 1 or 0
48 return rejected and 1 or 0
49
49
50 def addremove(ui, repo, *pats, **opts):
50 def addremove(ui, repo, *pats, **opts):
51 """add all new files, delete all missing files
51 """add all new files, delete all missing files
52
52
53 Add all new files and remove all missing files from the repository.
53 Add all new files and remove all missing files from the repository.
54
54
55 New files are ignored if they match any of the patterns in .hgignore. As
55 New files are ignored if they match any of the patterns in .hgignore. As
56 with add, these changes take effect at the next commit.
56 with add, these changes take effect at the next commit.
57
57
58 Use the -s option to detect renamed files. With a parameter > 0,
58 Use the -s option to detect renamed files. With a parameter > 0,
59 this compares every removed file with every added file and records
59 this compares every removed file with every added file and records
60 those similar enough as renames. This option takes a percentage
60 those similar enough as renames. This option takes a percentage
61 between 0 (disabled) and 100 (files must be identical) as its
61 between 0 (disabled) and 100 (files must be identical) as its
62 parameter. Detecting renamed files this way can be expensive.
62 parameter. Detecting renamed files this way can be expensive.
63 """
63 """
64 try:
64 try:
65 sim = float(opts.get('similarity') or 0)
65 sim = float(opts.get('similarity') or 0)
66 except ValueError:
66 except ValueError:
67 raise util.Abort(_('similarity must be a number'))
67 raise util.Abort(_('similarity must be a number'))
68 if sim < 0 or sim > 100:
68 if sim < 0 or sim > 100:
69 raise util.Abort(_('similarity must be between 0 and 100'))
69 raise util.Abort(_('similarity must be between 0 and 100'))
70 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
70 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
71
71
72 def annotate(ui, repo, *pats, **opts):
72 def annotate(ui, repo, *pats, **opts):
73 """show changeset information per file line
73 """show changeset information per file line
74
74
75 List changes in files, showing the revision id responsible for each line
75 List changes in files, showing the revision id responsible for each line
76
76
77 This command is useful to discover who did a change or when a change took
77 This command is useful to discover who did a change or when a change took
78 place.
78 place.
79
79
80 Without the -a option, annotate will avoid processing files it
80 Without the -a option, annotate will avoid processing files it
81 detects as binary. With -a, annotate will generate an annotation
81 detects as binary. With -a, annotate will generate an annotation
82 anyway, probably with undesirable results.
82 anyway, probably with undesirable results.
83 """
83 """
84 datefunc = ui.quiet and util.shortdate or util.datestr
84 datefunc = ui.quiet and util.shortdate or util.datestr
85 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
85 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
86
86
87 if not pats:
87 if not pats:
88 raise util.Abort(_('at least one file name or pattern required'))
88 raise util.Abort(_('at least one file name or pattern required'))
89
89
90 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
90 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
91 ('number', lambda x: str(x[0].rev())),
91 ('number', lambda x: str(x[0].rev())),
92 ('changeset', lambda x: short(x[0].node())),
92 ('changeset', lambda x: short(x[0].node())),
93 ('date', getdate),
93 ('date', getdate),
94 ('follow', lambda x: x[0].path()),
94 ('follow', lambda x: x[0].path()),
95 ]
95 ]
96
96
97 if (not opts['user'] and not opts['changeset'] and not opts['date']
97 if (not opts['user'] and not opts['changeset'] and not opts['date']
98 and not opts['follow']):
98 and not opts['follow']):
99 opts['number'] = 1
99 opts['number'] = 1
100
100
101 linenumber = opts.get('line_number') is not None
101 linenumber = opts.get('line_number') is not None
102 if (linenumber and (not opts['changeset']) and (not opts['number'])):
102 if (linenumber and (not opts['changeset']) and (not opts['number'])):
103 raise util.Abort(_('at least one of -n/-c is required for -l'))
103 raise util.Abort(_('at least one of -n/-c is required for -l'))
104
104
105 funcmap = [func for op, func in opmap if opts.get(op)]
105 funcmap = [func for op, func in opmap if opts.get(op)]
106 if linenumber:
106 if linenumber:
107 lastfunc = funcmap[-1]
107 lastfunc = funcmap[-1]
108 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
108 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
109
109
110 ctx = repo.changectx(opts['rev'])
110 ctx = repo.changectx(opts['rev'])
111
111
112 m = cmdutil.match(repo, pats, opts)
112 m = cmdutil.match(repo, pats, opts)
113 for abs in repo.walk(m, ctx.node()):
113 for abs in repo.walk(m, ctx.node()):
114 fctx = ctx.filectx(abs)
114 fctx = ctx.filectx(abs)
115 if not opts['text'] and util.binary(fctx.data()):
115 if not opts['text'] and util.binary(fctx.data()):
116 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
116 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
117 continue
117 continue
118
118
119 lines = fctx.annotate(follow=opts.get('follow'),
119 lines = fctx.annotate(follow=opts.get('follow'),
120 linenumber=linenumber)
120 linenumber=linenumber)
121 pieces = []
121 pieces = []
122
122
123 for f in funcmap:
123 for f in funcmap:
124 l = [f(n) for n, dummy in lines]
124 l = [f(n) for n, dummy in lines]
125 if l:
125 if l:
126 m = max(map(len, l))
126 m = max(map(len, l))
127 pieces.append(["%*s" % (m, x) for x in l])
127 pieces.append(["%*s" % (m, x) for x in l])
128
128
129 if pieces:
129 if pieces:
130 for p, l in zip(zip(*pieces), lines):
130 for p, l in zip(zip(*pieces), lines):
131 ui.write("%s: %s" % (" ".join(p), l[1]))
131 ui.write("%s: %s" % (" ".join(p), l[1]))
132
132
133 def archive(ui, repo, dest, **opts):
133 def archive(ui, repo, dest, **opts):
134 '''create unversioned archive of a repository revision
134 '''create unversioned archive of a repository revision
135
135
136 By default, the revision used is the parent of the working
136 By default, the revision used is the parent of the working
137 directory; use "-r" to specify a different revision.
137 directory; use "-r" to specify a different revision.
138
138
139 To specify the type of archive to create, use "-t". Valid
139 To specify the type of archive to create, use "-t". Valid
140 types are:
140 types are:
141
141
142 "files" (default): a directory full of files
142 "files" (default): a directory full of files
143 "tar": tar archive, uncompressed
143 "tar": tar archive, uncompressed
144 "tbz2": tar archive, compressed using bzip2
144 "tbz2": tar archive, compressed using bzip2
145 "tgz": tar archive, compressed using gzip
145 "tgz": tar archive, compressed using gzip
146 "uzip": zip archive, uncompressed
146 "uzip": zip archive, uncompressed
147 "zip": zip archive, compressed using deflate
147 "zip": zip archive, compressed using deflate
148
148
149 The exact name of the destination archive or directory is given
149 The exact name of the destination archive or directory is given
150 using a format string; see "hg help export" for details.
150 using a format string; see "hg help export" for details.
151
151
152 Each member added to an archive file has a directory prefix
152 Each member added to an archive file has a directory prefix
153 prepended. Use "-p" to specify a format string for the prefix.
153 prepended. Use "-p" to specify a format string for the prefix.
154 The default is the basename of the archive, with suffixes removed.
154 The default is the basename of the archive, with suffixes removed.
155 '''
155 '''
156
156
157 ctx = repo.changectx(opts['rev'])
157 ctx = repo.changectx(opts['rev'])
158 if not ctx:
158 if not ctx:
159 raise util.Abort(_('repository has no revisions'))
159 raise util.Abort(_('repository has no revisions'))
160 node = ctx.node()
160 node = ctx.node()
161 dest = cmdutil.make_filename(repo, dest, node)
161 dest = cmdutil.make_filename(repo, dest, node)
162 if os.path.realpath(dest) == repo.root:
162 if os.path.realpath(dest) == repo.root:
163 raise util.Abort(_('repository root cannot be destination'))
163 raise util.Abort(_('repository root cannot be destination'))
164 matchfn = cmdutil.match(repo, [], opts)
164 matchfn = cmdutil.match(repo, [], opts)
165 kind = opts.get('type') or 'files'
165 kind = opts.get('type') or 'files'
166 prefix = opts['prefix']
166 prefix = opts['prefix']
167 if dest == '-':
167 if dest == '-':
168 if kind == 'files':
168 if kind == 'files':
169 raise util.Abort(_('cannot archive plain files to stdout'))
169 raise util.Abort(_('cannot archive plain files to stdout'))
170 dest = sys.stdout
170 dest = sys.stdout
171 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
171 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
172 prefix = cmdutil.make_filename(repo, prefix, node)
172 prefix = cmdutil.make_filename(repo, prefix, node)
173 archival.archive(repo, dest, node, kind, not opts['no_decode'],
173 archival.archive(repo, dest, node, kind, not opts['no_decode'],
174 matchfn, prefix)
174 matchfn, prefix)
175
175
176 def backout(ui, repo, node=None, rev=None, **opts):
176 def backout(ui, repo, node=None, rev=None, **opts):
177 '''reverse effect of earlier changeset
177 '''reverse effect of earlier changeset
178
178
179 Commit the backed out changes as a new changeset. The new
179 Commit the backed out changes as a new changeset. The new
180 changeset is a child of the backed out changeset.
180 changeset is a child of the backed out changeset.
181
181
182 If you back out a changeset other than the tip, a new head is
182 If you back out a changeset other than the tip, a new head is
183 created. This head will be the new tip and you should merge this
183 created. This head will be the new tip and you should merge this
184 backout changeset with another head (current one by default).
184 backout changeset with another head (current one by default).
185
185
186 The --merge option remembers the parent of the working directory
186 The --merge option remembers the parent of the working directory
187 before starting the backout, then merges the new head with that
187 before starting the backout, then merges the new head with that
188 changeset afterwards. This saves you from doing the merge by
188 changeset afterwards. This saves you from doing the merge by
189 hand. The result of this merge is not committed, as for a normal
189 hand. The result of this merge is not committed, as for a normal
190 merge.
190 merge.
191
191
192 See 'hg help dates' for a list of formats valid for -d/--date.
192 See 'hg help dates' for a list of formats valid for -d/--date.
193 '''
193 '''
194 if rev and node:
194 if rev and node:
195 raise util.Abort(_("please specify just one revision"))
195 raise util.Abort(_("please specify just one revision"))
196
196
197 if not rev:
197 if not rev:
198 rev = node
198 rev = node
199
199
200 if not rev:
200 if not rev:
201 raise util.Abort(_("please specify a revision to backout"))
201 raise util.Abort(_("please specify a revision to backout"))
202
202
203 date = opts.get('date')
203 date = opts.get('date')
204 if date:
204 if date:
205 opts['date'] = util.parsedate(date)
205 opts['date'] = util.parsedate(date)
206
206
207 cmdutil.bail_if_changed(repo)
207 cmdutil.bail_if_changed(repo)
208 node = repo.lookup(rev)
208 node = repo.lookup(rev)
209
209
210 op1, op2 = repo.dirstate.parents()
210 op1, op2 = repo.dirstate.parents()
211 a = repo.changelog.ancestor(op1, node)
211 a = repo.changelog.ancestor(op1, node)
212 if a != node:
212 if a != node:
213 raise util.Abort(_('cannot back out change on a different branch'))
213 raise util.Abort(_('cannot back out change on a different branch'))
214
214
215 p1, p2 = repo.changelog.parents(node)
215 p1, p2 = repo.changelog.parents(node)
216 if p1 == nullid:
216 if p1 == nullid:
217 raise util.Abort(_('cannot back out a change with no parents'))
217 raise util.Abort(_('cannot back out a change with no parents'))
218 if p2 != nullid:
218 if p2 != nullid:
219 if not opts['parent']:
219 if not opts['parent']:
220 raise util.Abort(_('cannot back out a merge changeset without '
220 raise util.Abort(_('cannot back out a merge changeset without '
221 '--parent'))
221 '--parent'))
222 p = repo.lookup(opts['parent'])
222 p = repo.lookup(opts['parent'])
223 if p not in (p1, p2):
223 if p not in (p1, p2):
224 raise util.Abort(_('%s is not a parent of %s') %
224 raise util.Abort(_('%s is not a parent of %s') %
225 (short(p), short(node)))
225 (short(p), short(node)))
226 parent = p
226 parent = p
227 else:
227 else:
228 if opts['parent']:
228 if opts['parent']:
229 raise util.Abort(_('cannot use --parent on non-merge changeset'))
229 raise util.Abort(_('cannot use --parent on non-merge changeset'))
230 parent = p1
230 parent = p1
231
231
232 # the backout should appear on the same branch
232 # the backout should appear on the same branch
233 branch = repo.dirstate.branch()
233 branch = repo.dirstate.branch()
234 hg.clean(repo, node, show_stats=False)
234 hg.clean(repo, node, show_stats=False)
235 repo.dirstate.setbranch(branch)
235 repo.dirstate.setbranch(branch)
236 revert_opts = opts.copy()
236 revert_opts = opts.copy()
237 revert_opts['date'] = None
237 revert_opts['date'] = None
238 revert_opts['all'] = True
238 revert_opts['all'] = True
239 revert_opts['rev'] = hex(parent)
239 revert_opts['rev'] = hex(parent)
240 revert_opts['no_backup'] = None
240 revert_opts['no_backup'] = None
241 revert(ui, repo, **revert_opts)
241 revert(ui, repo, **revert_opts)
242 commit_opts = opts.copy()
242 commit_opts = opts.copy()
243 commit_opts['addremove'] = False
243 commit_opts['addremove'] = False
244 if not commit_opts['message'] and not commit_opts['logfile']:
244 if not commit_opts['message'] and not commit_opts['logfile']:
245 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
245 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
246 commit_opts['force_editor'] = True
246 commit_opts['force_editor'] = True
247 commit(ui, repo, **commit_opts)
247 commit(ui, repo, **commit_opts)
248 def nice(node):
248 def nice(node):
249 return '%d:%s' % (repo.changelog.rev(node), short(node))
249 return '%d:%s' % (repo.changelog.rev(node), short(node))
250 ui.status(_('changeset %s backs out changeset %s\n') %
250 ui.status(_('changeset %s backs out changeset %s\n') %
251 (nice(repo.changelog.tip()), nice(node)))
251 (nice(repo.changelog.tip()), nice(node)))
252 if op1 != node:
252 if op1 != node:
253 hg.clean(repo, op1, show_stats=False)
253 hg.clean(repo, op1, show_stats=False)
254 if opts['merge']:
254 if opts['merge']:
255 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
255 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
256 hg.merge(repo, hex(repo.changelog.tip()))
256 hg.merge(repo, hex(repo.changelog.tip()))
257 else:
257 else:
258 ui.status(_('the backout changeset is a new head - '
258 ui.status(_('the backout changeset is a new head - '
259 'do not forget to merge\n'))
259 'do not forget to merge\n'))
260 ui.status(_('(use "backout --merge" '
260 ui.status(_('(use "backout --merge" '
261 'if you want to auto-merge)\n'))
261 'if you want to auto-merge)\n'))
262
262
263 def bisect(ui, repo, rev=None, extra=None,
263 def bisect(ui, repo, rev=None, extra=None,
264 reset=None, good=None, bad=None, skip=None, noupdate=None):
264 reset=None, good=None, bad=None, skip=None, noupdate=None):
265 """subdivision search of changesets
265 """subdivision search of changesets
266
266
267 This command helps to find changesets which introduce problems.
267 This command helps to find changesets which introduce problems.
268 To use, mark the earliest changeset you know exhibits the problem
268 To use, mark the earliest changeset you know exhibits the problem
269 as bad, then mark the latest changeset which is free from the
269 as bad, then mark the latest changeset which is free from the
270 problem as good. Bisect will update your working directory to a
270 problem as good. Bisect will update your working directory to a
271 revision for testing. Once you have performed tests, mark the
271 revision for testing. Once you have performed tests, mark the
272 working directory as bad or good and bisect will either update to
272 working directory as bad or good and bisect will either update to
273 another candidate changeset or announce that it has found the bad
273 another candidate changeset or announce that it has found the bad
274 revision.
274 revision.
275 """
275 """
276 # backward compatibility
276 # backward compatibility
277 if rev in "good bad reset init".split():
277 if rev in "good bad reset init".split():
278 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
278 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
279 cmd, rev, extra = rev, extra, None
279 cmd, rev, extra = rev, extra, None
280 if cmd == "good":
280 if cmd == "good":
281 good = True
281 good = True
282 elif cmd == "bad":
282 elif cmd == "bad":
283 bad = True
283 bad = True
284 else:
284 else:
285 reset = True
285 reset = True
286 elif extra or good + bad + skip + reset > 1:
286 elif extra or good + bad + skip + reset > 1:
287 raise util.Abort("Incompatible arguments")
287 raise util.Abort("Incompatible arguments")
288
288
289 if reset:
289 if reset:
290 p = repo.join("bisect.state")
290 p = repo.join("bisect.state")
291 if os.path.exists(p):
291 if os.path.exists(p):
292 os.unlink(p)
292 os.unlink(p)
293 return
293 return
294
294
295 # load state
295 # load state
296 state = {'good': [], 'bad': [], 'skip': []}
296 state = {'good': [], 'bad': [], 'skip': []}
297 if os.path.exists(repo.join("bisect.state")):
297 if os.path.exists(repo.join("bisect.state")):
298 for l in repo.opener("bisect.state"):
298 for l in repo.opener("bisect.state"):
299 kind, node = l[:-1].split()
299 kind, node = l[:-1].split()
300 node = repo.lookup(node)
300 node = repo.lookup(node)
301 if kind not in state:
301 if kind not in state:
302 raise util.Abort(_("unknown bisect kind %s") % kind)
302 raise util.Abort(_("unknown bisect kind %s") % kind)
303 state[kind].append(node)
303 state[kind].append(node)
304
304
305 # update state
305 # update state
306 node = repo.lookup(rev or '.')
306 node = repo.lookup(rev or '.')
307 if good:
307 if good:
308 state['good'].append(node)
308 state['good'].append(node)
309 elif bad:
309 elif bad:
310 state['bad'].append(node)
310 state['bad'].append(node)
311 elif skip:
311 elif skip:
312 state['skip'].append(node)
312 state['skip'].append(node)
313
313
314 # save state
314 # save state
315 f = repo.opener("bisect.state", "w", atomictemp=True)
315 f = repo.opener("bisect.state", "w", atomictemp=True)
316 wlock = repo.wlock()
316 wlock = repo.wlock()
317 try:
317 try:
318 for kind in state:
318 for kind in state:
319 for node in state[kind]:
319 for node in state[kind]:
320 f.write("%s %s\n" % (kind, hex(node)))
320 f.write("%s %s\n" % (kind, hex(node)))
321 f.rename()
321 f.rename()
322 finally:
322 finally:
323 del wlock
323 del wlock
324
324
325 if not state['good'] or not state['bad']:
325 if not state['good'] or not state['bad']:
326 return
326 return
327
327
328 # actually bisect
328 # actually bisect
329 node, changesets, good = hbisect.bisect(repo.changelog, state)
329 node, changesets, good = hbisect.bisect(repo.changelog, state)
330 if changesets == 0:
330 if changesets == 0:
331 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
331 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
332 displayer = cmdutil.show_changeset(ui, repo, {})
332 displayer = cmdutil.show_changeset(ui, repo, {})
333 displayer.show(changenode=node)
333 displayer.show(changenode=node)
334 elif node is not None:
334 elif node is not None:
335 # compute the approximate number of remaining tests
335 # compute the approximate number of remaining tests
336 tests, size = 0, 2
336 tests, size = 0, 2
337 while size <= changesets:
337 while size <= changesets:
338 tests, size = tests + 1, size * 2
338 tests, size = tests + 1, size * 2
339 rev = repo.changelog.rev(node)
339 rev = repo.changelog.rev(node)
340 ui.write(_("Testing changeset %s:%s "
340 ui.write(_("Testing changeset %s:%s "
341 "(%s changesets remaining, ~%s tests)\n")
341 "(%s changesets remaining, ~%s tests)\n")
342 % (rev, short(node), changesets, tests))
342 % (rev, short(node), changesets, tests))
343 if not noupdate:
343 if not noupdate:
344 cmdutil.bail_if_changed(repo)
344 cmdutil.bail_if_changed(repo)
345 return hg.clean(repo, node)
345 return hg.clean(repo, node)
346
346
347 def branch(ui, repo, label=None, **opts):
347 def branch(ui, repo, label=None, **opts):
348 """set or show the current branch name
348 """set or show the current branch name
349
349
350 With no argument, show the current branch name. With one argument,
350 With no argument, show the current branch name. With one argument,
351 set the working directory branch name (the branch does not exist in
351 set the working directory branch name (the branch does not exist in
352 the repository until the next commit).
352 the repository until the next commit).
353
353
354 Unless --force is specified, branch will not let you set a
354 Unless --force is specified, branch will not let you set a
355 branch name that shadows an existing branch.
355 branch name that shadows an existing branch.
356
356
357 Use the command 'hg update' to switch to an existing branch.
357 Use the command 'hg update' to switch to an existing branch.
358 """
358 """
359
359
360 if label:
360 if label:
361 if not opts.get('force') and label in repo.branchtags():
361 if not opts.get('force') and label in repo.branchtags():
362 if label not in [p.branch() for p in repo.workingctx().parents()]:
362 if label not in [p.branch() for p in repo.workingctx().parents()]:
363 raise util.Abort(_('a branch of the same name already exists'
363 raise util.Abort(_('a branch of the same name already exists'
364 ' (use --force to override)'))
364 ' (use --force to override)'))
365 repo.dirstate.setbranch(util.fromlocal(label))
365 repo.dirstate.setbranch(util.fromlocal(label))
366 ui.status(_('marked working directory as branch %s\n') % label)
366 ui.status(_('marked working directory as branch %s\n') % label)
367 else:
367 else:
368 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
368 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
369
369
370 def branches(ui, repo, active=False):
370 def branches(ui, repo, active=False):
371 """list repository named branches
371 """list repository named branches
372
372
373 List the repository's named branches, indicating which ones are
373 List the repository's named branches, indicating which ones are
374 inactive. If active is specified, only show active branches.
374 inactive. If active is specified, only show active branches.
375
375
376 A branch is considered active if it contains unmerged heads.
376 A branch is considered active if it contains unmerged heads.
377
377
378 Use the command 'hg update' to switch to an existing branch.
378 Use the command 'hg update' to switch to an existing branch.
379 """
379 """
380 b = repo.branchtags()
380 b = repo.branchtags()
381 heads = dict.fromkeys(repo.heads(), 1)
381 heads = dict.fromkeys(repo.heads(), 1)
382 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
382 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
383 l.sort()
383 l.sort()
384 l.reverse()
384 l.reverse()
385 for ishead, r, n, t in l:
385 for ishead, r, n, t in l:
386 if active and not ishead:
386 if active and not ishead:
387 # If we're only displaying active branches, abort the loop on
387 # If we're only displaying active branches, abort the loop on
388 # encountering the first inactive head
388 # encountering the first inactive head
389 break
389 break
390 else:
390 else:
391 hexfunc = ui.debugflag and hex or short
391 hexfunc = ui.debugflag and hex or short
392 if ui.quiet:
392 if ui.quiet:
393 ui.write("%s\n" % t)
393 ui.write("%s\n" % t)
394 else:
394 else:
395 spaces = " " * (30 - util.locallen(t))
395 spaces = " " * (30 - util.locallen(t))
396 # The code only gets here if inactive branches are being
396 # The code only gets here if inactive branches are being
397 # displayed or the branch is active.
397 # displayed or the branch is active.
398 isinactive = ((not ishead) and " (inactive)") or ''
398 isinactive = ((not ishead) and " (inactive)") or ''
399 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
399 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
400
400
401 def bundle(ui, repo, fname, dest=None, **opts):
401 def bundle(ui, repo, fname, dest=None, **opts):
402 """create a changegroup file
402 """create a changegroup file
403
403
404 Generate a compressed changegroup file collecting changesets not
404 Generate a compressed changegroup file collecting changesets not
405 found in the other repository.
405 found in the other repository.
406
406
407 If no destination repository is specified the destination is
407 If no destination repository is specified the destination is
408 assumed to have all the nodes specified by one or more --base
408 assumed to have all the nodes specified by one or more --base
409 parameters. To create a bundle containing all changesets, use
409 parameters. To create a bundle containing all changesets, use
410 --all (or --base null). To change the compression method applied,
410 --all (or --base null). To change the compression method applied,
411 use the -t option (by default, bundles are compressed using bz2).
411 use the -t option (by default, bundles are compressed using bz2).
412
412
413 The bundle file can then be transferred using conventional means and
413 The bundle file can then be transferred using conventional means and
414 applied to another repository with the unbundle or pull command.
414 applied to another repository with the unbundle or pull command.
415 This is useful when direct push and pull are not available or when
415 This is useful when direct push and pull are not available or when
416 exporting an entire repository is undesirable.
416 exporting an entire repository is undesirable.
417
417
418 Applying bundles preserves all changeset contents including
418 Applying bundles preserves all changeset contents including
419 permissions, copy/rename information, and revision history.
419 permissions, copy/rename information, and revision history.
420 """
420 """
421 revs = opts.get('rev') or None
421 revs = opts.get('rev') or None
422 if revs:
422 if revs:
423 revs = [repo.lookup(rev) for rev in revs]
423 revs = [repo.lookup(rev) for rev in revs]
424 if opts.get('all'):
424 if opts.get('all'):
425 base = ['null']
425 base = ['null']
426 else:
426 else:
427 base = opts.get('base')
427 base = opts.get('base')
428 if base:
428 if base:
429 if dest:
429 if dest:
430 raise util.Abort(_("--base is incompatible with specifiying "
430 raise util.Abort(_("--base is incompatible with specifiying "
431 "a destination"))
431 "a destination"))
432 base = [repo.lookup(rev) for rev in base]
432 base = [repo.lookup(rev) for rev in base]
433 # create the right base
433 # create the right base
434 # XXX: nodesbetween / changegroup* should be "fixed" instead
434 # XXX: nodesbetween / changegroup* should be "fixed" instead
435 o = []
435 o = []
436 has = {nullid: None}
436 has = {nullid: None}
437 for n in base:
437 for n in base:
438 has.update(repo.changelog.reachable(n))
438 has.update(repo.changelog.reachable(n))
439 if revs:
439 if revs:
440 visit = list(revs)
440 visit = list(revs)
441 else:
441 else:
442 visit = repo.changelog.heads()
442 visit = repo.changelog.heads()
443 seen = {}
443 seen = {}
444 while visit:
444 while visit:
445 n = visit.pop(0)
445 n = visit.pop(0)
446 parents = [p for p in repo.changelog.parents(n) if p not in has]
446 parents = [p for p in repo.changelog.parents(n) if p not in has]
447 if len(parents) == 0:
447 if len(parents) == 0:
448 o.insert(0, n)
448 o.insert(0, n)
449 else:
449 else:
450 for p in parents:
450 for p in parents:
451 if p not in seen:
451 if p not in seen:
452 seen[p] = 1
452 seen[p] = 1
453 visit.append(p)
453 visit.append(p)
454 else:
454 else:
455 cmdutil.setremoteconfig(ui, opts)
455 cmdutil.setremoteconfig(ui, opts)
456 dest, revs, checkout = hg.parseurl(
456 dest, revs, checkout = hg.parseurl(
457 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
457 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
458 other = hg.repository(ui, dest)
458 other = hg.repository(ui, dest)
459 o = repo.findoutgoing(other, force=opts['force'])
459 o = repo.findoutgoing(other, force=opts['force'])
460
460
461 if revs:
461 if revs:
462 cg = repo.changegroupsubset(o, revs, 'bundle')
462 cg = repo.changegroupsubset(o, revs, 'bundle')
463 else:
463 else:
464 cg = repo.changegroup(o, 'bundle')
464 cg = repo.changegroup(o, 'bundle')
465
465
466 bundletype = opts.get('type', 'bzip2').lower()
466 bundletype = opts.get('type', 'bzip2').lower()
467 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
467 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
468 bundletype = btypes.get(bundletype)
468 bundletype = btypes.get(bundletype)
469 if bundletype not in changegroup.bundletypes:
469 if bundletype not in changegroup.bundletypes:
470 raise util.Abort(_('unknown bundle type specified with --type'))
470 raise util.Abort(_('unknown bundle type specified with --type'))
471
471
472 changegroup.writebundle(cg, fname, bundletype)
472 changegroup.writebundle(cg, fname, bundletype)
473
473
474 def cat(ui, repo, file1, *pats, **opts):
474 def cat(ui, repo, file1, *pats, **opts):
475 """output the current or given revision of files
475 """output the current or given revision of files
476
476
477 Print the specified files as they were at the given revision.
477 Print the specified files as they were at the given revision.
478 If no revision is given, the parent of the working directory is used,
478 If no revision is given, the parent of the working directory is used,
479 or tip if no revision is checked out.
479 or tip if no revision is checked out.
480
480
481 Output may be to a file, in which case the name of the file is
481 Output may be to a file, in which case the name of the file is
482 given using a format string. The formatting rules are the same as
482 given using a format string. The formatting rules are the same as
483 for the export command, with the following additions:
483 for the export command, with the following additions:
484
484
485 %s basename of file being printed
485 %s basename of file being printed
486 %d dirname of file being printed, or '.' if in repo root
486 %d dirname of file being printed, or '.' if in repo root
487 %p root-relative path name of file being printed
487 %p root-relative path name of file being printed
488 """
488 """
489 ctx = repo.changectx(opts['rev'])
489 ctx = repo.changectx(opts['rev'])
490 err = 1
490 err = 1
491 m = cmdutil.match(repo, (file1,) + pats, opts)
491 m = cmdutil.match(repo, (file1,) + pats, opts)
492 for abs in repo.walk(m, ctx.node()):
492 for abs in repo.walk(m, ctx.node()):
493 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
493 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
494 data = ctx.filectx(abs).data()
494 data = ctx.filectx(abs).data()
495 if opts.get('decode'):
495 if opts.get('decode'):
496 data = repo.wwritedata(abs, data)
496 data = repo.wwritedata(abs, data)
497 fp.write(data)
497 fp.write(data)
498 err = 0
498 err = 0
499 return err
499 return err
500
500
501 def clone(ui, source, dest=None, **opts):
501 def clone(ui, source, dest=None, **opts):
502 """make a copy of an existing repository
502 """make a copy of an existing repository
503
503
504 Create a copy of an existing repository in a new directory.
504 Create a copy of an existing repository in a new directory.
505
505
506 If no destination directory name is specified, it defaults to the
506 If no destination directory name is specified, it defaults to the
507 basename of the source.
507 basename of the source.
508
508
509 The location of the source is added to the new repository's
509 The location of the source is added to the new repository's
510 .hg/hgrc file, as the default to be used for future pulls.
510 .hg/hgrc file, as the default to be used for future pulls.
511
511
512 For efficiency, hardlinks are used for cloning whenever the source
512 For efficiency, hardlinks are used for cloning whenever the source
513 and destination are on the same filesystem (note this applies only
513 and destination are on the same filesystem (note this applies only
514 to the repository data, not to the checked out files). Some
514 to the repository data, not to the checked out files). Some
515 filesystems, such as AFS, implement hardlinking incorrectly, but
515 filesystems, such as AFS, implement hardlinking incorrectly, but
516 do not report errors. In these cases, use the --pull option to
516 do not report errors. In these cases, use the --pull option to
517 avoid hardlinking.
517 avoid hardlinking.
518
518
519 In some cases, you can clone repositories and checked out files
519 In some cases, you can clone repositories and checked out files
520 using full hardlinks with
520 using full hardlinks with
521
521
522 $ cp -al REPO REPOCLONE
522 $ cp -al REPO REPOCLONE
523
523
524 This is the fastest way to clone, but it is not always safe. The
524 This is the fastest way to clone, but it is not always safe. The
525 operation is not atomic (making sure REPO is not modified during
525 operation is not atomic (making sure REPO is not modified during
526 the operation is up to you) and you have to make sure your editor
526 the operation is up to you) and you have to make sure your editor
527 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
527 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
528 this is not compatible with certain extensions that place their
528 this is not compatible with certain extensions that place their
529 metadata under the .hg directory, such as mq.
529 metadata under the .hg directory, such as mq.
530
530
531 If you use the -r option to clone up to a specific revision, no
531 If you use the -r option to clone up to a specific revision, no
532 subsequent revisions will be present in the cloned repository.
532 subsequent revisions will be present in the cloned repository.
533 This option implies --pull, even on local repositories.
533 This option implies --pull, even on local repositories.
534
534
535 See pull for valid source format details.
535 See pull for valid source format details.
536
536
537 It is possible to specify an ssh:// URL as the destination, but no
537 It is possible to specify an ssh:// URL as the destination, but no
538 .hg/hgrc and working directory will be created on the remote side.
538 .hg/hgrc and working directory will be created on the remote side.
539 Look at the help text for the pull command for important details
539 Look at the help text for the pull command for important details
540 about ssh:// URLs.
540 about ssh:// URLs.
541 """
541 """
542 cmdutil.setremoteconfig(ui, opts)
542 cmdutil.setremoteconfig(ui, opts)
543 hg.clone(ui, source, dest,
543 hg.clone(ui, source, dest,
544 pull=opts['pull'],
544 pull=opts['pull'],
545 stream=opts['uncompressed'],
545 stream=opts['uncompressed'],
546 rev=opts['rev'],
546 rev=opts['rev'],
547 update=not opts['noupdate'])
547 update=not opts['noupdate'])
548
548
549 def commit(ui, repo, *pats, **opts):
549 def commit(ui, repo, *pats, **opts):
550 """commit the specified files or all outstanding changes
550 """commit the specified files or all outstanding changes
551
551
552 Commit changes to the given files into the repository.
552 Commit changes to the given files into the repository.
553
553
554 If a list of files is omitted, all changes reported by "hg status"
554 If a list of files is omitted, all changes reported by "hg status"
555 will be committed.
555 will be committed.
556
556
557 If you are committing the result of a merge, do not provide any
557 If you are committing the result of a merge, do not provide any
558 file names or -I/-X filters.
558 file names or -I/-X filters.
559
559
560 If no commit message is specified, the configured editor is started to
560 If no commit message is specified, the configured editor is started to
561 enter a message.
561 enter a message.
562
562
563 See 'hg help dates' for a list of formats valid for -d/--date.
563 See 'hg help dates' for a list of formats valid for -d/--date.
564 """
564 """
565 def commitfunc(ui, repo, files, message, match, opts):
565 def commitfunc(ui, repo, message, match, opts):
566 return repo.commit(files, message, opts['user'], opts['date'], match,
566 return repo.commit(match.files(), message, opts['user'], opts['date'],
567 force_editor=opts.get('force_editor'))
567 match, force_editor=opts.get('force_editor'))
568
568
569 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
569 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
570 if not node:
570 if not node:
571 return
571 return
572 cl = repo.changelog
572 cl = repo.changelog
573 rev = cl.rev(node)
573 rev = cl.rev(node)
574 parents = cl.parentrevs(rev)
574 parents = cl.parentrevs(rev)
575 if rev - 1 in parents:
575 if rev - 1 in parents:
576 # one of the parents was the old tip
576 # one of the parents was the old tip
577 return
577 return
578 if (parents == (nullrev, nullrev) or
578 if (parents == (nullrev, nullrev) or
579 len(cl.heads(cl.node(parents[0]))) > 1 and
579 len(cl.heads(cl.node(parents[0]))) > 1 and
580 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
580 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
581 ui.status(_('created new head\n'))
581 ui.status(_('created new head\n'))
582
582
583 def copy(ui, repo, *pats, **opts):
583 def copy(ui, repo, *pats, **opts):
584 """mark files as copied for the next commit
584 """mark files as copied for the next commit
585
585
586 Mark dest as having copies of source files. If dest is a
586 Mark dest as having copies of source files. If dest is a
587 directory, copies are put in that directory. If dest is a file,
587 directory, copies are put in that directory. If dest is a file,
588 there can only be one source.
588 there can only be one source.
589
589
590 By default, this command copies the contents of files as they
590 By default, this command copies the contents of files as they
591 stand in the working directory. If invoked with --after, the
591 stand in the working directory. If invoked with --after, the
592 operation is recorded, but no copying is performed.
592 operation is recorded, but no copying is performed.
593
593
594 This command takes effect in the next commit. To undo a copy
594 This command takes effect in the next commit. To undo a copy
595 before that, see hg revert.
595 before that, see hg revert.
596 """
596 """
597 wlock = repo.wlock(False)
597 wlock = repo.wlock(False)
598 try:
598 try:
599 return cmdutil.copy(ui, repo, pats, opts)
599 return cmdutil.copy(ui, repo, pats, opts)
600 finally:
600 finally:
601 del wlock
601 del wlock
602
602
603 def debugancestor(ui, repo, *args):
603 def debugancestor(ui, repo, *args):
604 """find the ancestor revision of two revisions in a given index"""
604 """find the ancestor revision of two revisions in a given index"""
605 if len(args) == 3:
605 if len(args) == 3:
606 index, rev1, rev2 = args
606 index, rev1, rev2 = args
607 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
607 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
608 lookup = r.lookup
608 lookup = r.lookup
609 elif len(args) == 2:
609 elif len(args) == 2:
610 if not repo:
610 if not repo:
611 raise util.Abort(_("There is no Mercurial repository here "
611 raise util.Abort(_("There is no Mercurial repository here "
612 "(.hg not found)"))
612 "(.hg not found)"))
613 rev1, rev2 = args
613 rev1, rev2 = args
614 r = repo.changelog
614 r = repo.changelog
615 lookup = repo.lookup
615 lookup = repo.lookup
616 else:
616 else:
617 raise util.Abort(_('either two or three arguments required'))
617 raise util.Abort(_('either two or three arguments required'))
618 a = r.ancestor(lookup(rev1), lookup(rev2))
618 a = r.ancestor(lookup(rev1), lookup(rev2))
619 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
619 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
620
620
621 def debugcomplete(ui, cmd='', **opts):
621 def debugcomplete(ui, cmd='', **opts):
622 """returns the completion list associated with the given command"""
622 """returns the completion list associated with the given command"""
623
623
624 if opts['options']:
624 if opts['options']:
625 options = []
625 options = []
626 otables = [globalopts]
626 otables = [globalopts]
627 if cmd:
627 if cmd:
628 aliases, entry = cmdutil.findcmd(ui, cmd, table)
628 aliases, entry = cmdutil.findcmd(ui, cmd, table)
629 otables.append(entry[1])
629 otables.append(entry[1])
630 for t in otables:
630 for t in otables:
631 for o in t:
631 for o in t:
632 if o[0]:
632 if o[0]:
633 options.append('-%s' % o[0])
633 options.append('-%s' % o[0])
634 options.append('--%s' % o[1])
634 options.append('--%s' % o[1])
635 ui.write("%s\n" % "\n".join(options))
635 ui.write("%s\n" % "\n".join(options))
636 return
636 return
637
637
638 clist = cmdutil.findpossible(ui, cmd, table).keys()
638 clist = cmdutil.findpossible(ui, cmd, table).keys()
639 clist.sort()
639 clist.sort()
640 ui.write("%s\n" % "\n".join(clist))
640 ui.write("%s\n" % "\n".join(clist))
641
641
642 def debugfsinfo(ui, path = "."):
642 def debugfsinfo(ui, path = "."):
643 file('.debugfsinfo', 'w').write('')
643 file('.debugfsinfo', 'w').write('')
644 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
644 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
645 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
645 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
646 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
646 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
647 and 'yes' or 'no'))
647 and 'yes' or 'no'))
648 os.unlink('.debugfsinfo')
648 os.unlink('.debugfsinfo')
649
649
650 def debugrebuildstate(ui, repo, rev=""):
650 def debugrebuildstate(ui, repo, rev=""):
651 """rebuild the dirstate as it would look like for the given revision"""
651 """rebuild the dirstate as it would look like for the given revision"""
652 if rev == "":
652 if rev == "":
653 rev = repo.changelog.tip()
653 rev = repo.changelog.tip()
654 ctx = repo.changectx(rev)
654 ctx = repo.changectx(rev)
655 files = ctx.manifest()
655 files = ctx.manifest()
656 wlock = repo.wlock()
656 wlock = repo.wlock()
657 try:
657 try:
658 repo.dirstate.rebuild(rev, files)
658 repo.dirstate.rebuild(rev, files)
659 finally:
659 finally:
660 del wlock
660 del wlock
661
661
662 def debugcheckstate(ui, repo):
662 def debugcheckstate(ui, repo):
663 """validate the correctness of the current dirstate"""
663 """validate the correctness of the current dirstate"""
664 parent1, parent2 = repo.dirstate.parents()
664 parent1, parent2 = repo.dirstate.parents()
665 m1 = repo.changectx(parent1).manifest()
665 m1 = repo.changectx(parent1).manifest()
666 m2 = repo.changectx(parent2).manifest()
666 m2 = repo.changectx(parent2).manifest()
667 errors = 0
667 errors = 0
668 for f in repo.dirstate:
668 for f in repo.dirstate:
669 state = repo.dirstate[f]
669 state = repo.dirstate[f]
670 if state in "nr" and f not in m1:
670 if state in "nr" and f not in m1:
671 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
671 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
672 errors += 1
672 errors += 1
673 if state in "a" and f in m1:
673 if state in "a" and f in m1:
674 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
674 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
675 errors += 1
675 errors += 1
676 if state in "m" and f not in m1 and f not in m2:
676 if state in "m" and f not in m1 and f not in m2:
677 ui.warn(_("%s in state %s, but not in either manifest\n") %
677 ui.warn(_("%s in state %s, but not in either manifest\n") %
678 (f, state))
678 (f, state))
679 errors += 1
679 errors += 1
680 for f in m1:
680 for f in m1:
681 state = repo.dirstate[f]
681 state = repo.dirstate[f]
682 if state not in "nrm":
682 if state not in "nrm":
683 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
683 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
684 errors += 1
684 errors += 1
685 if errors:
685 if errors:
686 error = _(".hg/dirstate inconsistent with current parent's manifest")
686 error = _(".hg/dirstate inconsistent with current parent's manifest")
687 raise util.Abort(error)
687 raise util.Abort(error)
688
688
689 def showconfig(ui, repo, *values, **opts):
689 def showconfig(ui, repo, *values, **opts):
690 """show combined config settings from all hgrc files
690 """show combined config settings from all hgrc files
691
691
692 With no args, print names and values of all config items.
692 With no args, print names and values of all config items.
693
693
694 With one arg of the form section.name, print just the value of
694 With one arg of the form section.name, print just the value of
695 that config item.
695 that config item.
696
696
697 With multiple args, print names and values of all config items
697 With multiple args, print names and values of all config items
698 with matching section names."""
698 with matching section names."""
699
699
700 untrusted = bool(opts.get('untrusted'))
700 untrusted = bool(opts.get('untrusted'))
701 if values:
701 if values:
702 if len([v for v in values if '.' in v]) > 1:
702 if len([v for v in values if '.' in v]) > 1:
703 raise util.Abort(_('only one config item permitted'))
703 raise util.Abort(_('only one config item permitted'))
704 for section, name, value in ui.walkconfig(untrusted=untrusted):
704 for section, name, value in ui.walkconfig(untrusted=untrusted):
705 sectname = section + '.' + name
705 sectname = section + '.' + name
706 if values:
706 if values:
707 for v in values:
707 for v in values:
708 if v == section:
708 if v == section:
709 ui.write('%s=%s\n' % (sectname, value))
709 ui.write('%s=%s\n' % (sectname, value))
710 elif v == sectname:
710 elif v == sectname:
711 ui.write(value, '\n')
711 ui.write(value, '\n')
712 else:
712 else:
713 ui.write('%s=%s\n' % (sectname, value))
713 ui.write('%s=%s\n' % (sectname, value))
714
714
715 def debugsetparents(ui, repo, rev1, rev2=None):
715 def debugsetparents(ui, repo, rev1, rev2=None):
716 """manually set the parents of the current working directory
716 """manually set the parents of the current working directory
717
717
718 This is useful for writing repository conversion tools, but should
718 This is useful for writing repository conversion tools, but should
719 be used with care.
719 be used with care.
720 """
720 """
721
721
722 if not rev2:
722 if not rev2:
723 rev2 = hex(nullid)
723 rev2 = hex(nullid)
724
724
725 wlock = repo.wlock()
725 wlock = repo.wlock()
726 try:
726 try:
727 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
727 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
728 finally:
728 finally:
729 del wlock
729 del wlock
730
730
731 def debugstate(ui, repo, nodates=None):
731 def debugstate(ui, repo, nodates=None):
732 """show the contents of the current dirstate"""
732 """show the contents of the current dirstate"""
733 k = repo.dirstate._map.items()
733 k = repo.dirstate._map.items()
734 k.sort()
734 k.sort()
735 timestr = ""
735 timestr = ""
736 showdate = not nodates
736 showdate = not nodates
737 for file_, ent in k:
737 for file_, ent in k:
738 if showdate:
738 if showdate:
739 if ent[3] == -1:
739 if ent[3] == -1:
740 # Pad or slice to locale representation
740 # Pad or slice to locale representation
741 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
741 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
742 timestr = 'unset'
742 timestr = 'unset'
743 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
743 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
744 else:
744 else:
745 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
745 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
746 if ent[1] & 020000:
746 if ent[1] & 020000:
747 mode = 'lnk'
747 mode = 'lnk'
748 else:
748 else:
749 mode = '%3o' % (ent[1] & 0777)
749 mode = '%3o' % (ent[1] & 0777)
750 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
750 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
751 for f in repo.dirstate.copies():
751 for f in repo.dirstate.copies():
752 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
752 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
753
753
754 def debugdata(ui, file_, rev):
754 def debugdata(ui, file_, rev):
755 """dump the contents of a data file revision"""
755 """dump the contents of a data file revision"""
756 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
756 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
757 try:
757 try:
758 ui.write(r.revision(r.lookup(rev)))
758 ui.write(r.revision(r.lookup(rev)))
759 except KeyError:
759 except KeyError:
760 raise util.Abort(_('invalid revision identifier %s') % rev)
760 raise util.Abort(_('invalid revision identifier %s') % rev)
761
761
762 def debugdate(ui, date, range=None, **opts):
762 def debugdate(ui, date, range=None, **opts):
763 """parse and display a date"""
763 """parse and display a date"""
764 if opts["extended"]:
764 if opts["extended"]:
765 d = util.parsedate(date, util.extendeddateformats)
765 d = util.parsedate(date, util.extendeddateformats)
766 else:
766 else:
767 d = util.parsedate(date)
767 d = util.parsedate(date)
768 ui.write("internal: %s %s\n" % d)
768 ui.write("internal: %s %s\n" % d)
769 ui.write("standard: %s\n" % util.datestr(d))
769 ui.write("standard: %s\n" % util.datestr(d))
770 if range:
770 if range:
771 m = util.matchdate(range)
771 m = util.matchdate(range)
772 ui.write("match: %s\n" % m(d[0]))
772 ui.write("match: %s\n" % m(d[0]))
773
773
774 def debugindex(ui, file_):
774 def debugindex(ui, file_):
775 """dump the contents of an index file"""
775 """dump the contents of an index file"""
776 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
776 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
777 ui.write(" rev offset length base linkrev" +
777 ui.write(" rev offset length base linkrev" +
778 " nodeid p1 p2\n")
778 " nodeid p1 p2\n")
779 for i in xrange(r.count()):
779 for i in xrange(r.count()):
780 node = r.node(i)
780 node = r.node(i)
781 try:
781 try:
782 pp = r.parents(node)
782 pp = r.parents(node)
783 except:
783 except:
784 pp = [nullid, nullid]
784 pp = [nullid, nullid]
785 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
785 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
786 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
786 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
787 short(node), short(pp[0]), short(pp[1])))
787 short(node), short(pp[0]), short(pp[1])))
788
788
789 def debugindexdot(ui, file_):
789 def debugindexdot(ui, file_):
790 """dump an index DAG as a .dot file"""
790 """dump an index DAG as a .dot file"""
791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
792 ui.write("digraph G {\n")
792 ui.write("digraph G {\n")
793 for i in xrange(r.count()):
793 for i in xrange(r.count()):
794 node = r.node(i)
794 node = r.node(i)
795 pp = r.parents(node)
795 pp = r.parents(node)
796 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
796 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
797 if pp[1] != nullid:
797 if pp[1] != nullid:
798 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
798 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
799 ui.write("}\n")
799 ui.write("}\n")
800
800
801 def debuginstall(ui):
801 def debuginstall(ui):
802 '''test Mercurial installation'''
802 '''test Mercurial installation'''
803
803
804 def writetemp(contents):
804 def writetemp(contents):
805 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
805 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
806 f = os.fdopen(fd, "wb")
806 f = os.fdopen(fd, "wb")
807 f.write(contents)
807 f.write(contents)
808 f.close()
808 f.close()
809 return name
809 return name
810
810
811 problems = 0
811 problems = 0
812
812
813 # encoding
813 # encoding
814 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
814 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
815 try:
815 try:
816 util.fromlocal("test")
816 util.fromlocal("test")
817 except util.Abort, inst:
817 except util.Abort, inst:
818 ui.write(" %s\n" % inst)
818 ui.write(" %s\n" % inst)
819 ui.write(_(" (check that your locale is properly set)\n"))
819 ui.write(_(" (check that your locale is properly set)\n"))
820 problems += 1
820 problems += 1
821
821
822 # compiled modules
822 # compiled modules
823 ui.status(_("Checking extensions...\n"))
823 ui.status(_("Checking extensions...\n"))
824 try:
824 try:
825 import bdiff, mpatch, base85
825 import bdiff, mpatch, base85
826 except Exception, inst:
826 except Exception, inst:
827 ui.write(" %s\n" % inst)
827 ui.write(" %s\n" % inst)
828 ui.write(_(" One or more extensions could not be found"))
828 ui.write(_(" One or more extensions could not be found"))
829 ui.write(_(" (check that you compiled the extensions)\n"))
829 ui.write(_(" (check that you compiled the extensions)\n"))
830 problems += 1
830 problems += 1
831
831
832 # templates
832 # templates
833 ui.status(_("Checking templates...\n"))
833 ui.status(_("Checking templates...\n"))
834 try:
834 try:
835 import templater
835 import templater
836 t = templater.templater(templater.templatepath("map-cmdline.default"))
836 t = templater.templater(templater.templatepath("map-cmdline.default"))
837 except Exception, inst:
837 except Exception, inst:
838 ui.write(" %s\n" % inst)
838 ui.write(" %s\n" % inst)
839 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
839 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
840 problems += 1
840 problems += 1
841
841
842 # patch
842 # patch
843 ui.status(_("Checking patch...\n"))
843 ui.status(_("Checking patch...\n"))
844 patchproblems = 0
844 patchproblems = 0
845 a = "1\n2\n3\n4\n"
845 a = "1\n2\n3\n4\n"
846 b = "1\n2\n3\ninsert\n4\n"
846 b = "1\n2\n3\ninsert\n4\n"
847 fa = writetemp(a)
847 fa = writetemp(a)
848 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
848 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
849 os.path.basename(fa))
849 os.path.basename(fa))
850 fd = writetemp(d)
850 fd = writetemp(d)
851
851
852 files = {}
852 files = {}
853 try:
853 try:
854 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
854 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
855 except util.Abort, e:
855 except util.Abort, e:
856 ui.write(_(" patch call failed:\n"))
856 ui.write(_(" patch call failed:\n"))
857 ui.write(" " + str(e) + "\n")
857 ui.write(" " + str(e) + "\n")
858 patchproblems += 1
858 patchproblems += 1
859 else:
859 else:
860 if list(files) != [os.path.basename(fa)]:
860 if list(files) != [os.path.basename(fa)]:
861 ui.write(_(" unexpected patch output!\n"))
861 ui.write(_(" unexpected patch output!\n"))
862 patchproblems += 1
862 patchproblems += 1
863 a = file(fa).read()
863 a = file(fa).read()
864 if a != b:
864 if a != b:
865 ui.write(_(" patch test failed!\n"))
865 ui.write(_(" patch test failed!\n"))
866 patchproblems += 1
866 patchproblems += 1
867
867
868 if patchproblems:
868 if patchproblems:
869 if ui.config('ui', 'patch'):
869 if ui.config('ui', 'patch'):
870 ui.write(_(" (Current patch tool may be incompatible with patch,"
870 ui.write(_(" (Current patch tool may be incompatible with patch,"
871 " or misconfigured. Please check your .hgrc file)\n"))
871 " or misconfigured. Please check your .hgrc file)\n"))
872 else:
872 else:
873 ui.write(_(" Internal patcher failure, please report this error"
873 ui.write(_(" Internal patcher failure, please report this error"
874 " to http://www.selenic.com/mercurial/bts\n"))
874 " to http://www.selenic.com/mercurial/bts\n"))
875 problems += patchproblems
875 problems += patchproblems
876
876
877 os.unlink(fa)
877 os.unlink(fa)
878 os.unlink(fd)
878 os.unlink(fd)
879
879
880 # editor
880 # editor
881 ui.status(_("Checking commit editor...\n"))
881 ui.status(_("Checking commit editor...\n"))
882 editor = ui.geteditor()
882 editor = ui.geteditor()
883 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
883 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
884 if not cmdpath:
884 if not cmdpath:
885 if editor == 'vi':
885 if editor == 'vi':
886 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
886 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
887 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
887 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
888 else:
888 else:
889 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
889 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
890 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
890 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
891 problems += 1
891 problems += 1
892
892
893 # check username
893 # check username
894 ui.status(_("Checking username...\n"))
894 ui.status(_("Checking username...\n"))
895 user = os.environ.get("HGUSER")
895 user = os.environ.get("HGUSER")
896 if user is None:
896 if user is None:
897 user = ui.config("ui", "username")
897 user = ui.config("ui", "username")
898 if user is None:
898 if user is None:
899 user = os.environ.get("EMAIL")
899 user = os.environ.get("EMAIL")
900 if not user:
900 if not user:
901 ui.warn(" ")
901 ui.warn(" ")
902 ui.username()
902 ui.username()
903 ui.write(_(" (specify a username in your .hgrc file)\n"))
903 ui.write(_(" (specify a username in your .hgrc file)\n"))
904
904
905 if not problems:
905 if not problems:
906 ui.status(_("No problems detected\n"))
906 ui.status(_("No problems detected\n"))
907 else:
907 else:
908 ui.write(_("%s problems detected,"
908 ui.write(_("%s problems detected,"
909 " please check your install!\n") % problems)
909 " please check your install!\n") % problems)
910
910
911 return problems
911 return problems
912
912
913 def debugrename(ui, repo, file1, *pats, **opts):
913 def debugrename(ui, repo, file1, *pats, **opts):
914 """dump rename information"""
914 """dump rename information"""
915
915
916 ctx = repo.changectx(opts.get('rev', 'tip'))
916 ctx = repo.changectx(opts.get('rev', 'tip'))
917 m = cmdutil.match(repo, (file1,) + pats, opts)
917 m = cmdutil.match(repo, (file1,) + pats, opts)
918 for abs in repo.walk(m, ctx.node()):
918 for abs in repo.walk(m, ctx.node()):
919 fctx = ctx.filectx(abs)
919 fctx = ctx.filectx(abs)
920 o = fctx.filelog().renamed(fctx.filenode())
920 o = fctx.filelog().renamed(fctx.filenode())
921 rel = m.rel(abs)
921 rel = m.rel(abs)
922 if o:
922 if o:
923 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
923 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
924 else:
924 else:
925 ui.write(_("%s not renamed\n") % rel)
925 ui.write(_("%s not renamed\n") % rel)
926
926
927 def debugwalk(ui, repo, *pats, **opts):
927 def debugwalk(ui, repo, *pats, **opts):
928 """show how files match on given patterns"""
928 """show how files match on given patterns"""
929 m = cmdutil.match(repo, pats, opts)
929 m = cmdutil.match(repo, pats, opts)
930 items = list(repo.walk(m))
930 items = list(repo.walk(m))
931 if not items:
931 if not items:
932 return
932 return
933 fmt = 'f %%-%ds %%-%ds %%s' % (
933 fmt = 'f %%-%ds %%-%ds %%s' % (
934 max([len(abs) for abs in items]),
934 max([len(abs) for abs in items]),
935 max([len(m.rel(abs)) for abs in items]))
935 max([len(m.rel(abs)) for abs in items]))
936 for abs in items:
936 for abs in items:
937 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
937 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
938 ui.write("%s\n" % line.rstrip())
938 ui.write("%s\n" % line.rstrip())
939
939
940 def diff(ui, repo, *pats, **opts):
940 def diff(ui, repo, *pats, **opts):
941 """diff repository (or selected files)
941 """diff repository (or selected files)
942
942
943 Show differences between revisions for the specified files.
943 Show differences between revisions for the specified files.
944
944
945 Differences between files are shown using the unified diff format.
945 Differences between files are shown using the unified diff format.
946
946
947 NOTE: diff may generate unexpected results for merges, as it will
947 NOTE: diff may generate unexpected results for merges, as it will
948 default to comparing against the working directory's first parent
948 default to comparing against the working directory's first parent
949 changeset if no revisions are specified.
949 changeset if no revisions are specified.
950
950
951 When two revision arguments are given, then changes are shown
951 When two revision arguments are given, then changes are shown
952 between those revisions. If only one revision is specified then
952 between those revisions. If only one revision is specified then
953 that revision is compared to the working directory, and, when no
953 that revision is compared to the working directory, and, when no
954 revisions are specified, the working directory files are compared
954 revisions are specified, the working directory files are compared
955 to its parent.
955 to its parent.
956
956
957 Without the -a option, diff will avoid generating diffs of files
957 Without the -a option, diff will avoid generating diffs of files
958 it detects as binary. With -a, diff will generate a diff anyway,
958 it detects as binary. With -a, diff will generate a diff anyway,
959 probably with undesirable results.
959 probably with undesirable results.
960 """
960 """
961 node1, node2 = cmdutil.revpair(repo, opts['rev'])
961 node1, node2 = cmdutil.revpair(repo, opts['rev'])
962
962
963 m = cmdutil.match(repo, pats, opts)
963 m = cmdutil.match(repo, pats, opts)
964 patch.diff(repo, node1, node2, m.files(), match=m,
964 patch.diff(repo, node1, node2, m.files(), match=m,
965 opts=patch.diffopts(ui, opts))
965 opts=patch.diffopts(ui, opts))
966
966
967 def export(ui, repo, *changesets, **opts):
967 def export(ui, repo, *changesets, **opts):
968 """dump the header and diffs for one or more changesets
968 """dump the header and diffs for one or more changesets
969
969
970 Print the changeset header and diffs for one or more revisions.
970 Print the changeset header and diffs for one or more revisions.
971
971
972 The information shown in the changeset header is: author,
972 The information shown in the changeset header is: author,
973 changeset hash, parent(s) and commit comment.
973 changeset hash, parent(s) and commit comment.
974
974
975 NOTE: export may generate unexpected diff output for merge changesets,
975 NOTE: export may generate unexpected diff output for merge changesets,
976 as it will compare the merge changeset against its first parent only.
976 as it will compare the merge changeset against its first parent only.
977
977
978 Output may be to a file, in which case the name of the file is
978 Output may be to a file, in which case the name of the file is
979 given using a format string. The formatting rules are as follows:
979 given using a format string. The formatting rules are as follows:
980
980
981 %% literal "%" character
981 %% literal "%" character
982 %H changeset hash (40 bytes of hexadecimal)
982 %H changeset hash (40 bytes of hexadecimal)
983 %N number of patches being generated
983 %N number of patches being generated
984 %R changeset revision number
984 %R changeset revision number
985 %b basename of the exporting repository
985 %b basename of the exporting repository
986 %h short-form changeset hash (12 bytes of hexadecimal)
986 %h short-form changeset hash (12 bytes of hexadecimal)
987 %n zero-padded sequence number, starting at 1
987 %n zero-padded sequence number, starting at 1
988 %r zero-padded changeset revision number
988 %r zero-padded changeset revision number
989
989
990 Without the -a option, export will avoid generating diffs of files
990 Without the -a option, export will avoid generating diffs of files
991 it detects as binary. With -a, export will generate a diff anyway,
991 it detects as binary. With -a, export will generate a diff anyway,
992 probably with undesirable results.
992 probably with undesirable results.
993
993
994 With the --switch-parent option, the diff will be against the second
994 With the --switch-parent option, the diff will be against the second
995 parent. It can be useful to review a merge.
995 parent. It can be useful to review a merge.
996 """
996 """
997 if not changesets:
997 if not changesets:
998 raise util.Abort(_("export requires at least one changeset"))
998 raise util.Abort(_("export requires at least one changeset"))
999 revs = cmdutil.revrange(repo, changesets)
999 revs = cmdutil.revrange(repo, changesets)
1000 if len(revs) > 1:
1000 if len(revs) > 1:
1001 ui.note(_('exporting patches:\n'))
1001 ui.note(_('exporting patches:\n'))
1002 else:
1002 else:
1003 ui.note(_('exporting patch:\n'))
1003 ui.note(_('exporting patch:\n'))
1004 patch.export(repo, revs, template=opts['output'],
1004 patch.export(repo, revs, template=opts['output'],
1005 switch_parent=opts['switch_parent'],
1005 switch_parent=opts['switch_parent'],
1006 opts=patch.diffopts(ui, opts))
1006 opts=patch.diffopts(ui, opts))
1007
1007
1008 def grep(ui, repo, pattern, *pats, **opts):
1008 def grep(ui, repo, pattern, *pats, **opts):
1009 """search for a pattern in specified files and revisions
1009 """search for a pattern in specified files and revisions
1010
1010
1011 Search revisions of files for a regular expression.
1011 Search revisions of files for a regular expression.
1012
1012
1013 This command behaves differently than Unix grep. It only accepts
1013 This command behaves differently than Unix grep. It only accepts
1014 Python/Perl regexps. It searches repository history, not the
1014 Python/Perl regexps. It searches repository history, not the
1015 working directory. It always prints the revision number in which
1015 working directory. It always prints the revision number in which
1016 a match appears.
1016 a match appears.
1017
1017
1018 By default, grep only prints output for the first revision of a
1018 By default, grep only prints output for the first revision of a
1019 file in which it finds a match. To get it to print every revision
1019 file in which it finds a match. To get it to print every revision
1020 that contains a change in match status ("-" for a match that
1020 that contains a change in match status ("-" for a match that
1021 becomes a non-match, or "+" for a non-match that becomes a match),
1021 becomes a non-match, or "+" for a non-match that becomes a match),
1022 use the --all flag.
1022 use the --all flag.
1023 """
1023 """
1024 reflags = 0
1024 reflags = 0
1025 if opts['ignore_case']:
1025 if opts['ignore_case']:
1026 reflags |= re.I
1026 reflags |= re.I
1027 try:
1027 try:
1028 regexp = re.compile(pattern, reflags)
1028 regexp = re.compile(pattern, reflags)
1029 except Exception, inst:
1029 except Exception, inst:
1030 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1030 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1031 return None
1031 return None
1032 sep, eol = ':', '\n'
1032 sep, eol = ':', '\n'
1033 if opts['print0']:
1033 if opts['print0']:
1034 sep = eol = '\0'
1034 sep = eol = '\0'
1035
1035
1036 fcache = {}
1036 fcache = {}
1037 def getfile(fn):
1037 def getfile(fn):
1038 if fn not in fcache:
1038 if fn not in fcache:
1039 fcache[fn] = repo.file(fn)
1039 fcache[fn] = repo.file(fn)
1040 return fcache[fn]
1040 return fcache[fn]
1041
1041
1042 def matchlines(body):
1042 def matchlines(body):
1043 begin = 0
1043 begin = 0
1044 linenum = 0
1044 linenum = 0
1045 while True:
1045 while True:
1046 match = regexp.search(body, begin)
1046 match = regexp.search(body, begin)
1047 if not match:
1047 if not match:
1048 break
1048 break
1049 mstart, mend = match.span()
1049 mstart, mend = match.span()
1050 linenum += body.count('\n', begin, mstart) + 1
1050 linenum += body.count('\n', begin, mstart) + 1
1051 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1051 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1052 lend = body.find('\n', mend)
1052 lend = body.find('\n', mend)
1053 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1053 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1054 begin = lend + 1
1054 begin = lend + 1
1055
1055
1056 class linestate(object):
1056 class linestate(object):
1057 def __init__(self, line, linenum, colstart, colend):
1057 def __init__(self, line, linenum, colstart, colend):
1058 self.line = line
1058 self.line = line
1059 self.linenum = linenum
1059 self.linenum = linenum
1060 self.colstart = colstart
1060 self.colstart = colstart
1061 self.colend = colend
1061 self.colend = colend
1062
1062
1063 def __hash__(self):
1063 def __hash__(self):
1064 return hash((self.linenum, self.line))
1064 return hash((self.linenum, self.line))
1065
1065
1066 def __eq__(self, other):
1066 def __eq__(self, other):
1067 return self.line == other.line
1067 return self.line == other.line
1068
1068
1069 matches = {}
1069 matches = {}
1070 copies = {}
1070 copies = {}
1071 def grepbody(fn, rev, body):
1071 def grepbody(fn, rev, body):
1072 matches[rev].setdefault(fn, [])
1072 matches[rev].setdefault(fn, [])
1073 m = matches[rev][fn]
1073 m = matches[rev][fn]
1074 for lnum, cstart, cend, line in matchlines(body):
1074 for lnum, cstart, cend, line in matchlines(body):
1075 s = linestate(line, lnum, cstart, cend)
1075 s = linestate(line, lnum, cstart, cend)
1076 m.append(s)
1076 m.append(s)
1077
1077
1078 def difflinestates(a, b):
1078 def difflinestates(a, b):
1079 sm = difflib.SequenceMatcher(None, a, b)
1079 sm = difflib.SequenceMatcher(None, a, b)
1080 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1080 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1081 if tag == 'insert':
1081 if tag == 'insert':
1082 for i in xrange(blo, bhi):
1082 for i in xrange(blo, bhi):
1083 yield ('+', b[i])
1083 yield ('+', b[i])
1084 elif tag == 'delete':
1084 elif tag == 'delete':
1085 for i in xrange(alo, ahi):
1085 for i in xrange(alo, ahi):
1086 yield ('-', a[i])
1086 yield ('-', a[i])
1087 elif tag == 'replace':
1087 elif tag == 'replace':
1088 for i in xrange(alo, ahi):
1088 for i in xrange(alo, ahi):
1089 yield ('-', a[i])
1089 yield ('-', a[i])
1090 for i in xrange(blo, bhi):
1090 for i in xrange(blo, bhi):
1091 yield ('+', b[i])
1091 yield ('+', b[i])
1092
1092
1093 prev = {}
1093 prev = {}
1094 def display(fn, rev, states, prevstates):
1094 def display(fn, rev, states, prevstates):
1095 datefunc = ui.quiet and util.shortdate or util.datestr
1095 datefunc = ui.quiet and util.shortdate or util.datestr
1096 found = False
1096 found = False
1097 filerevmatches = {}
1097 filerevmatches = {}
1098 r = prev.get(fn, -1)
1098 r = prev.get(fn, -1)
1099 if opts['all']:
1099 if opts['all']:
1100 iter = difflinestates(states, prevstates)
1100 iter = difflinestates(states, prevstates)
1101 else:
1101 else:
1102 iter = [('', l) for l in prevstates]
1102 iter = [('', l) for l in prevstates]
1103 for change, l in iter:
1103 for change, l in iter:
1104 cols = [fn, str(r)]
1104 cols = [fn, str(r)]
1105 if opts['line_number']:
1105 if opts['line_number']:
1106 cols.append(str(l.linenum))
1106 cols.append(str(l.linenum))
1107 if opts['all']:
1107 if opts['all']:
1108 cols.append(change)
1108 cols.append(change)
1109 if opts['user']:
1109 if opts['user']:
1110 cols.append(ui.shortuser(get(r)[1]))
1110 cols.append(ui.shortuser(get(r)[1]))
1111 if opts.get('date'):
1111 if opts.get('date'):
1112 cols.append(datefunc(get(r)[2]))
1112 cols.append(datefunc(get(r)[2]))
1113 if opts['files_with_matches']:
1113 if opts['files_with_matches']:
1114 c = (fn, r)
1114 c = (fn, r)
1115 if c in filerevmatches:
1115 if c in filerevmatches:
1116 continue
1116 continue
1117 filerevmatches[c] = 1
1117 filerevmatches[c] = 1
1118 else:
1118 else:
1119 cols.append(l.line)
1119 cols.append(l.line)
1120 ui.write(sep.join(cols), eol)
1120 ui.write(sep.join(cols), eol)
1121 found = True
1121 found = True
1122 return found
1122 return found
1123
1123
1124 fstate = {}
1124 fstate = {}
1125 skip = {}
1125 skip = {}
1126 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1126 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1127 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1127 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1128 found = False
1128 found = False
1129 follow = opts.get('follow')
1129 follow = opts.get('follow')
1130 for st, rev, fns in changeiter:
1130 for st, rev, fns in changeiter:
1131 if st == 'window':
1131 if st == 'window':
1132 matches.clear()
1132 matches.clear()
1133 elif st == 'add':
1133 elif st == 'add':
1134 ctx = repo.changectx(rev)
1134 ctx = repo.changectx(rev)
1135 matches[rev] = {}
1135 matches[rev] = {}
1136 for fn in fns:
1136 for fn in fns:
1137 if fn in skip:
1137 if fn in skip:
1138 continue
1138 continue
1139 try:
1139 try:
1140 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1140 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1141 fstate.setdefault(fn, [])
1141 fstate.setdefault(fn, [])
1142 if follow:
1142 if follow:
1143 copied = getfile(fn).renamed(ctx.filenode(fn))
1143 copied = getfile(fn).renamed(ctx.filenode(fn))
1144 if copied:
1144 if copied:
1145 copies.setdefault(rev, {})[fn] = copied[0]
1145 copies.setdefault(rev, {})[fn] = copied[0]
1146 except revlog.LookupError:
1146 except revlog.LookupError:
1147 pass
1147 pass
1148 elif st == 'iter':
1148 elif st == 'iter':
1149 states = matches[rev].items()
1149 states = matches[rev].items()
1150 states.sort()
1150 states.sort()
1151 for fn, m in states:
1151 for fn, m in states:
1152 copy = copies.get(rev, {}).get(fn)
1152 copy = copies.get(rev, {}).get(fn)
1153 if fn in skip:
1153 if fn in skip:
1154 if copy:
1154 if copy:
1155 skip[copy] = True
1155 skip[copy] = True
1156 continue
1156 continue
1157 if fn in prev or fstate[fn]:
1157 if fn in prev or fstate[fn]:
1158 r = display(fn, rev, m, fstate[fn])
1158 r = display(fn, rev, m, fstate[fn])
1159 found = found or r
1159 found = found or r
1160 if r and not opts['all']:
1160 if r and not opts['all']:
1161 skip[fn] = True
1161 skip[fn] = True
1162 if copy:
1162 if copy:
1163 skip[copy] = True
1163 skip[copy] = True
1164 fstate[fn] = m
1164 fstate[fn] = m
1165 if copy:
1165 if copy:
1166 fstate[copy] = m
1166 fstate[copy] = m
1167 prev[fn] = rev
1167 prev[fn] = rev
1168
1168
1169 fstate = fstate.items()
1169 fstate = fstate.items()
1170 fstate.sort()
1170 fstate.sort()
1171 for fn, state in fstate:
1171 for fn, state in fstate:
1172 if fn in skip:
1172 if fn in skip:
1173 continue
1173 continue
1174 if fn not in copies.get(prev[fn], {}):
1174 if fn not in copies.get(prev[fn], {}):
1175 found = display(fn, rev, {}, state) or found
1175 found = display(fn, rev, {}, state) or found
1176 return (not found and 1) or 0
1176 return (not found and 1) or 0
1177
1177
1178 def heads(ui, repo, *branchrevs, **opts):
1178 def heads(ui, repo, *branchrevs, **opts):
1179 """show current repository heads or show branch heads
1179 """show current repository heads or show branch heads
1180
1180
1181 With no arguments, show all repository head changesets.
1181 With no arguments, show all repository head changesets.
1182
1182
1183 If branch or revisions names are given this will show the heads of
1183 If branch or revisions names are given this will show the heads of
1184 the specified branches or the branches those revisions are tagged
1184 the specified branches or the branches those revisions are tagged
1185 with.
1185 with.
1186
1186
1187 Repository "heads" are changesets that don't have child
1187 Repository "heads" are changesets that don't have child
1188 changesets. They are where development generally takes place and
1188 changesets. They are where development generally takes place and
1189 are the usual targets for update and merge operations.
1189 are the usual targets for update and merge operations.
1190
1190
1191 Branch heads are changesets that have a given branch tag, but have
1191 Branch heads are changesets that have a given branch tag, but have
1192 no child changesets with that tag. They are usually where
1192 no child changesets with that tag. They are usually where
1193 development on the given branch takes place.
1193 development on the given branch takes place.
1194 """
1194 """
1195 if opts['rev']:
1195 if opts['rev']:
1196 start = repo.lookup(opts['rev'])
1196 start = repo.lookup(opts['rev'])
1197 else:
1197 else:
1198 start = None
1198 start = None
1199 if not branchrevs:
1199 if not branchrevs:
1200 # Assume we're looking repo-wide heads if no revs were specified.
1200 # Assume we're looking repo-wide heads if no revs were specified.
1201 heads = repo.heads(start)
1201 heads = repo.heads(start)
1202 else:
1202 else:
1203 heads = []
1203 heads = []
1204 visitedset = util.set()
1204 visitedset = util.set()
1205 for branchrev in branchrevs:
1205 for branchrev in branchrevs:
1206 branch = repo.changectx(branchrev).branch()
1206 branch = repo.changectx(branchrev).branch()
1207 if branch in visitedset:
1207 if branch in visitedset:
1208 continue
1208 continue
1209 visitedset.add(branch)
1209 visitedset.add(branch)
1210 bheads = repo.branchheads(branch, start)
1210 bheads = repo.branchheads(branch, start)
1211 if not bheads:
1211 if not bheads:
1212 if branch != branchrev:
1212 if branch != branchrev:
1213 ui.warn(_("no changes on branch %s containing %s are "
1213 ui.warn(_("no changes on branch %s containing %s are "
1214 "reachable from %s\n")
1214 "reachable from %s\n")
1215 % (branch, branchrev, opts['rev']))
1215 % (branch, branchrev, opts['rev']))
1216 else:
1216 else:
1217 ui.warn(_("no changes on branch %s are reachable from %s\n")
1217 ui.warn(_("no changes on branch %s are reachable from %s\n")
1218 % (branch, opts['rev']))
1218 % (branch, opts['rev']))
1219 heads.extend(bheads)
1219 heads.extend(bheads)
1220 if not heads:
1220 if not heads:
1221 return 1
1221 return 1
1222 displayer = cmdutil.show_changeset(ui, repo, opts)
1222 displayer = cmdutil.show_changeset(ui, repo, opts)
1223 for n in heads:
1223 for n in heads:
1224 displayer.show(changenode=n)
1224 displayer.show(changenode=n)
1225
1225
1226 def help_(ui, name=None, with_version=False):
1226 def help_(ui, name=None, with_version=False):
1227 """show help for a command, extension, or list of commands
1227 """show help for a command, extension, or list of commands
1228
1228
1229 With no arguments, print a list of commands and short help.
1229 With no arguments, print a list of commands and short help.
1230
1230
1231 Given a command name, print help for that command.
1231 Given a command name, print help for that command.
1232
1232
1233 Given an extension name, print help for that extension, and the
1233 Given an extension name, print help for that extension, and the
1234 commands it provides."""
1234 commands it provides."""
1235 option_lists = []
1235 option_lists = []
1236
1236
1237 def addglobalopts(aliases):
1237 def addglobalopts(aliases):
1238 if ui.verbose:
1238 if ui.verbose:
1239 option_lists.append((_("global options:"), globalopts))
1239 option_lists.append((_("global options:"), globalopts))
1240 if name == 'shortlist':
1240 if name == 'shortlist':
1241 option_lists.append((_('use "hg help" for the full list '
1241 option_lists.append((_('use "hg help" for the full list '
1242 'of commands'), ()))
1242 'of commands'), ()))
1243 else:
1243 else:
1244 if name == 'shortlist':
1244 if name == 'shortlist':
1245 msg = _('use "hg help" for the full list of commands '
1245 msg = _('use "hg help" for the full list of commands '
1246 'or "hg -v" for details')
1246 'or "hg -v" for details')
1247 elif aliases:
1247 elif aliases:
1248 msg = _('use "hg -v help%s" to show aliases and '
1248 msg = _('use "hg -v help%s" to show aliases and '
1249 'global options') % (name and " " + name or "")
1249 'global options') % (name and " " + name or "")
1250 else:
1250 else:
1251 msg = _('use "hg -v help %s" to show global options') % name
1251 msg = _('use "hg -v help %s" to show global options') % name
1252 option_lists.append((msg, ()))
1252 option_lists.append((msg, ()))
1253
1253
1254 def helpcmd(name):
1254 def helpcmd(name):
1255 if with_version:
1255 if with_version:
1256 version_(ui)
1256 version_(ui)
1257 ui.write('\n')
1257 ui.write('\n')
1258 aliases, i = cmdutil.findcmd(ui, name, table)
1258 aliases, i = cmdutil.findcmd(ui, name, table)
1259 # synopsis
1259 # synopsis
1260 ui.write("%s\n" % i[2])
1260 ui.write("%s\n" % i[2])
1261
1261
1262 # aliases
1262 # aliases
1263 if not ui.quiet and len(aliases) > 1:
1263 if not ui.quiet and len(aliases) > 1:
1264 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1264 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1265
1265
1266 # description
1266 # description
1267 doc = i[0].__doc__
1267 doc = i[0].__doc__
1268 if not doc:
1268 if not doc:
1269 doc = _("(No help text available)")
1269 doc = _("(No help text available)")
1270 if ui.quiet:
1270 if ui.quiet:
1271 doc = doc.splitlines(0)[0]
1271 doc = doc.splitlines(0)[0]
1272 ui.write("\n%s\n" % doc.rstrip())
1272 ui.write("\n%s\n" % doc.rstrip())
1273
1273
1274 if not ui.quiet:
1274 if not ui.quiet:
1275 # options
1275 # options
1276 if i[1]:
1276 if i[1]:
1277 option_lists.append((_("options:\n"), i[1]))
1277 option_lists.append((_("options:\n"), i[1]))
1278
1278
1279 addglobalopts(False)
1279 addglobalopts(False)
1280
1280
1281 def helplist(header, select=None):
1281 def helplist(header, select=None):
1282 h = {}
1282 h = {}
1283 cmds = {}
1283 cmds = {}
1284 for c, e in table.items():
1284 for c, e in table.items():
1285 f = c.split("|", 1)[0]
1285 f = c.split("|", 1)[0]
1286 if select and not select(f):
1286 if select and not select(f):
1287 continue
1287 continue
1288 if name == "shortlist" and not f.startswith("^"):
1288 if name == "shortlist" and not f.startswith("^"):
1289 continue
1289 continue
1290 f = f.lstrip("^")
1290 f = f.lstrip("^")
1291 if not ui.debugflag and f.startswith("debug"):
1291 if not ui.debugflag and f.startswith("debug"):
1292 continue
1292 continue
1293 doc = e[0].__doc__
1293 doc = e[0].__doc__
1294 if not doc:
1294 if not doc:
1295 doc = _("(No help text available)")
1295 doc = _("(No help text available)")
1296 h[f] = doc.splitlines(0)[0].rstrip()
1296 h[f] = doc.splitlines(0)[0].rstrip()
1297 cmds[f] = c.lstrip("^")
1297 cmds[f] = c.lstrip("^")
1298
1298
1299 if not h:
1299 if not h:
1300 ui.status(_('no commands defined\n'))
1300 ui.status(_('no commands defined\n'))
1301 return
1301 return
1302
1302
1303 ui.status(header)
1303 ui.status(header)
1304 fns = h.keys()
1304 fns = h.keys()
1305 fns.sort()
1305 fns.sort()
1306 m = max(map(len, fns))
1306 m = max(map(len, fns))
1307 for f in fns:
1307 for f in fns:
1308 if ui.verbose:
1308 if ui.verbose:
1309 commands = cmds[f].replace("|",", ")
1309 commands = cmds[f].replace("|",", ")
1310 ui.write(" %s:\n %s\n"%(commands, h[f]))
1310 ui.write(" %s:\n %s\n"%(commands, h[f]))
1311 else:
1311 else:
1312 ui.write(' %-*s %s\n' % (m, f, h[f]))
1312 ui.write(' %-*s %s\n' % (m, f, h[f]))
1313
1313
1314 if not ui.quiet:
1314 if not ui.quiet:
1315 addglobalopts(True)
1315 addglobalopts(True)
1316
1316
1317 def helptopic(name):
1317 def helptopic(name):
1318 v = None
1318 v = None
1319 for i in help.helptable:
1319 for i in help.helptable:
1320 l = i.split('|')
1320 l = i.split('|')
1321 if name in l:
1321 if name in l:
1322 v = i
1322 v = i
1323 header = l[-1]
1323 header = l[-1]
1324 if not v:
1324 if not v:
1325 raise cmdutil.UnknownCommand(name)
1325 raise cmdutil.UnknownCommand(name)
1326
1326
1327 # description
1327 # description
1328 doc = help.helptable[v]
1328 doc = help.helptable[v]
1329 if not doc:
1329 if not doc:
1330 doc = _("(No help text available)")
1330 doc = _("(No help text available)")
1331 if callable(doc):
1331 if callable(doc):
1332 doc = doc()
1332 doc = doc()
1333
1333
1334 ui.write("%s\n" % header)
1334 ui.write("%s\n" % header)
1335 ui.write("%s\n" % doc.rstrip())
1335 ui.write("%s\n" % doc.rstrip())
1336
1336
1337 def helpext(name):
1337 def helpext(name):
1338 try:
1338 try:
1339 mod = extensions.find(name)
1339 mod = extensions.find(name)
1340 except KeyError:
1340 except KeyError:
1341 raise cmdutil.UnknownCommand(name)
1341 raise cmdutil.UnknownCommand(name)
1342
1342
1343 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1343 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1344 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1344 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1345 for d in doc[1:]:
1345 for d in doc[1:]:
1346 ui.write(d, '\n')
1346 ui.write(d, '\n')
1347
1347
1348 ui.status('\n')
1348 ui.status('\n')
1349
1349
1350 try:
1350 try:
1351 ct = mod.cmdtable
1351 ct = mod.cmdtable
1352 except AttributeError:
1352 except AttributeError:
1353 ct = {}
1353 ct = {}
1354
1354
1355 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1355 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1356 helplist(_('list of commands:\n\n'), modcmds.has_key)
1356 helplist(_('list of commands:\n\n'), modcmds.has_key)
1357
1357
1358 if name and name != 'shortlist':
1358 if name and name != 'shortlist':
1359 i = None
1359 i = None
1360 for f in (helpcmd, helptopic, helpext):
1360 for f in (helpcmd, helptopic, helpext):
1361 try:
1361 try:
1362 f(name)
1362 f(name)
1363 i = None
1363 i = None
1364 break
1364 break
1365 except cmdutil.UnknownCommand, inst:
1365 except cmdutil.UnknownCommand, inst:
1366 i = inst
1366 i = inst
1367 if i:
1367 if i:
1368 raise i
1368 raise i
1369
1369
1370 else:
1370 else:
1371 # program name
1371 # program name
1372 if ui.verbose or with_version:
1372 if ui.verbose or with_version:
1373 version_(ui)
1373 version_(ui)
1374 else:
1374 else:
1375 ui.status(_("Mercurial Distributed SCM\n"))
1375 ui.status(_("Mercurial Distributed SCM\n"))
1376 ui.status('\n')
1376 ui.status('\n')
1377
1377
1378 # list of commands
1378 # list of commands
1379 if name == "shortlist":
1379 if name == "shortlist":
1380 header = _('basic commands:\n\n')
1380 header = _('basic commands:\n\n')
1381 else:
1381 else:
1382 header = _('list of commands:\n\n')
1382 header = _('list of commands:\n\n')
1383
1383
1384 helplist(header)
1384 helplist(header)
1385
1385
1386 # list all option lists
1386 # list all option lists
1387 opt_output = []
1387 opt_output = []
1388 for title, options in option_lists:
1388 for title, options in option_lists:
1389 opt_output.append(("\n%s" % title, None))
1389 opt_output.append(("\n%s" % title, None))
1390 for shortopt, longopt, default, desc in options:
1390 for shortopt, longopt, default, desc in options:
1391 if "DEPRECATED" in desc and not ui.verbose: continue
1391 if "DEPRECATED" in desc and not ui.verbose: continue
1392 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1392 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1393 longopt and " --%s" % longopt),
1393 longopt and " --%s" % longopt),
1394 "%s%s" % (desc,
1394 "%s%s" % (desc,
1395 default
1395 default
1396 and _(" (default: %s)") % default
1396 and _(" (default: %s)") % default
1397 or "")))
1397 or "")))
1398
1398
1399 if opt_output:
1399 if opt_output:
1400 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1400 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1401 for first, second in opt_output:
1401 for first, second in opt_output:
1402 if second:
1402 if second:
1403 ui.write(" %-*s %s\n" % (opts_len, first, second))
1403 ui.write(" %-*s %s\n" % (opts_len, first, second))
1404 else:
1404 else:
1405 ui.write("%s\n" % first)
1405 ui.write("%s\n" % first)
1406
1406
1407 def identify(ui, repo, source=None,
1407 def identify(ui, repo, source=None,
1408 rev=None, num=None, id=None, branch=None, tags=None):
1408 rev=None, num=None, id=None, branch=None, tags=None):
1409 """identify the working copy or specified revision
1409 """identify the working copy or specified revision
1410
1410
1411 With no revision, print a summary of the current state of the repo.
1411 With no revision, print a summary of the current state of the repo.
1412
1412
1413 With a path, do a lookup in another repository.
1413 With a path, do a lookup in another repository.
1414
1414
1415 This summary identifies the repository state using one or two parent
1415 This summary identifies the repository state using one or two parent
1416 hash identifiers, followed by a "+" if there are uncommitted changes
1416 hash identifiers, followed by a "+" if there are uncommitted changes
1417 in the working directory, a list of tags for this revision and a branch
1417 in the working directory, a list of tags for this revision and a branch
1418 name for non-default branches.
1418 name for non-default branches.
1419 """
1419 """
1420
1420
1421 if not repo and not source:
1421 if not repo and not source:
1422 raise util.Abort(_("There is no Mercurial repository here "
1422 raise util.Abort(_("There is no Mercurial repository here "
1423 "(.hg not found)"))
1423 "(.hg not found)"))
1424
1424
1425 hexfunc = ui.debugflag and hex or short
1425 hexfunc = ui.debugflag and hex or short
1426 default = not (num or id or branch or tags)
1426 default = not (num or id or branch or tags)
1427 output = []
1427 output = []
1428
1428
1429 if source:
1429 if source:
1430 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1430 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1431 srepo = hg.repository(ui, source)
1431 srepo = hg.repository(ui, source)
1432 if not rev and revs:
1432 if not rev and revs:
1433 rev = revs[0]
1433 rev = revs[0]
1434 if not rev:
1434 if not rev:
1435 rev = "tip"
1435 rev = "tip"
1436 if num or branch or tags:
1436 if num or branch or tags:
1437 raise util.Abort(
1437 raise util.Abort(
1438 "can't query remote revision number, branch, or tags")
1438 "can't query remote revision number, branch, or tags")
1439 output = [hexfunc(srepo.lookup(rev))]
1439 output = [hexfunc(srepo.lookup(rev))]
1440 elif not rev:
1440 elif not rev:
1441 ctx = repo.workingctx()
1441 ctx = repo.workingctx()
1442 parents = ctx.parents()
1442 parents = ctx.parents()
1443 changed = False
1443 changed = False
1444 if default or id or num:
1444 if default or id or num:
1445 changed = ctx.files() + ctx.deleted()
1445 changed = ctx.files() + ctx.deleted()
1446 if default or id:
1446 if default or id:
1447 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1447 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1448 (changed) and "+" or "")]
1448 (changed) and "+" or "")]
1449 if num:
1449 if num:
1450 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1450 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1451 (changed) and "+" or ""))
1451 (changed) and "+" or ""))
1452 else:
1452 else:
1453 ctx = repo.changectx(rev)
1453 ctx = repo.changectx(rev)
1454 if default or id:
1454 if default or id:
1455 output = [hexfunc(ctx.node())]
1455 output = [hexfunc(ctx.node())]
1456 if num:
1456 if num:
1457 output.append(str(ctx.rev()))
1457 output.append(str(ctx.rev()))
1458
1458
1459 if not source and default and not ui.quiet:
1459 if not source and default and not ui.quiet:
1460 b = util.tolocal(ctx.branch())
1460 b = util.tolocal(ctx.branch())
1461 if b != 'default':
1461 if b != 'default':
1462 output.append("(%s)" % b)
1462 output.append("(%s)" % b)
1463
1463
1464 # multiple tags for a single parent separated by '/'
1464 # multiple tags for a single parent separated by '/'
1465 t = "/".join(ctx.tags())
1465 t = "/".join(ctx.tags())
1466 if t:
1466 if t:
1467 output.append(t)
1467 output.append(t)
1468
1468
1469 if branch:
1469 if branch:
1470 output.append(util.tolocal(ctx.branch()))
1470 output.append(util.tolocal(ctx.branch()))
1471
1471
1472 if tags:
1472 if tags:
1473 output.extend(ctx.tags())
1473 output.extend(ctx.tags())
1474
1474
1475 ui.write("%s\n" % ' '.join(output))
1475 ui.write("%s\n" % ' '.join(output))
1476
1476
1477 def import_(ui, repo, patch1, *patches, **opts):
1477 def import_(ui, repo, patch1, *patches, **opts):
1478 """import an ordered set of patches
1478 """import an ordered set of patches
1479
1479
1480 Import a list of patches and commit them individually.
1480 Import a list of patches and commit them individually.
1481
1481
1482 If there are outstanding changes in the working directory, import
1482 If there are outstanding changes in the working directory, import
1483 will abort unless given the -f flag.
1483 will abort unless given the -f flag.
1484
1484
1485 You can import a patch straight from a mail message. Even patches
1485 You can import a patch straight from a mail message. Even patches
1486 as attachments work (body part must be type text/plain or
1486 as attachments work (body part must be type text/plain or
1487 text/x-patch to be used). From and Subject headers of email
1487 text/x-patch to be used). From and Subject headers of email
1488 message are used as default committer and commit message. All
1488 message are used as default committer and commit message. All
1489 text/plain body parts before first diff are added to commit
1489 text/plain body parts before first diff are added to commit
1490 message.
1490 message.
1491
1491
1492 If the imported patch was generated by hg export, user and description
1492 If the imported patch was generated by hg export, user and description
1493 from patch override values from message headers and body. Values
1493 from patch override values from message headers and body. Values
1494 given on command line with -m and -u override these.
1494 given on command line with -m and -u override these.
1495
1495
1496 If --exact is specified, import will set the working directory
1496 If --exact is specified, import will set the working directory
1497 to the parent of each patch before applying it, and will abort
1497 to the parent of each patch before applying it, and will abort
1498 if the resulting changeset has a different ID than the one
1498 if the resulting changeset has a different ID than the one
1499 recorded in the patch. This may happen due to character set
1499 recorded in the patch. This may happen due to character set
1500 problems or other deficiencies in the text patch format.
1500 problems or other deficiencies in the text patch format.
1501
1501
1502 To read a patch from standard input, use patch name "-".
1502 To read a patch from standard input, use patch name "-".
1503 See 'hg help dates' for a list of formats valid for -d/--date.
1503 See 'hg help dates' for a list of formats valid for -d/--date.
1504 """
1504 """
1505 patches = (patch1,) + patches
1505 patches = (patch1,) + patches
1506
1506
1507 date = opts.get('date')
1507 date = opts.get('date')
1508 if date:
1508 if date:
1509 opts['date'] = util.parsedate(date)
1509 opts['date'] = util.parsedate(date)
1510
1510
1511 if opts.get('exact') or not opts['force']:
1511 if opts.get('exact') or not opts['force']:
1512 cmdutil.bail_if_changed(repo)
1512 cmdutil.bail_if_changed(repo)
1513
1513
1514 d = opts["base"]
1514 d = opts["base"]
1515 strip = opts["strip"]
1515 strip = opts["strip"]
1516 wlock = lock = None
1516 wlock = lock = None
1517 try:
1517 try:
1518 wlock = repo.wlock()
1518 wlock = repo.wlock()
1519 lock = repo.lock()
1519 lock = repo.lock()
1520 for p in patches:
1520 for p in patches:
1521 pf = os.path.join(d, p)
1521 pf = os.path.join(d, p)
1522
1522
1523 if pf == '-':
1523 if pf == '-':
1524 ui.status(_("applying patch from stdin\n"))
1524 ui.status(_("applying patch from stdin\n"))
1525 data = patch.extract(ui, sys.stdin)
1525 data = patch.extract(ui, sys.stdin)
1526 else:
1526 else:
1527 ui.status(_("applying %s\n") % p)
1527 ui.status(_("applying %s\n") % p)
1528 if os.path.exists(pf):
1528 if os.path.exists(pf):
1529 data = patch.extract(ui, file(pf, 'rb'))
1529 data = patch.extract(ui, file(pf, 'rb'))
1530 else:
1530 else:
1531 data = patch.extract(ui, urllib.urlopen(pf))
1531 data = patch.extract(ui, urllib.urlopen(pf))
1532 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1532 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1533
1533
1534 if tmpname is None:
1534 if tmpname is None:
1535 raise util.Abort(_('no diffs found'))
1535 raise util.Abort(_('no diffs found'))
1536
1536
1537 try:
1537 try:
1538 cmdline_message = cmdutil.logmessage(opts)
1538 cmdline_message = cmdutil.logmessage(opts)
1539 if cmdline_message:
1539 if cmdline_message:
1540 # pickup the cmdline msg
1540 # pickup the cmdline msg
1541 message = cmdline_message
1541 message = cmdline_message
1542 elif message:
1542 elif message:
1543 # pickup the patch msg
1543 # pickup the patch msg
1544 message = message.strip()
1544 message = message.strip()
1545 else:
1545 else:
1546 # launch the editor
1546 # launch the editor
1547 message = None
1547 message = None
1548 ui.debug(_('message:\n%s\n') % message)
1548 ui.debug(_('message:\n%s\n') % message)
1549
1549
1550 wp = repo.workingctx().parents()
1550 wp = repo.workingctx().parents()
1551 if opts.get('exact'):
1551 if opts.get('exact'):
1552 if not nodeid or not p1:
1552 if not nodeid or not p1:
1553 raise util.Abort(_('not a mercurial patch'))
1553 raise util.Abort(_('not a mercurial patch'))
1554 p1 = repo.lookup(p1)
1554 p1 = repo.lookup(p1)
1555 p2 = repo.lookup(p2 or hex(nullid))
1555 p2 = repo.lookup(p2 or hex(nullid))
1556
1556
1557 if p1 != wp[0].node():
1557 if p1 != wp[0].node():
1558 hg.clean(repo, p1)
1558 hg.clean(repo, p1)
1559 repo.dirstate.setparents(p1, p2)
1559 repo.dirstate.setparents(p1, p2)
1560 elif p2:
1560 elif p2:
1561 try:
1561 try:
1562 p1 = repo.lookup(p1)
1562 p1 = repo.lookup(p1)
1563 p2 = repo.lookup(p2)
1563 p2 = repo.lookup(p2)
1564 if p1 == wp[0].node():
1564 if p1 == wp[0].node():
1565 repo.dirstate.setparents(p1, p2)
1565 repo.dirstate.setparents(p1, p2)
1566 except RepoError:
1566 except RepoError:
1567 pass
1567 pass
1568 if opts.get('exact') or opts.get('import_branch'):
1568 if opts.get('exact') or opts.get('import_branch'):
1569 repo.dirstate.setbranch(branch or 'default')
1569 repo.dirstate.setbranch(branch or 'default')
1570
1570
1571 files = {}
1571 files = {}
1572 try:
1572 try:
1573 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1573 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1574 files=files)
1574 files=files)
1575 finally:
1575 finally:
1576 files = patch.updatedir(ui, repo, files)
1576 files = patch.updatedir(ui, repo, files)
1577 if not opts.get('no_commit'):
1577 if not opts.get('no_commit'):
1578 n = repo.commit(files, message, opts.get('user') or user,
1578 n = repo.commit(files, message, opts.get('user') or user,
1579 opts.get('date') or date)
1579 opts.get('date') or date)
1580 if opts.get('exact'):
1580 if opts.get('exact'):
1581 if hex(n) != nodeid:
1581 if hex(n) != nodeid:
1582 repo.rollback()
1582 repo.rollback()
1583 raise util.Abort(_('patch is damaged'
1583 raise util.Abort(_('patch is damaged'
1584 ' or loses information'))
1584 ' or loses information'))
1585 # Force a dirstate write so that the next transaction
1585 # Force a dirstate write so that the next transaction
1586 # backups an up-do-date file.
1586 # backups an up-do-date file.
1587 repo.dirstate.write()
1587 repo.dirstate.write()
1588 finally:
1588 finally:
1589 os.unlink(tmpname)
1589 os.unlink(tmpname)
1590 finally:
1590 finally:
1591 del lock, wlock
1591 del lock, wlock
1592
1592
1593 def incoming(ui, repo, source="default", **opts):
1593 def incoming(ui, repo, source="default", **opts):
1594 """show new changesets found in source
1594 """show new changesets found in source
1595
1595
1596 Show new changesets found in the specified path/URL or the default
1596 Show new changesets found in the specified path/URL or the default
1597 pull location. These are the changesets that would be pulled if a pull
1597 pull location. These are the changesets that would be pulled if a pull
1598 was requested.
1598 was requested.
1599
1599
1600 For remote repository, using --bundle avoids downloading the changesets
1600 For remote repository, using --bundle avoids downloading the changesets
1601 twice if the incoming is followed by a pull.
1601 twice if the incoming is followed by a pull.
1602
1602
1603 See pull for valid source format details.
1603 See pull for valid source format details.
1604 """
1604 """
1605 limit = cmdutil.loglimit(opts)
1605 limit = cmdutil.loglimit(opts)
1606 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1606 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1607 cmdutil.setremoteconfig(ui, opts)
1607 cmdutil.setremoteconfig(ui, opts)
1608
1608
1609 other = hg.repository(ui, source)
1609 other = hg.repository(ui, source)
1610 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1610 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1611 if revs:
1611 if revs:
1612 revs = [other.lookup(rev) for rev in revs]
1612 revs = [other.lookup(rev) for rev in revs]
1613 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1613 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1614 if not incoming:
1614 if not incoming:
1615 try:
1615 try:
1616 os.unlink(opts["bundle"])
1616 os.unlink(opts["bundle"])
1617 except:
1617 except:
1618 pass
1618 pass
1619 ui.status(_("no changes found\n"))
1619 ui.status(_("no changes found\n"))
1620 return 1
1620 return 1
1621
1621
1622 cleanup = None
1622 cleanup = None
1623 try:
1623 try:
1624 fname = opts["bundle"]
1624 fname = opts["bundle"]
1625 if fname or not other.local():
1625 if fname or not other.local():
1626 # create a bundle (uncompressed if other repo is not local)
1626 # create a bundle (uncompressed if other repo is not local)
1627 if revs is None:
1627 if revs is None:
1628 cg = other.changegroup(incoming, "incoming")
1628 cg = other.changegroup(incoming, "incoming")
1629 else:
1629 else:
1630 cg = other.changegroupsubset(incoming, revs, 'incoming')
1630 cg = other.changegroupsubset(incoming, revs, 'incoming')
1631 bundletype = other.local() and "HG10BZ" or "HG10UN"
1631 bundletype = other.local() and "HG10BZ" or "HG10UN"
1632 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1632 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1633 # keep written bundle?
1633 # keep written bundle?
1634 if opts["bundle"]:
1634 if opts["bundle"]:
1635 cleanup = None
1635 cleanup = None
1636 if not other.local():
1636 if not other.local():
1637 # use the created uncompressed bundlerepo
1637 # use the created uncompressed bundlerepo
1638 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1638 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1639
1639
1640 o = other.changelog.nodesbetween(incoming, revs)[0]
1640 o = other.changelog.nodesbetween(incoming, revs)[0]
1641 if opts['newest_first']:
1641 if opts['newest_first']:
1642 o.reverse()
1642 o.reverse()
1643 displayer = cmdutil.show_changeset(ui, other, opts)
1643 displayer = cmdutil.show_changeset(ui, other, opts)
1644 count = 0
1644 count = 0
1645 for n in o:
1645 for n in o:
1646 if count >= limit:
1646 if count >= limit:
1647 break
1647 break
1648 parents = [p for p in other.changelog.parents(n) if p != nullid]
1648 parents = [p for p in other.changelog.parents(n) if p != nullid]
1649 if opts['no_merges'] and len(parents) == 2:
1649 if opts['no_merges'] and len(parents) == 2:
1650 continue
1650 continue
1651 count += 1
1651 count += 1
1652 displayer.show(changenode=n)
1652 displayer.show(changenode=n)
1653 finally:
1653 finally:
1654 if hasattr(other, 'close'):
1654 if hasattr(other, 'close'):
1655 other.close()
1655 other.close()
1656 if cleanup:
1656 if cleanup:
1657 os.unlink(cleanup)
1657 os.unlink(cleanup)
1658
1658
1659 def init(ui, dest=".", **opts):
1659 def init(ui, dest=".", **opts):
1660 """create a new repository in the given directory
1660 """create a new repository in the given directory
1661
1661
1662 Initialize a new repository in the given directory. If the given
1662 Initialize a new repository in the given directory. If the given
1663 directory does not exist, it is created.
1663 directory does not exist, it is created.
1664
1664
1665 If no directory is given, the current directory is used.
1665 If no directory is given, the current directory is used.
1666
1666
1667 It is possible to specify an ssh:// URL as the destination.
1667 It is possible to specify an ssh:// URL as the destination.
1668 Look at the help text for the pull command for important details
1668 Look at the help text for the pull command for important details
1669 about ssh:// URLs.
1669 about ssh:// URLs.
1670 """
1670 """
1671 cmdutil.setremoteconfig(ui, opts)
1671 cmdutil.setremoteconfig(ui, opts)
1672 hg.repository(ui, dest, create=1)
1672 hg.repository(ui, dest, create=1)
1673
1673
1674 def locate(ui, repo, *pats, **opts):
1674 def locate(ui, repo, *pats, **opts):
1675 """locate files matching specific patterns
1675 """locate files matching specific patterns
1676
1676
1677 Print all files under Mercurial control whose names match the
1677 Print all files under Mercurial control whose names match the
1678 given patterns.
1678 given patterns.
1679
1679
1680 This command searches the entire repository by default. To search
1680 This command searches the entire repository by default. To search
1681 just the current directory and its subdirectories, use
1681 just the current directory and its subdirectories, use
1682 "--include .".
1682 "--include .".
1683
1683
1684 If no patterns are given to match, this command prints all file
1684 If no patterns are given to match, this command prints all file
1685 names.
1685 names.
1686
1686
1687 If you want to feed the output of this command into the "xargs"
1687 If you want to feed the output of this command into the "xargs"
1688 command, use the "-0" option to both this command and "xargs".
1688 command, use the "-0" option to both this command and "xargs".
1689 This will avoid the problem of "xargs" treating single filenames
1689 This will avoid the problem of "xargs" treating single filenames
1690 that contain white space as multiple filenames.
1690 that contain white space as multiple filenames.
1691 """
1691 """
1692 end = opts['print0'] and '\0' or '\n'
1692 end = opts['print0'] and '\0' or '\n'
1693 rev = opts['rev']
1693 rev = opts['rev']
1694 if rev:
1694 if rev:
1695 node = repo.lookup(rev)
1695 node = repo.lookup(rev)
1696 else:
1696 else:
1697 node = None
1697 node = None
1698
1698
1699 ret = 1
1699 ret = 1
1700 m = cmdutil.match(repo, pats, opts, default='relglob')
1700 m = cmdutil.match(repo, pats, opts, default='relglob')
1701 m.bad = lambda x,y: False
1701 m.bad = lambda x,y: False
1702 for abs in repo.walk(m, node):
1702 for abs in repo.walk(m, node):
1703 if not node and abs not in repo.dirstate:
1703 if not node and abs not in repo.dirstate:
1704 continue
1704 continue
1705 if opts['fullpath']:
1705 if opts['fullpath']:
1706 ui.write(os.path.join(repo.root, abs), end)
1706 ui.write(os.path.join(repo.root, abs), end)
1707 else:
1707 else:
1708 ui.write(((pats and m.rel(abs)) or abs), end)
1708 ui.write(((pats and m.rel(abs)) or abs), end)
1709 ret = 0
1709 ret = 0
1710
1710
1711 return ret
1711 return ret
1712
1712
1713 def log(ui, repo, *pats, **opts):
1713 def log(ui, repo, *pats, **opts):
1714 """show revision history of entire repository or files
1714 """show revision history of entire repository or files
1715
1715
1716 Print the revision history of the specified files or the entire
1716 Print the revision history of the specified files or the entire
1717 project.
1717 project.
1718
1718
1719 File history is shown without following rename or copy history of
1719 File history is shown without following rename or copy history of
1720 files. Use -f/--follow with a file name to follow history across
1720 files. Use -f/--follow with a file name to follow history across
1721 renames and copies. --follow without a file name will only show
1721 renames and copies. --follow without a file name will only show
1722 ancestors or descendants of the starting revision. --follow-first
1722 ancestors or descendants of the starting revision. --follow-first
1723 only follows the first parent of merge revisions.
1723 only follows the first parent of merge revisions.
1724
1724
1725 If no revision range is specified, the default is tip:0 unless
1725 If no revision range is specified, the default is tip:0 unless
1726 --follow is set, in which case the working directory parent is
1726 --follow is set, in which case the working directory parent is
1727 used as the starting revision.
1727 used as the starting revision.
1728
1728
1729 See 'hg help dates' for a list of formats valid for -d/--date.
1729 See 'hg help dates' for a list of formats valid for -d/--date.
1730
1730
1731 By default this command outputs: changeset id and hash, tags,
1731 By default this command outputs: changeset id and hash, tags,
1732 non-trivial parents, user, date and time, and a summary for each
1732 non-trivial parents, user, date and time, and a summary for each
1733 commit. When the -v/--verbose switch is used, the list of changed
1733 commit. When the -v/--verbose switch is used, the list of changed
1734 files and full commit message is shown.
1734 files and full commit message is shown.
1735
1735
1736 NOTE: log -p may generate unexpected diff output for merge
1736 NOTE: log -p may generate unexpected diff output for merge
1737 changesets, as it will compare the merge changeset against its
1737 changesets, as it will compare the merge changeset against its
1738 first parent only. Also, the files: list will only reflect files
1738 first parent only. Also, the files: list will only reflect files
1739 that are different from BOTH parents.
1739 that are different from BOTH parents.
1740
1740
1741 """
1741 """
1742
1742
1743 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1743 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1744 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1744 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1745
1745
1746 limit = cmdutil.loglimit(opts)
1746 limit = cmdutil.loglimit(opts)
1747 count = 0
1747 count = 0
1748
1748
1749 if opts['copies'] and opts['rev']:
1749 if opts['copies'] and opts['rev']:
1750 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1750 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1751 else:
1751 else:
1752 endrev = repo.changelog.count()
1752 endrev = repo.changelog.count()
1753 rcache = {}
1753 rcache = {}
1754 ncache = {}
1754 ncache = {}
1755 def getrenamed(fn, rev):
1755 def getrenamed(fn, rev):
1756 '''looks up all renames for a file (up to endrev) the first
1756 '''looks up all renames for a file (up to endrev) the first
1757 time the file is given. It indexes on the changerev and only
1757 time the file is given. It indexes on the changerev and only
1758 parses the manifest if linkrev != changerev.
1758 parses the manifest if linkrev != changerev.
1759 Returns rename info for fn at changerev rev.'''
1759 Returns rename info for fn at changerev rev.'''
1760 if fn not in rcache:
1760 if fn not in rcache:
1761 rcache[fn] = {}
1761 rcache[fn] = {}
1762 ncache[fn] = {}
1762 ncache[fn] = {}
1763 fl = repo.file(fn)
1763 fl = repo.file(fn)
1764 for i in xrange(fl.count()):
1764 for i in xrange(fl.count()):
1765 node = fl.node(i)
1765 node = fl.node(i)
1766 lr = fl.linkrev(node)
1766 lr = fl.linkrev(node)
1767 renamed = fl.renamed(node)
1767 renamed = fl.renamed(node)
1768 rcache[fn][lr] = renamed
1768 rcache[fn][lr] = renamed
1769 if renamed:
1769 if renamed:
1770 ncache[fn][node] = renamed
1770 ncache[fn][node] = renamed
1771 if lr >= endrev:
1771 if lr >= endrev:
1772 break
1772 break
1773 if rev in rcache[fn]:
1773 if rev in rcache[fn]:
1774 return rcache[fn][rev]
1774 return rcache[fn][rev]
1775
1775
1776 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1776 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1777 # filectx logic.
1777 # filectx logic.
1778
1778
1779 try:
1779 try:
1780 return repo.changectx(rev).filectx(fn).renamed()
1780 return repo.changectx(rev).filectx(fn).renamed()
1781 except revlog.LookupError:
1781 except revlog.LookupError:
1782 pass
1782 pass
1783 return None
1783 return None
1784
1784
1785 df = False
1785 df = False
1786 if opts["date"]:
1786 if opts["date"]:
1787 df = util.matchdate(opts["date"])
1787 df = util.matchdate(opts["date"])
1788
1788
1789 only_branches = opts['only_branch']
1789 only_branches = opts['only_branch']
1790
1790
1791 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1791 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1792 for st, rev, fns in changeiter:
1792 for st, rev, fns in changeiter:
1793 if st == 'add':
1793 if st == 'add':
1794 changenode = repo.changelog.node(rev)
1794 changenode = repo.changelog.node(rev)
1795 parents = [p for p in repo.changelog.parentrevs(rev)
1795 parents = [p for p in repo.changelog.parentrevs(rev)
1796 if p != nullrev]
1796 if p != nullrev]
1797 if opts['no_merges'] and len(parents) == 2:
1797 if opts['no_merges'] and len(parents) == 2:
1798 continue
1798 continue
1799 if opts['only_merges'] and len(parents) != 2:
1799 if opts['only_merges'] and len(parents) != 2:
1800 continue
1800 continue
1801
1801
1802 if only_branches:
1802 if only_branches:
1803 revbranch = get(rev)[5]['branch']
1803 revbranch = get(rev)[5]['branch']
1804 if revbranch not in only_branches:
1804 if revbranch not in only_branches:
1805 continue
1805 continue
1806
1806
1807 if df:
1807 if df:
1808 changes = get(rev)
1808 changes = get(rev)
1809 if not df(changes[2][0]):
1809 if not df(changes[2][0]):
1810 continue
1810 continue
1811
1811
1812 if opts['keyword']:
1812 if opts['keyword']:
1813 changes = get(rev)
1813 changes = get(rev)
1814 miss = 0
1814 miss = 0
1815 for k in [kw.lower() for kw in opts['keyword']]:
1815 for k in [kw.lower() for kw in opts['keyword']]:
1816 if not (k in changes[1].lower() or
1816 if not (k in changes[1].lower() or
1817 k in changes[4].lower() or
1817 k in changes[4].lower() or
1818 k in " ".join(changes[3]).lower()):
1818 k in " ".join(changes[3]).lower()):
1819 miss = 1
1819 miss = 1
1820 break
1820 break
1821 if miss:
1821 if miss:
1822 continue
1822 continue
1823
1823
1824 copies = []
1824 copies = []
1825 if opts.get('copies') and rev:
1825 if opts.get('copies') and rev:
1826 for fn in get(rev)[3]:
1826 for fn in get(rev)[3]:
1827 rename = getrenamed(fn, rev)
1827 rename = getrenamed(fn, rev)
1828 if rename:
1828 if rename:
1829 copies.append((fn, rename[0]))
1829 copies.append((fn, rename[0]))
1830 displayer.show(rev, changenode, copies=copies)
1830 displayer.show(rev, changenode, copies=copies)
1831 elif st == 'iter':
1831 elif st == 'iter':
1832 if count == limit: break
1832 if count == limit: break
1833 if displayer.flush(rev):
1833 if displayer.flush(rev):
1834 count += 1
1834 count += 1
1835
1835
1836 def manifest(ui, repo, node=None, rev=None):
1836 def manifest(ui, repo, node=None, rev=None):
1837 """output the current or given revision of the project manifest
1837 """output the current or given revision of the project manifest
1838
1838
1839 Print a list of version controlled files for the given revision.
1839 Print a list of version controlled files for the given revision.
1840 If no revision is given, the parent of the working directory is used,
1840 If no revision is given, the parent of the working directory is used,
1841 or tip if no revision is checked out.
1841 or tip if no revision is checked out.
1842
1842
1843 The manifest is the list of files being version controlled. If no revision
1843 The manifest is the list of files being version controlled. If no revision
1844 is given then the first parent of the working directory is used.
1844 is given then the first parent of the working directory is used.
1845
1845
1846 With -v flag, print file permissions, symlink and executable bits. With
1846 With -v flag, print file permissions, symlink and executable bits. With
1847 --debug flag, print file revision hashes.
1847 --debug flag, print file revision hashes.
1848 """
1848 """
1849
1849
1850 if rev and node:
1850 if rev and node:
1851 raise util.Abort(_("please specify just one revision"))
1851 raise util.Abort(_("please specify just one revision"))
1852
1852
1853 if not node:
1853 if not node:
1854 node = rev
1854 node = rev
1855
1855
1856 m = repo.changectx(node).manifest()
1856 m = repo.changectx(node).manifest()
1857 files = m.keys()
1857 files = m.keys()
1858 files.sort()
1858 files.sort()
1859
1859
1860 for f in files:
1860 for f in files:
1861 if ui.debugflag:
1861 if ui.debugflag:
1862 ui.write("%40s " % hex(m[f]))
1862 ui.write("%40s " % hex(m[f]))
1863 if ui.verbose:
1863 if ui.verbose:
1864 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1864 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1865 perm = m.execf(f) and "755" or "644"
1865 perm = m.execf(f) and "755" or "644"
1866 ui.write("%3s %1s " % (perm, type))
1866 ui.write("%3s %1s " % (perm, type))
1867 ui.write("%s\n" % f)
1867 ui.write("%s\n" % f)
1868
1868
1869 def merge(ui, repo, node=None, force=None, rev=None):
1869 def merge(ui, repo, node=None, force=None, rev=None):
1870 """merge working directory with another revision
1870 """merge working directory with another revision
1871
1871
1872 Merge the contents of the current working directory and the
1872 Merge the contents of the current working directory and the
1873 requested revision. Files that changed between either parent are
1873 requested revision. Files that changed between either parent are
1874 marked as changed for the next commit and a commit must be
1874 marked as changed for the next commit and a commit must be
1875 performed before any further updates are allowed.
1875 performed before any further updates are allowed.
1876
1876
1877 If no revision is specified, the working directory's parent is a
1877 If no revision is specified, the working directory's parent is a
1878 head revision, and the repository contains exactly one other head,
1878 head revision, and the repository contains exactly one other head,
1879 the other head is merged with by default. Otherwise, an explicit
1879 the other head is merged with by default. Otherwise, an explicit
1880 revision to merge with must be provided.
1880 revision to merge with must be provided.
1881 """
1881 """
1882
1882
1883 if rev and node:
1883 if rev and node:
1884 raise util.Abort(_("please specify just one revision"))
1884 raise util.Abort(_("please specify just one revision"))
1885 if not node:
1885 if not node:
1886 node = rev
1886 node = rev
1887
1887
1888 if not node:
1888 if not node:
1889 heads = repo.heads()
1889 heads = repo.heads()
1890 if len(heads) > 2:
1890 if len(heads) > 2:
1891 raise util.Abort(_('repo has %d heads - '
1891 raise util.Abort(_('repo has %d heads - '
1892 'please merge with an explicit rev') %
1892 'please merge with an explicit rev') %
1893 len(heads))
1893 len(heads))
1894 parent = repo.dirstate.parents()[0]
1894 parent = repo.dirstate.parents()[0]
1895 if len(heads) == 1:
1895 if len(heads) == 1:
1896 msg = _('there is nothing to merge')
1896 msg = _('there is nothing to merge')
1897 if parent != repo.lookup(repo.workingctx().branch()):
1897 if parent != repo.lookup(repo.workingctx().branch()):
1898 msg = _('%s - use "hg update" instead') % msg
1898 msg = _('%s - use "hg update" instead') % msg
1899 raise util.Abort(msg)
1899 raise util.Abort(msg)
1900
1900
1901 if parent not in heads:
1901 if parent not in heads:
1902 raise util.Abort(_('working dir not at a head rev - '
1902 raise util.Abort(_('working dir not at a head rev - '
1903 'use "hg update" or merge with an explicit rev'))
1903 'use "hg update" or merge with an explicit rev'))
1904 node = parent == heads[0] and heads[-1] or heads[0]
1904 node = parent == heads[0] and heads[-1] or heads[0]
1905 return hg.merge(repo, node, force=force)
1905 return hg.merge(repo, node, force=force)
1906
1906
1907 def outgoing(ui, repo, dest=None, **opts):
1907 def outgoing(ui, repo, dest=None, **opts):
1908 """show changesets not found in destination
1908 """show changesets not found in destination
1909
1909
1910 Show changesets not found in the specified destination repository or
1910 Show changesets not found in the specified destination repository or
1911 the default push location. These are the changesets that would be pushed
1911 the default push location. These are the changesets that would be pushed
1912 if a push was requested.
1912 if a push was requested.
1913
1913
1914 See pull for valid destination format details.
1914 See pull for valid destination format details.
1915 """
1915 """
1916 limit = cmdutil.loglimit(opts)
1916 limit = cmdutil.loglimit(opts)
1917 dest, revs, checkout = hg.parseurl(
1917 dest, revs, checkout = hg.parseurl(
1918 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1918 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1919 cmdutil.setremoteconfig(ui, opts)
1919 cmdutil.setremoteconfig(ui, opts)
1920 if revs:
1920 if revs:
1921 revs = [repo.lookup(rev) for rev in revs]
1921 revs = [repo.lookup(rev) for rev in revs]
1922
1922
1923 other = hg.repository(ui, dest)
1923 other = hg.repository(ui, dest)
1924 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1924 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1925 o = repo.findoutgoing(other, force=opts['force'])
1925 o = repo.findoutgoing(other, force=opts['force'])
1926 if not o:
1926 if not o:
1927 ui.status(_("no changes found\n"))
1927 ui.status(_("no changes found\n"))
1928 return 1
1928 return 1
1929 o = repo.changelog.nodesbetween(o, revs)[0]
1929 o = repo.changelog.nodesbetween(o, revs)[0]
1930 if opts['newest_first']:
1930 if opts['newest_first']:
1931 o.reverse()
1931 o.reverse()
1932 displayer = cmdutil.show_changeset(ui, repo, opts)
1932 displayer = cmdutil.show_changeset(ui, repo, opts)
1933 count = 0
1933 count = 0
1934 for n in o:
1934 for n in o:
1935 if count >= limit:
1935 if count >= limit:
1936 break
1936 break
1937 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1937 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1938 if opts['no_merges'] and len(parents) == 2:
1938 if opts['no_merges'] and len(parents) == 2:
1939 continue
1939 continue
1940 count += 1
1940 count += 1
1941 displayer.show(changenode=n)
1941 displayer.show(changenode=n)
1942
1942
1943 def parents(ui, repo, file_=None, **opts):
1943 def parents(ui, repo, file_=None, **opts):
1944 """show the parents of the working dir or revision
1944 """show the parents of the working dir or revision
1945
1945
1946 Print the working directory's parent revisions. If a
1946 Print the working directory's parent revisions. If a
1947 revision is given via --rev, the parent of that revision
1947 revision is given via --rev, the parent of that revision
1948 will be printed. If a file argument is given, revision in
1948 will be printed. If a file argument is given, revision in
1949 which the file was last changed (before the working directory
1949 which the file was last changed (before the working directory
1950 revision or the argument to --rev if given) is printed.
1950 revision or the argument to --rev if given) is printed.
1951 """
1951 """
1952 rev = opts.get('rev')
1952 rev = opts.get('rev')
1953 if rev:
1953 if rev:
1954 ctx = repo.changectx(rev)
1954 ctx = repo.changectx(rev)
1955 else:
1955 else:
1956 ctx = repo.workingctx()
1956 ctx = repo.workingctx()
1957
1957
1958 if file_:
1958 if file_:
1959 m = cmdutil.match(repo, (file_,), opts)
1959 m = cmdutil.match(repo, (file_,), opts)
1960 if m.anypats() or len(m.files()) != 1:
1960 if m.anypats() or len(m.files()) != 1:
1961 raise util.Abort(_('can only specify an explicit file name'))
1961 raise util.Abort(_('can only specify an explicit file name'))
1962 file_ = m.files()[0]
1962 file_ = m.files()[0]
1963 filenodes = []
1963 filenodes = []
1964 for cp in ctx.parents():
1964 for cp in ctx.parents():
1965 if not cp:
1965 if not cp:
1966 continue
1966 continue
1967 try:
1967 try:
1968 filenodes.append(cp.filenode(file_))
1968 filenodes.append(cp.filenode(file_))
1969 except revlog.LookupError:
1969 except revlog.LookupError:
1970 pass
1970 pass
1971 if not filenodes:
1971 if not filenodes:
1972 raise util.Abort(_("'%s' not found in manifest!") % file_)
1972 raise util.Abort(_("'%s' not found in manifest!") % file_)
1973 fl = repo.file(file_)
1973 fl = repo.file(file_)
1974 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1974 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1975 else:
1975 else:
1976 p = [cp.node() for cp in ctx.parents()]
1976 p = [cp.node() for cp in ctx.parents()]
1977
1977
1978 displayer = cmdutil.show_changeset(ui, repo, opts)
1978 displayer = cmdutil.show_changeset(ui, repo, opts)
1979 for n in p:
1979 for n in p:
1980 if n != nullid:
1980 if n != nullid:
1981 displayer.show(changenode=n)
1981 displayer.show(changenode=n)
1982
1982
1983 def paths(ui, repo, search=None):
1983 def paths(ui, repo, search=None):
1984 """show definition of symbolic path names
1984 """show definition of symbolic path names
1985
1985
1986 Show definition of symbolic path name NAME. If no name is given, show
1986 Show definition of symbolic path name NAME. If no name is given, show
1987 definition of available names.
1987 definition of available names.
1988
1988
1989 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1989 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1990 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1990 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1991 """
1991 """
1992 if search:
1992 if search:
1993 for name, path in ui.configitems("paths"):
1993 for name, path in ui.configitems("paths"):
1994 if name == search:
1994 if name == search:
1995 ui.write("%s\n" % util.hidepassword(path))
1995 ui.write("%s\n" % util.hidepassword(path))
1996 return
1996 return
1997 ui.warn(_("not found!\n"))
1997 ui.warn(_("not found!\n"))
1998 return 1
1998 return 1
1999 else:
1999 else:
2000 for name, path in ui.configitems("paths"):
2000 for name, path in ui.configitems("paths"):
2001 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2001 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2002
2002
2003 def postincoming(ui, repo, modheads, optupdate, checkout):
2003 def postincoming(ui, repo, modheads, optupdate, checkout):
2004 if modheads == 0:
2004 if modheads == 0:
2005 return
2005 return
2006 if optupdate:
2006 if optupdate:
2007 if modheads <= 1 or checkout:
2007 if modheads <= 1 or checkout:
2008 return hg.update(repo, checkout)
2008 return hg.update(repo, checkout)
2009 else:
2009 else:
2010 ui.status(_("not updating, since new heads added\n"))
2010 ui.status(_("not updating, since new heads added\n"))
2011 if modheads > 1:
2011 if modheads > 1:
2012 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2012 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2013 else:
2013 else:
2014 ui.status(_("(run 'hg update' to get a working copy)\n"))
2014 ui.status(_("(run 'hg update' to get a working copy)\n"))
2015
2015
2016 def pull(ui, repo, source="default", **opts):
2016 def pull(ui, repo, source="default", **opts):
2017 """pull changes from the specified source
2017 """pull changes from the specified source
2018
2018
2019 Pull changes from a remote repository to a local one.
2019 Pull changes from a remote repository to a local one.
2020
2020
2021 This finds all changes from the repository at the specified path
2021 This finds all changes from the repository at the specified path
2022 or URL and adds them to the local repository. By default, this
2022 or URL and adds them to the local repository. By default, this
2023 does not update the copy of the project in the working directory.
2023 does not update the copy of the project in the working directory.
2024
2024
2025 Valid URLs are of the form:
2025 Valid URLs are of the form:
2026
2026
2027 local/filesystem/path (or file://local/filesystem/path)
2027 local/filesystem/path (or file://local/filesystem/path)
2028 http://[user@]host[:port]/[path]
2028 http://[user@]host[:port]/[path]
2029 https://[user@]host[:port]/[path]
2029 https://[user@]host[:port]/[path]
2030 ssh://[user@]host[:port]/[path]
2030 ssh://[user@]host[:port]/[path]
2031 static-http://host[:port]/[path]
2031 static-http://host[:port]/[path]
2032
2032
2033 Paths in the local filesystem can either point to Mercurial
2033 Paths in the local filesystem can either point to Mercurial
2034 repositories or to bundle files (as created by 'hg bundle' or
2034 repositories or to bundle files (as created by 'hg bundle' or
2035 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2035 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2036 allows access to a Mercurial repository where you simply use a web
2036 allows access to a Mercurial repository where you simply use a web
2037 server to publish the .hg directory as static content.
2037 server to publish the .hg directory as static content.
2038
2038
2039 An optional identifier after # indicates a particular branch, tag,
2039 An optional identifier after # indicates a particular branch, tag,
2040 or changeset to pull.
2040 or changeset to pull.
2041
2041
2042 Some notes about using SSH with Mercurial:
2042 Some notes about using SSH with Mercurial:
2043 - SSH requires an accessible shell account on the destination machine
2043 - SSH requires an accessible shell account on the destination machine
2044 and a copy of hg in the remote path or specified with as remotecmd.
2044 and a copy of hg in the remote path or specified with as remotecmd.
2045 - path is relative to the remote user's home directory by default.
2045 - path is relative to the remote user's home directory by default.
2046 Use an extra slash at the start of a path to specify an absolute path:
2046 Use an extra slash at the start of a path to specify an absolute path:
2047 ssh://example.com//tmp/repository
2047 ssh://example.com//tmp/repository
2048 - Mercurial doesn't use its own compression via SSH; the right thing
2048 - Mercurial doesn't use its own compression via SSH; the right thing
2049 to do is to configure it in your ~/.ssh/config, e.g.:
2049 to do is to configure it in your ~/.ssh/config, e.g.:
2050 Host *.mylocalnetwork.example.com
2050 Host *.mylocalnetwork.example.com
2051 Compression no
2051 Compression no
2052 Host *
2052 Host *
2053 Compression yes
2053 Compression yes
2054 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2054 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2055 with the --ssh command line option.
2055 with the --ssh command line option.
2056 """
2056 """
2057 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2057 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2058 cmdutil.setremoteconfig(ui, opts)
2058 cmdutil.setremoteconfig(ui, opts)
2059
2059
2060 other = hg.repository(ui, source)
2060 other = hg.repository(ui, source)
2061 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2061 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2062 if revs:
2062 if revs:
2063 try:
2063 try:
2064 revs = [other.lookup(rev) for rev in revs]
2064 revs = [other.lookup(rev) for rev in revs]
2065 except NoCapability:
2065 except NoCapability:
2066 error = _("Other repository doesn't support revision lookup, "
2066 error = _("Other repository doesn't support revision lookup, "
2067 "so a rev cannot be specified.")
2067 "so a rev cannot be specified.")
2068 raise util.Abort(error)
2068 raise util.Abort(error)
2069
2069
2070 modheads = repo.pull(other, heads=revs, force=opts['force'])
2070 modheads = repo.pull(other, heads=revs, force=opts['force'])
2071 return postincoming(ui, repo, modheads, opts['update'], checkout)
2071 return postincoming(ui, repo, modheads, opts['update'], checkout)
2072
2072
2073 def push(ui, repo, dest=None, **opts):
2073 def push(ui, repo, dest=None, **opts):
2074 """push changes to the specified destination
2074 """push changes to the specified destination
2075
2075
2076 Push changes from the local repository to the given destination.
2076 Push changes from the local repository to the given destination.
2077
2077
2078 This is the symmetrical operation for pull. It helps to move
2078 This is the symmetrical operation for pull. It helps to move
2079 changes from the current repository to a different one. If the
2079 changes from the current repository to a different one. If the
2080 destination is local this is identical to a pull in that directory
2080 destination is local this is identical to a pull in that directory
2081 from the current one.
2081 from the current one.
2082
2082
2083 By default, push will refuse to run if it detects the result would
2083 By default, push will refuse to run if it detects the result would
2084 increase the number of remote heads. This generally indicates the
2084 increase the number of remote heads. This generally indicates the
2085 the client has forgotten to sync and merge before pushing.
2085 the client has forgotten to sync and merge before pushing.
2086
2086
2087 Valid URLs are of the form:
2087 Valid URLs are of the form:
2088
2088
2089 local/filesystem/path (or file://local/filesystem/path)
2089 local/filesystem/path (or file://local/filesystem/path)
2090 ssh://[user@]host[:port]/[path]
2090 ssh://[user@]host[:port]/[path]
2091 http://[user@]host[:port]/[path]
2091 http://[user@]host[:port]/[path]
2092 https://[user@]host[:port]/[path]
2092 https://[user@]host[:port]/[path]
2093
2093
2094 An optional identifier after # indicates a particular branch, tag,
2094 An optional identifier after # indicates a particular branch, tag,
2095 or changeset to push.
2095 or changeset to push.
2096
2096
2097 Look at the help text for the pull command for important details
2097 Look at the help text for the pull command for important details
2098 about ssh:// URLs.
2098 about ssh:// URLs.
2099
2099
2100 Pushing to http:// and https:// URLs is only possible, if this
2100 Pushing to http:// and https:// URLs is only possible, if this
2101 feature is explicitly enabled on the remote Mercurial server.
2101 feature is explicitly enabled on the remote Mercurial server.
2102 """
2102 """
2103 dest, revs, checkout = hg.parseurl(
2103 dest, revs, checkout = hg.parseurl(
2104 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2104 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2105 cmdutil.setremoteconfig(ui, opts)
2105 cmdutil.setremoteconfig(ui, opts)
2106
2106
2107 other = hg.repository(ui, dest)
2107 other = hg.repository(ui, dest)
2108 ui.status('pushing to %s\n' % util.hidepassword(dest))
2108 ui.status('pushing to %s\n' % util.hidepassword(dest))
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 r = repo.push(other, opts['force'], revs=revs)
2111 r = repo.push(other, opts['force'], revs=revs)
2112 return r == 0
2112 return r == 0
2113
2113
2114 def rawcommit(ui, repo, *pats, **opts):
2114 def rawcommit(ui, repo, *pats, **opts):
2115 """raw commit interface (DEPRECATED)
2115 """raw commit interface (DEPRECATED)
2116
2116
2117 (DEPRECATED)
2117 (DEPRECATED)
2118 Lowlevel commit, for use in helper scripts.
2118 Lowlevel commit, for use in helper scripts.
2119
2119
2120 This command is not intended to be used by normal users, as it is
2120 This command is not intended to be used by normal users, as it is
2121 primarily useful for importing from other SCMs.
2121 primarily useful for importing from other SCMs.
2122
2122
2123 This command is now deprecated and will be removed in a future
2123 This command is now deprecated and will be removed in a future
2124 release, please use debugsetparents and commit instead.
2124 release, please use debugsetparents and commit instead.
2125 """
2125 """
2126
2126
2127 ui.warn(_("(the rawcommit command is deprecated)\n"))
2127 ui.warn(_("(the rawcommit command is deprecated)\n"))
2128
2128
2129 message = cmdutil.logmessage(opts)
2129 message = cmdutil.logmessage(opts)
2130
2130
2131 files = cmdutil.match(repo, pats, opts).files()
2131 files = cmdutil.match(repo, pats, opts).files()
2132 if opts['files']:
2132 if opts['files']:
2133 files += open(opts['files']).read().splitlines()
2133 files += open(opts['files']).read().splitlines()
2134
2134
2135 parents = [repo.lookup(p) for p in opts['parent']]
2135 parents = [repo.lookup(p) for p in opts['parent']]
2136
2136
2137 try:
2137 try:
2138 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2138 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2139 except ValueError, inst:
2139 except ValueError, inst:
2140 raise util.Abort(str(inst))
2140 raise util.Abort(str(inst))
2141
2141
2142 def recover(ui, repo):
2142 def recover(ui, repo):
2143 """roll back an interrupted transaction
2143 """roll back an interrupted transaction
2144
2144
2145 Recover from an interrupted commit or pull.
2145 Recover from an interrupted commit or pull.
2146
2146
2147 This command tries to fix the repository status after an interrupted
2147 This command tries to fix the repository status after an interrupted
2148 operation. It should only be necessary when Mercurial suggests it.
2148 operation. It should only be necessary when Mercurial suggests it.
2149 """
2149 """
2150 if repo.recover():
2150 if repo.recover():
2151 return hg.verify(repo)
2151 return hg.verify(repo)
2152 return 1
2152 return 1
2153
2153
2154 def remove(ui, repo, *pats, **opts):
2154 def remove(ui, repo, *pats, **opts):
2155 """remove the specified files on the next commit
2155 """remove the specified files on the next commit
2156
2156
2157 Schedule the indicated files for removal from the repository.
2157 Schedule the indicated files for removal from the repository.
2158
2158
2159 This only removes files from the current branch, not from the entire
2159 This only removes files from the current branch, not from the entire
2160 project history. -A can be used to remove only files that have already
2160 project history. -A can be used to remove only files that have already
2161 been deleted, -f can be used to force deletion, and -Af can be used
2161 been deleted, -f can be used to force deletion, and -Af can be used
2162 to remove files from the next revision without deleting them.
2162 to remove files from the next revision without deleting them.
2163
2163
2164 The following table details the behavior of remove for different file
2164 The following table details the behavior of remove for different file
2165 states (columns) and option combinations (rows). The file states are
2165 states (columns) and option combinations (rows). The file states are
2166 Added, Clean, Modified and Missing (as reported by hg status). The
2166 Added, Clean, Modified and Missing (as reported by hg status). The
2167 actions are Warn, Remove (from branch) and Delete (from disk).
2167 actions are Warn, Remove (from branch) and Delete (from disk).
2168
2168
2169 A C M !
2169 A C M !
2170 none W RD W R
2170 none W RD W R
2171 -f R RD RD R
2171 -f R RD RD R
2172 -A W W W R
2172 -A W W W R
2173 -Af R R R R
2173 -Af R R R R
2174
2174
2175 This command schedules the files to be removed at the next commit.
2175 This command schedules the files to be removed at the next commit.
2176 To undo a remove before that, see hg revert.
2176 To undo a remove before that, see hg revert.
2177 """
2177 """
2178
2178
2179 after, force = opts.get('after'), opts.get('force')
2179 after, force = opts.get('after'), opts.get('force')
2180 if not pats and not after:
2180 if not pats and not after:
2181 raise util.Abort(_('no files specified'))
2181 raise util.Abort(_('no files specified'))
2182
2182
2183 m = cmdutil.match(repo, pats, opts)
2183 m = cmdutil.match(repo, pats, opts)
2184 mardu = map(dict.fromkeys, repo.status(files=m.files(), match=m))[:5]
2184 mardu = map(dict.fromkeys, repo.status(files=m.files(), match=m))[:5]
2185 modified, added, removed, deleted, unknown = mardu
2185 modified, added, removed, deleted, unknown = mardu
2186
2186
2187 remove, forget = [], []
2187 remove, forget = [], []
2188 for abs in repo.walk(m):
2188 for abs in repo.walk(m):
2189
2189
2190 reason = None
2190 reason = None
2191 if abs in removed or abs in unknown:
2191 if abs in removed or abs in unknown:
2192 continue
2192 continue
2193
2193
2194 # last column
2194 # last column
2195 elif abs in deleted:
2195 elif abs in deleted:
2196 remove.append(abs)
2196 remove.append(abs)
2197
2197
2198 # rest of the third row
2198 # rest of the third row
2199 elif after and not force:
2199 elif after and not force:
2200 reason = _('still exists (use -f to force removal)')
2200 reason = _('still exists (use -f to force removal)')
2201
2201
2202 # rest of the first column
2202 # rest of the first column
2203 elif abs in added:
2203 elif abs in added:
2204 if not force:
2204 if not force:
2205 reason = _('has been marked for add (use -f to force removal)')
2205 reason = _('has been marked for add (use -f to force removal)')
2206 else:
2206 else:
2207 forget.append(abs)
2207 forget.append(abs)
2208
2208
2209 # rest of the third column
2209 # rest of the third column
2210 elif abs in modified:
2210 elif abs in modified:
2211 if not force:
2211 if not force:
2212 reason = _('is modified (use -f to force removal)')
2212 reason = _('is modified (use -f to force removal)')
2213 else:
2213 else:
2214 remove.append(abs)
2214 remove.append(abs)
2215
2215
2216 # rest of the second column
2216 # rest of the second column
2217 elif not reason:
2217 elif not reason:
2218 remove.append(abs)
2218 remove.append(abs)
2219
2219
2220 if reason:
2220 if reason:
2221 ui.warn(_('not removing %s: file %s\n') % (m.rel(abs), reason))
2221 ui.warn(_('not removing %s: file %s\n') % (m.rel(abs), reason))
2222 elif ui.verbose or not m.exact(abs):
2222 elif ui.verbose or not m.exact(abs):
2223 ui.status(_('removing %s\n') % m.rel(abs))
2223 ui.status(_('removing %s\n') % m.rel(abs))
2224
2224
2225 repo.forget(forget)
2225 repo.forget(forget)
2226 repo.remove(remove, unlink=not after)
2226 repo.remove(remove, unlink=not after)
2227
2227
2228 def rename(ui, repo, *pats, **opts):
2228 def rename(ui, repo, *pats, **opts):
2229 """rename files; equivalent of copy + remove
2229 """rename files; equivalent of copy + remove
2230
2230
2231 Mark dest as copies of sources; mark sources for deletion. If
2231 Mark dest as copies of sources; mark sources for deletion. If
2232 dest is a directory, copies are put in that directory. If dest is
2232 dest is a directory, copies are put in that directory. If dest is
2233 a file, there can only be one source.
2233 a file, there can only be one source.
2234
2234
2235 By default, this command copies the contents of files as they
2235 By default, this command copies the contents of files as they
2236 stand in the working directory. If invoked with --after, the
2236 stand in the working directory. If invoked with --after, the
2237 operation is recorded, but no copying is performed.
2237 operation is recorded, but no copying is performed.
2238
2238
2239 This command takes effect in the next commit. To undo a rename
2239 This command takes effect in the next commit. To undo a rename
2240 before that, see hg revert.
2240 before that, see hg revert.
2241 """
2241 """
2242 wlock = repo.wlock(False)
2242 wlock = repo.wlock(False)
2243 try:
2243 try:
2244 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2244 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2245 finally:
2245 finally:
2246 del wlock
2246 del wlock
2247
2247
2248 def resolve(ui, repo, *pats, **opts):
2248 def resolve(ui, repo, *pats, **opts):
2249 """resolve file merges from a branch merge or update
2249 """resolve file merges from a branch merge or update
2250
2250
2251 This command will attempt to resolve unresolved merges from the
2251 This command will attempt to resolve unresolved merges from the
2252 last update or merge command. This will use the local file
2252 last update or merge command. This will use the local file
2253 revision preserved at the last update or merge to cleanly retry
2253 revision preserved at the last update or merge to cleanly retry
2254 the file merge attempt. With no file or options specified, this
2254 the file merge attempt. With no file or options specified, this
2255 command will attempt to resolve all unresolved files.
2255 command will attempt to resolve all unresolved files.
2256 """
2256 """
2257
2257
2258 if len([x for x in opts if opts[x]]) > 1:
2258 if len([x for x in opts if opts[x]]) > 1:
2259 raise util.Abort(_("too many options specified"))
2259 raise util.Abort(_("too many options specified"))
2260
2260
2261 ms = merge_.mergestate(repo)
2261 ms = merge_.mergestate(repo)
2262 m = cmdutil.match(repo, pats, opts)
2262 m = cmdutil.match(repo, pats, opts)
2263
2263
2264 for f in ms:
2264 for f in ms:
2265 if m(f):
2265 if m(f):
2266 if opts.get("list"):
2266 if opts.get("list"):
2267 ui.write("%s %s\n" % (ms[f].upper(), f))
2267 ui.write("%s %s\n" % (ms[f].upper(), f))
2268 elif opts.get("mark"):
2268 elif opts.get("mark"):
2269 ms.mark(f, "r")
2269 ms.mark(f, "r")
2270 elif opts.get("unmark"):
2270 elif opts.get("unmark"):
2271 ms.mark(f, "u")
2271 ms.mark(f, "u")
2272 else:
2272 else:
2273 wctx = repo.workingctx()
2273 wctx = repo.workingctx()
2274 mctx = wctx.parents()[-1]
2274 mctx = wctx.parents()[-1]
2275 ms.resolve(f, wctx, mctx)
2275 ms.resolve(f, wctx, mctx)
2276
2276
2277 def revert(ui, repo, *pats, **opts):
2277 def revert(ui, repo, *pats, **opts):
2278 """restore individual files or dirs to an earlier state
2278 """restore individual files or dirs to an earlier state
2279
2279
2280 (use update -r to check out earlier revisions, revert does not
2280 (use update -r to check out earlier revisions, revert does not
2281 change the working dir parents)
2281 change the working dir parents)
2282
2282
2283 With no revision specified, revert the named files or directories
2283 With no revision specified, revert the named files or directories
2284 to the contents they had in the parent of the working directory.
2284 to the contents they had in the parent of the working directory.
2285 This restores the contents of the affected files to an unmodified
2285 This restores the contents of the affected files to an unmodified
2286 state and unschedules adds, removes, copies, and renames. If the
2286 state and unschedules adds, removes, copies, and renames. If the
2287 working directory has two parents, you must explicitly specify the
2287 working directory has two parents, you must explicitly specify the
2288 revision to revert to.
2288 revision to revert to.
2289
2289
2290 Using the -r option, revert the given files or directories to their
2290 Using the -r option, revert the given files or directories to their
2291 contents as of a specific revision. This can be helpful to "roll
2291 contents as of a specific revision. This can be helpful to "roll
2292 back" some or all of an earlier change.
2292 back" some or all of an earlier change.
2293 See 'hg help dates' for a list of formats valid for -d/--date.
2293 See 'hg help dates' for a list of formats valid for -d/--date.
2294
2294
2295 Revert modifies the working directory. It does not commit any
2295 Revert modifies the working directory. It does not commit any
2296 changes, or change the parent of the working directory. If you
2296 changes, or change the parent of the working directory. If you
2297 revert to a revision other than the parent of the working
2297 revert to a revision other than the parent of the working
2298 directory, the reverted files will thus appear modified
2298 directory, the reverted files will thus appear modified
2299 afterwards.
2299 afterwards.
2300
2300
2301 If a file has been deleted, it is restored. If the executable
2301 If a file has been deleted, it is restored. If the executable
2302 mode of a file was changed, it is reset.
2302 mode of a file was changed, it is reset.
2303
2303
2304 If names are given, all files matching the names are reverted.
2304 If names are given, all files matching the names are reverted.
2305 If no arguments are given, no files are reverted.
2305 If no arguments are given, no files are reverted.
2306
2306
2307 Modified files are saved with a .orig suffix before reverting.
2307 Modified files are saved with a .orig suffix before reverting.
2308 To disable these backups, use --no-backup.
2308 To disable these backups, use --no-backup.
2309 """
2309 """
2310
2310
2311 if opts["date"]:
2311 if opts["date"]:
2312 if opts["rev"]:
2312 if opts["rev"]:
2313 raise util.Abort(_("you can't specify a revision and a date"))
2313 raise util.Abort(_("you can't specify a revision and a date"))
2314 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2314 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2315
2315
2316 if not pats and not opts['all']:
2316 if not pats and not opts['all']:
2317 raise util.Abort(_('no files or directories specified; '
2317 raise util.Abort(_('no files or directories specified; '
2318 'use --all to revert the whole repo'))
2318 'use --all to revert the whole repo'))
2319
2319
2320 parent, p2 = repo.dirstate.parents()
2320 parent, p2 = repo.dirstate.parents()
2321 if not opts['rev'] and p2 != nullid:
2321 if not opts['rev'] and p2 != nullid:
2322 raise util.Abort(_('uncommitted merge - please provide a '
2322 raise util.Abort(_('uncommitted merge - please provide a '
2323 'specific revision'))
2323 'specific revision'))
2324 ctx = repo.changectx(opts['rev'])
2324 ctx = repo.changectx(opts['rev'])
2325 node = ctx.node()
2325 node = ctx.node()
2326 mf = ctx.manifest()
2326 mf = ctx.manifest()
2327 if node == parent:
2327 if node == parent:
2328 pmf = mf
2328 pmf = mf
2329 else:
2329 else:
2330 pmf = None
2330 pmf = None
2331
2331
2332 # need all matching names in dirstate and manifest of target rev,
2332 # need all matching names in dirstate and manifest of target rev,
2333 # so have to walk both. do not print errors if files exist in one
2333 # so have to walk both. do not print errors if files exist in one
2334 # but not other.
2334 # but not other.
2335
2335
2336 names = {}
2336 names = {}
2337
2337
2338 wlock = repo.wlock()
2338 wlock = repo.wlock()
2339 try:
2339 try:
2340 # walk dirstate.
2340 # walk dirstate.
2341 files = []
2341 files = []
2342
2342
2343 m = cmdutil.match(repo, pats, opts)
2343 m = cmdutil.match(repo, pats, opts)
2344 m.bad = lambda x,y: False
2344 m.bad = lambda x,y: False
2345 for abs in repo.walk(m):
2345 for abs in repo.walk(m):
2346 names[abs] = m.rel(abs), m.exact(abs)
2346 names[abs] = m.rel(abs), m.exact(abs)
2347
2347
2348 # walk target manifest.
2348 # walk target manifest.
2349
2349
2350 def badfn(path, msg):
2350 def badfn(path, msg):
2351 if path in names:
2351 if path in names:
2352 return False
2352 return False
2353 path_ = path + '/'
2353 path_ = path + '/'
2354 for f in names:
2354 for f in names:
2355 if f.startswith(path_):
2355 if f.startswith(path_):
2356 return False
2356 return False
2357 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2357 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2358 return False
2358 return False
2359
2359
2360 m = cmdutil.match(repo, pats, opts)
2360 m = cmdutil.match(repo, pats, opts)
2361 m.bad = badfn
2361 m.bad = badfn
2362 for abs in repo.walk(m, node=node):
2362 for abs in repo.walk(m, node=node):
2363 if abs not in names:
2363 if abs not in names:
2364 names[abs] = m.rel(abs), m.exact(abs)
2364 names[abs] = m.rel(abs), m.exact(abs)
2365
2365
2366 m = cmdutil.matchfiles(repo, names)
2366 m = cmdutil.matchfiles(repo, names)
2367 changes = repo.status(files=m.files(), match=m)[:4]
2367 changes = repo.status(files=m.files(), match=m)[:4]
2368 modified, added, removed, deleted = map(dict.fromkeys, changes)
2368 modified, added, removed, deleted = map(dict.fromkeys, changes)
2369
2369
2370 # if f is a rename, also revert the source
2370 # if f is a rename, also revert the source
2371 cwd = repo.getcwd()
2371 cwd = repo.getcwd()
2372 for f in added:
2372 for f in added:
2373 src = repo.dirstate.copied(f)
2373 src = repo.dirstate.copied(f)
2374 if src and src not in names and repo.dirstate[src] == 'r':
2374 if src and src not in names and repo.dirstate[src] == 'r':
2375 removed[src] = None
2375 removed[src] = None
2376 names[src] = (repo.pathto(src, cwd), True)
2376 names[src] = (repo.pathto(src, cwd), True)
2377
2377
2378 def removeforget(abs):
2378 def removeforget(abs):
2379 if repo.dirstate[abs] == 'a':
2379 if repo.dirstate[abs] == 'a':
2380 return _('forgetting %s\n')
2380 return _('forgetting %s\n')
2381 return _('removing %s\n')
2381 return _('removing %s\n')
2382
2382
2383 revert = ([], _('reverting %s\n'))
2383 revert = ([], _('reverting %s\n'))
2384 add = ([], _('adding %s\n'))
2384 add = ([], _('adding %s\n'))
2385 remove = ([], removeforget)
2385 remove = ([], removeforget)
2386 undelete = ([], _('undeleting %s\n'))
2386 undelete = ([], _('undeleting %s\n'))
2387
2387
2388 disptable = (
2388 disptable = (
2389 # dispatch table:
2389 # dispatch table:
2390 # file state
2390 # file state
2391 # action if in target manifest
2391 # action if in target manifest
2392 # action if not in target manifest
2392 # action if not in target manifest
2393 # make backup if in target manifest
2393 # make backup if in target manifest
2394 # make backup if not in target manifest
2394 # make backup if not in target manifest
2395 (modified, revert, remove, True, True),
2395 (modified, revert, remove, True, True),
2396 (added, revert, remove, True, False),
2396 (added, revert, remove, True, False),
2397 (removed, undelete, None, False, False),
2397 (removed, undelete, None, False, False),
2398 (deleted, revert, remove, False, False),
2398 (deleted, revert, remove, False, False),
2399 )
2399 )
2400
2400
2401 entries = names.items()
2401 entries = names.items()
2402 entries.sort()
2402 entries.sort()
2403
2403
2404 for abs, (rel, exact) in entries:
2404 for abs, (rel, exact) in entries:
2405 mfentry = mf.get(abs)
2405 mfentry = mf.get(abs)
2406 target = repo.wjoin(abs)
2406 target = repo.wjoin(abs)
2407 def handle(xlist, dobackup):
2407 def handle(xlist, dobackup):
2408 xlist[0].append(abs)
2408 xlist[0].append(abs)
2409 if dobackup and not opts['no_backup'] and util.lexists(target):
2409 if dobackup and not opts['no_backup'] and util.lexists(target):
2410 bakname = "%s.orig" % rel
2410 bakname = "%s.orig" % rel
2411 ui.note(_('saving current version of %s as %s\n') %
2411 ui.note(_('saving current version of %s as %s\n') %
2412 (rel, bakname))
2412 (rel, bakname))
2413 if not opts.get('dry_run'):
2413 if not opts.get('dry_run'):
2414 util.copyfile(target, bakname)
2414 util.copyfile(target, bakname)
2415 if ui.verbose or not exact:
2415 if ui.verbose or not exact:
2416 msg = xlist[1]
2416 msg = xlist[1]
2417 if not isinstance(msg, basestring):
2417 if not isinstance(msg, basestring):
2418 msg = msg(abs)
2418 msg = msg(abs)
2419 ui.status(msg % rel)
2419 ui.status(msg % rel)
2420 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2420 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2421 if abs not in table: continue
2421 if abs not in table: continue
2422 # file has changed in dirstate
2422 # file has changed in dirstate
2423 if mfentry:
2423 if mfentry:
2424 handle(hitlist, backuphit)
2424 handle(hitlist, backuphit)
2425 elif misslist is not None:
2425 elif misslist is not None:
2426 handle(misslist, backupmiss)
2426 handle(misslist, backupmiss)
2427 break
2427 break
2428 else:
2428 else:
2429 if abs not in repo.dirstate:
2429 if abs not in repo.dirstate:
2430 if mfentry:
2430 if mfentry:
2431 handle(add, True)
2431 handle(add, True)
2432 elif exact:
2432 elif exact:
2433 ui.warn(_('file not managed: %s\n') % rel)
2433 ui.warn(_('file not managed: %s\n') % rel)
2434 continue
2434 continue
2435 # file has not changed in dirstate
2435 # file has not changed in dirstate
2436 if node == parent:
2436 if node == parent:
2437 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2437 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2438 continue
2438 continue
2439 if pmf is None:
2439 if pmf is None:
2440 # only need parent manifest in this unlikely case,
2440 # only need parent manifest in this unlikely case,
2441 # so do not read by default
2441 # so do not read by default
2442 pmf = repo.changectx(parent).manifest()
2442 pmf = repo.changectx(parent).manifest()
2443 if abs in pmf:
2443 if abs in pmf:
2444 if mfentry:
2444 if mfentry:
2445 # if version of file is same in parent and target
2445 # if version of file is same in parent and target
2446 # manifests, do nothing
2446 # manifests, do nothing
2447 if (pmf[abs] != mfentry or
2447 if (pmf[abs] != mfentry or
2448 pmf.flags(abs) != mf.flags(abs)):
2448 pmf.flags(abs) != mf.flags(abs)):
2449 handle(revert, False)
2449 handle(revert, False)
2450 else:
2450 else:
2451 handle(remove, False)
2451 handle(remove, False)
2452
2452
2453 if not opts.get('dry_run'):
2453 if not opts.get('dry_run'):
2454 def checkout(f):
2454 def checkout(f):
2455 fc = ctx[f]
2455 fc = ctx[f]
2456 repo.wwrite(f, fc.data(), fc.fileflags())
2456 repo.wwrite(f, fc.data(), fc.fileflags())
2457
2457
2458 audit_path = util.path_auditor(repo.root)
2458 audit_path = util.path_auditor(repo.root)
2459 for f in remove[0]:
2459 for f in remove[0]:
2460 if repo.dirstate[f] == 'a':
2460 if repo.dirstate[f] == 'a':
2461 repo.dirstate.forget(f)
2461 repo.dirstate.forget(f)
2462 continue
2462 continue
2463 audit_path(f)
2463 audit_path(f)
2464 try:
2464 try:
2465 util.unlink(repo.wjoin(f))
2465 util.unlink(repo.wjoin(f))
2466 except OSError:
2466 except OSError:
2467 pass
2467 pass
2468 repo.dirstate.remove(f)
2468 repo.dirstate.remove(f)
2469
2469
2470 normal = None
2470 normal = None
2471 if node == parent:
2471 if node == parent:
2472 # We're reverting to our parent. If possible, we'd like status
2472 # We're reverting to our parent. If possible, we'd like status
2473 # to report the file as clean. We have to use normallookup for
2473 # to report the file as clean. We have to use normallookup for
2474 # merges to avoid losing information about merged/dirty files.
2474 # merges to avoid losing information about merged/dirty files.
2475 if p2 != nullid:
2475 if p2 != nullid:
2476 normal = repo.dirstate.normallookup
2476 normal = repo.dirstate.normallookup
2477 else:
2477 else:
2478 normal = repo.dirstate.normal
2478 normal = repo.dirstate.normal
2479 for f in revert[0]:
2479 for f in revert[0]:
2480 checkout(f)
2480 checkout(f)
2481 if normal:
2481 if normal:
2482 normal(f)
2482 normal(f)
2483
2483
2484 for f in add[0]:
2484 for f in add[0]:
2485 checkout(f)
2485 checkout(f)
2486 repo.dirstate.add(f)
2486 repo.dirstate.add(f)
2487
2487
2488 normal = repo.dirstate.normallookup
2488 normal = repo.dirstate.normallookup
2489 if node == parent and p2 == nullid:
2489 if node == parent and p2 == nullid:
2490 normal = repo.dirstate.normal
2490 normal = repo.dirstate.normal
2491 for f in undelete[0]:
2491 for f in undelete[0]:
2492 checkout(f)
2492 checkout(f)
2493 normal(f)
2493 normal(f)
2494
2494
2495 finally:
2495 finally:
2496 del wlock
2496 del wlock
2497
2497
2498 def rollback(ui, repo):
2498 def rollback(ui, repo):
2499 """roll back the last transaction
2499 """roll back the last transaction
2500
2500
2501 This command should be used with care. There is only one level of
2501 This command should be used with care. There is only one level of
2502 rollback, and there is no way to undo a rollback. It will also
2502 rollback, and there is no way to undo a rollback. It will also
2503 restore the dirstate at the time of the last transaction, losing
2503 restore the dirstate at the time of the last transaction, losing
2504 any dirstate changes since that time.
2504 any dirstate changes since that time.
2505
2505
2506 Transactions are used to encapsulate the effects of all commands
2506 Transactions are used to encapsulate the effects of all commands
2507 that create new changesets or propagate existing changesets into a
2507 that create new changesets or propagate existing changesets into a
2508 repository. For example, the following commands are transactional,
2508 repository. For example, the following commands are transactional,
2509 and their effects can be rolled back:
2509 and their effects can be rolled back:
2510
2510
2511 commit
2511 commit
2512 import
2512 import
2513 pull
2513 pull
2514 push (with this repository as destination)
2514 push (with this repository as destination)
2515 unbundle
2515 unbundle
2516
2516
2517 This command is not intended for use on public repositories. Once
2517 This command is not intended for use on public repositories. Once
2518 changes are visible for pull by other users, rolling a transaction
2518 changes are visible for pull by other users, rolling a transaction
2519 back locally is ineffective (someone else may already have pulled
2519 back locally is ineffective (someone else may already have pulled
2520 the changes). Furthermore, a race is possible with readers of the
2520 the changes). Furthermore, a race is possible with readers of the
2521 repository; for example an in-progress pull from the repository
2521 repository; for example an in-progress pull from the repository
2522 may fail if a rollback is performed.
2522 may fail if a rollback is performed.
2523 """
2523 """
2524 repo.rollback()
2524 repo.rollback()
2525
2525
2526 def root(ui, repo):
2526 def root(ui, repo):
2527 """print the root (top) of the current working dir
2527 """print the root (top) of the current working dir
2528
2528
2529 Print the root directory of the current repository.
2529 Print the root directory of the current repository.
2530 """
2530 """
2531 ui.write(repo.root + "\n")
2531 ui.write(repo.root + "\n")
2532
2532
2533 def serve(ui, repo, **opts):
2533 def serve(ui, repo, **opts):
2534 """export the repository via HTTP
2534 """export the repository via HTTP
2535
2535
2536 Start a local HTTP repository browser and pull server.
2536 Start a local HTTP repository browser and pull server.
2537
2537
2538 By default, the server logs accesses to stdout and errors to
2538 By default, the server logs accesses to stdout and errors to
2539 stderr. Use the "-A" and "-E" options to log to files.
2539 stderr. Use the "-A" and "-E" options to log to files.
2540 """
2540 """
2541
2541
2542 if opts["stdio"]:
2542 if opts["stdio"]:
2543 if repo is None:
2543 if repo is None:
2544 raise RepoError(_("There is no Mercurial repository here"
2544 raise RepoError(_("There is no Mercurial repository here"
2545 " (.hg not found)"))
2545 " (.hg not found)"))
2546 s = sshserver.sshserver(ui, repo)
2546 s = sshserver.sshserver(ui, repo)
2547 s.serve_forever()
2547 s.serve_forever()
2548
2548
2549 parentui = ui.parentui or ui
2549 parentui = ui.parentui or ui
2550 optlist = ("name templates style address port prefix ipv6"
2550 optlist = ("name templates style address port prefix ipv6"
2551 " accesslog errorlog webdir_conf certificate")
2551 " accesslog errorlog webdir_conf certificate")
2552 for o in optlist.split():
2552 for o in optlist.split():
2553 if opts[o]:
2553 if opts[o]:
2554 parentui.setconfig("web", o, str(opts[o]))
2554 parentui.setconfig("web", o, str(opts[o]))
2555 if (repo is not None) and (repo.ui != parentui):
2555 if (repo is not None) and (repo.ui != parentui):
2556 repo.ui.setconfig("web", o, str(opts[o]))
2556 repo.ui.setconfig("web", o, str(opts[o]))
2557
2557
2558 if repo is None and not ui.config("web", "webdir_conf"):
2558 if repo is None and not ui.config("web", "webdir_conf"):
2559 raise RepoError(_("There is no Mercurial repository here"
2559 raise RepoError(_("There is no Mercurial repository here"
2560 " (.hg not found)"))
2560 " (.hg not found)"))
2561
2561
2562 class service:
2562 class service:
2563 def init(self):
2563 def init(self):
2564 util.set_signal_handler()
2564 util.set_signal_handler()
2565 self.httpd = hgweb.server.create_server(parentui, repo)
2565 self.httpd = hgweb.server.create_server(parentui, repo)
2566
2566
2567 if not ui.verbose: return
2567 if not ui.verbose: return
2568
2568
2569 if self.httpd.prefix:
2569 if self.httpd.prefix:
2570 prefix = self.httpd.prefix.strip('/') + '/'
2570 prefix = self.httpd.prefix.strip('/') + '/'
2571 else:
2571 else:
2572 prefix = ''
2572 prefix = ''
2573
2573
2574 port = ':%d' % self.httpd.port
2574 port = ':%d' % self.httpd.port
2575 if port == ':80':
2575 if port == ':80':
2576 port = ''
2576 port = ''
2577
2577
2578 bindaddr = self.httpd.addr
2578 bindaddr = self.httpd.addr
2579 if bindaddr == '0.0.0.0':
2579 if bindaddr == '0.0.0.0':
2580 bindaddr = '*'
2580 bindaddr = '*'
2581 elif ':' in bindaddr: # IPv6
2581 elif ':' in bindaddr: # IPv6
2582 bindaddr = '[%s]' % bindaddr
2582 bindaddr = '[%s]' % bindaddr
2583
2583
2584 fqaddr = self.httpd.fqaddr
2584 fqaddr = self.httpd.fqaddr
2585 if ':' in fqaddr:
2585 if ':' in fqaddr:
2586 fqaddr = '[%s]' % fqaddr
2586 fqaddr = '[%s]' % fqaddr
2587 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2587 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2588 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2588 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2589
2589
2590 def run(self):
2590 def run(self):
2591 self.httpd.serve_forever()
2591 self.httpd.serve_forever()
2592
2592
2593 service = service()
2593 service = service()
2594
2594
2595 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2595 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2596
2596
2597 def status(ui, repo, *pats, **opts):
2597 def status(ui, repo, *pats, **opts):
2598 """show changed files in the working directory
2598 """show changed files in the working directory
2599
2599
2600 Show status of files in the repository. If names are given, only
2600 Show status of files in the repository. If names are given, only
2601 files that match are shown. Files that are clean or ignored or
2601 files that match are shown. Files that are clean or ignored or
2602 source of a copy/move operation, are not listed unless -c (clean),
2602 source of a copy/move operation, are not listed unless -c (clean),
2603 -i (ignored), -C (copies) or -A is given. Unless options described
2603 -i (ignored), -C (copies) or -A is given. Unless options described
2604 with "show only ..." are given, the options -mardu are used.
2604 with "show only ..." are given, the options -mardu are used.
2605
2605
2606 Option -q/--quiet hides untracked (unknown and ignored) files
2606 Option -q/--quiet hides untracked (unknown and ignored) files
2607 unless explicitly requested with -u/--unknown or -i/-ignored.
2607 unless explicitly requested with -u/--unknown or -i/-ignored.
2608
2608
2609 NOTE: status may appear to disagree with diff if permissions have
2609 NOTE: status may appear to disagree with diff if permissions have
2610 changed or a merge has occurred. The standard diff format does not
2610 changed or a merge has occurred. The standard diff format does not
2611 report permission changes and diff only reports changes relative
2611 report permission changes and diff only reports changes relative
2612 to one merge parent.
2612 to one merge parent.
2613
2613
2614 If one revision is given, it is used as the base revision.
2614 If one revision is given, it is used as the base revision.
2615 If two revisions are given, the difference between them is shown.
2615 If two revisions are given, the difference between them is shown.
2616
2616
2617 The codes used to show the status of files are:
2617 The codes used to show the status of files are:
2618 M = modified
2618 M = modified
2619 A = added
2619 A = added
2620 R = removed
2620 R = removed
2621 C = clean
2621 C = clean
2622 ! = deleted, but still tracked
2622 ! = deleted, but still tracked
2623 ? = not tracked
2623 ? = not tracked
2624 I = ignored
2624 I = ignored
2625 = the previous added file was copied from here
2625 = the previous added file was copied from here
2626 """
2626 """
2627
2627
2628 all = opts['all']
2628 all = opts['all']
2629 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2629 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2630
2630
2631 matcher = cmdutil.match(repo, pats, opts)
2631 matcher = cmdutil.match(repo, pats, opts)
2632 cwd = (pats and repo.getcwd()) or ''
2632 cwd = (pats and repo.getcwd()) or ''
2633 modified, added, removed, deleted, unknown, ignored, clean = [
2633 modified, added, removed, deleted, unknown, ignored, clean = [
2634 n for n in repo.status(node1, node2, matcher.files(), matcher,
2634 n for n in repo.status(node1, node2, matcher.files(), matcher,
2635 list_ignored=opts['ignored']
2635 list_ignored=opts['ignored']
2636 or all and not ui.quiet,
2636 or all and not ui.quiet,
2637 list_clean=opts['clean'] or all,
2637 list_clean=opts['clean'] or all,
2638 list_unknown=opts['unknown']
2638 list_unknown=opts['unknown']
2639 or not (ui.quiet or
2639 or not (ui.quiet or
2640 opts['modified'] or
2640 opts['modified'] or
2641 opts['added'] or
2641 opts['added'] or
2642 opts['removed'] or
2642 opts['removed'] or
2643 opts['deleted'] or
2643 opts['deleted'] or
2644 opts['ignored']))]
2644 opts['ignored']))]
2645
2645
2646 changetypes = (('modified', 'M', modified),
2646 changetypes = (('modified', 'M', modified),
2647 ('added', 'A', added),
2647 ('added', 'A', added),
2648 ('removed', 'R', removed),
2648 ('removed', 'R', removed),
2649 ('deleted', '!', deleted),
2649 ('deleted', '!', deleted),
2650 ('unknown', '?', unknown),
2650 ('unknown', '?', unknown),
2651 ('ignored', 'I', ignored))
2651 ('ignored', 'I', ignored))
2652
2652
2653 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2653 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2654
2654
2655 copy = {}
2655 copy = {}
2656 showcopy = {}
2656 showcopy = {}
2657 if ((all or opts.get('copies')) and not opts.get('no_status')):
2657 if ((all or opts.get('copies')) and not opts.get('no_status')):
2658 if opts.get('rev') == []:
2658 if opts.get('rev') == []:
2659 # fast path, more correct with merge parents
2659 # fast path, more correct with merge parents
2660 showcopy = copy = repo.dirstate.copies().copy()
2660 showcopy = copy = repo.dirstate.copies().copy()
2661 else:
2661 else:
2662 ctxn = repo.changectx(nullid)
2662 ctxn = repo.changectx(nullid)
2663 ctx1 = repo.changectx(node1)
2663 ctx1 = repo.changectx(node1)
2664 ctx2 = repo.changectx(node2)
2664 ctx2 = repo.changectx(node2)
2665 if node2 is None:
2665 if node2 is None:
2666 ctx2 = repo.workingctx()
2666 ctx2 = repo.workingctx()
2667 copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn)
2667 copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn)
2668 for k, v in copy.items():
2668 for k, v in copy.items():
2669 copy[v] = k
2669 copy[v] = k
2670
2670
2671 end = opts['print0'] and '\0' or '\n'
2671 end = opts['print0'] and '\0' or '\n'
2672
2672
2673 for opt, char, changes in ([ct for ct in explicit_changetypes
2673 for opt, char, changes in ([ct for ct in explicit_changetypes
2674 if all or opts[ct[0]]]
2674 if all or opts[ct[0]]]
2675 or changetypes):
2675 or changetypes):
2676
2676
2677 if opts['no_status']:
2677 if opts['no_status']:
2678 format = "%%s%s" % end
2678 format = "%%s%s" % end
2679 else:
2679 else:
2680 format = "%s %%s%s" % (char, end)
2680 format = "%s %%s%s" % (char, end)
2681
2681
2682 for f in changes:
2682 for f in changes:
2683 ui.write(format % repo.pathto(f, cwd))
2683 ui.write(format % repo.pathto(f, cwd))
2684 if f in copy and (f in added or f in showcopy):
2684 if f in copy and (f in added or f in showcopy):
2685 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2685 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2686
2686
2687 def tag(ui, repo, name1, *names, **opts):
2687 def tag(ui, repo, name1, *names, **opts):
2688 """add one or more tags for the current or given revision
2688 """add one or more tags for the current or given revision
2689
2689
2690 Name a particular revision using <name>.
2690 Name a particular revision using <name>.
2691
2691
2692 Tags are used to name particular revisions of the repository and are
2692 Tags are used to name particular revisions of the repository and are
2693 very useful to compare different revisions, to go back to significant
2693 very useful to compare different revisions, to go back to significant
2694 earlier versions or to mark branch points as releases, etc.
2694 earlier versions or to mark branch points as releases, etc.
2695
2695
2696 If no revision is given, the parent of the working directory is used,
2696 If no revision is given, the parent of the working directory is used,
2697 or tip if no revision is checked out.
2697 or tip if no revision is checked out.
2698
2698
2699 To facilitate version control, distribution, and merging of tags,
2699 To facilitate version control, distribution, and merging of tags,
2700 they are stored as a file named ".hgtags" which is managed
2700 they are stored as a file named ".hgtags" which is managed
2701 similarly to other project files and can be hand-edited if
2701 similarly to other project files and can be hand-edited if
2702 necessary. The file '.hg/localtags' is used for local tags (not
2702 necessary. The file '.hg/localtags' is used for local tags (not
2703 shared among repositories).
2703 shared among repositories).
2704
2704
2705 See 'hg help dates' for a list of formats valid for -d/--date.
2705 See 'hg help dates' for a list of formats valid for -d/--date.
2706 """
2706 """
2707
2707
2708 rev_ = None
2708 rev_ = None
2709 names = (name1,) + names
2709 names = (name1,) + names
2710 if len(names) != len(dict.fromkeys(names)):
2710 if len(names) != len(dict.fromkeys(names)):
2711 raise util.Abort(_('tag names must be unique'))
2711 raise util.Abort(_('tag names must be unique'))
2712 for n in names:
2712 for n in names:
2713 if n in ['tip', '.', 'null']:
2713 if n in ['tip', '.', 'null']:
2714 raise util.Abort(_('the name \'%s\' is reserved') % n)
2714 raise util.Abort(_('the name \'%s\' is reserved') % n)
2715 if opts['rev'] and opts['remove']:
2715 if opts['rev'] and opts['remove']:
2716 raise util.Abort(_("--rev and --remove are incompatible"))
2716 raise util.Abort(_("--rev and --remove are incompatible"))
2717 if opts['rev']:
2717 if opts['rev']:
2718 rev_ = opts['rev']
2718 rev_ = opts['rev']
2719 message = opts['message']
2719 message = opts['message']
2720 if opts['remove']:
2720 if opts['remove']:
2721 expectedtype = opts['local'] and 'local' or 'global'
2721 expectedtype = opts['local'] and 'local' or 'global'
2722 for n in names:
2722 for n in names:
2723 if not repo.tagtype(n):
2723 if not repo.tagtype(n):
2724 raise util.Abort(_('tag \'%s\' does not exist') % n)
2724 raise util.Abort(_('tag \'%s\' does not exist') % n)
2725 if repo.tagtype(n) != expectedtype:
2725 if repo.tagtype(n) != expectedtype:
2726 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2726 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2727 (n, expectedtype))
2727 (n, expectedtype))
2728 rev_ = nullid
2728 rev_ = nullid
2729 if not message:
2729 if not message:
2730 message = _('Removed tag %s') % ', '.join(names)
2730 message = _('Removed tag %s') % ', '.join(names)
2731 elif not opts['force']:
2731 elif not opts['force']:
2732 for n in names:
2732 for n in names:
2733 if n in repo.tags():
2733 if n in repo.tags():
2734 raise util.Abort(_('tag \'%s\' already exists '
2734 raise util.Abort(_('tag \'%s\' already exists '
2735 '(use -f to force)') % n)
2735 '(use -f to force)') % n)
2736 if not rev_ and repo.dirstate.parents()[1] != nullid:
2736 if not rev_ and repo.dirstate.parents()[1] != nullid:
2737 raise util.Abort(_('uncommitted merge - please provide a '
2737 raise util.Abort(_('uncommitted merge - please provide a '
2738 'specific revision'))
2738 'specific revision'))
2739 r = repo.changectx(rev_).node()
2739 r = repo.changectx(rev_).node()
2740
2740
2741 if not message:
2741 if not message:
2742 message = (_('Added tag %s for changeset %s') %
2742 message = (_('Added tag %s for changeset %s') %
2743 (', '.join(names), short(r)))
2743 (', '.join(names), short(r)))
2744
2744
2745 date = opts.get('date')
2745 date = opts.get('date')
2746 if date:
2746 if date:
2747 date = util.parsedate(date)
2747 date = util.parsedate(date)
2748
2748
2749 repo.tag(names, r, message, opts['local'], opts['user'], date)
2749 repo.tag(names, r, message, opts['local'], opts['user'], date)
2750
2750
2751 def tags(ui, repo):
2751 def tags(ui, repo):
2752 """list repository tags
2752 """list repository tags
2753
2753
2754 List the repository tags.
2754 List the repository tags.
2755
2755
2756 This lists both regular and local tags. When the -v/--verbose switch
2756 This lists both regular and local tags. When the -v/--verbose switch
2757 is used, a third column "local" is printed for local tags.
2757 is used, a third column "local" is printed for local tags.
2758 """
2758 """
2759
2759
2760 l = repo.tagslist()
2760 l = repo.tagslist()
2761 l.reverse()
2761 l.reverse()
2762 hexfunc = ui.debugflag and hex or short
2762 hexfunc = ui.debugflag and hex or short
2763 tagtype = ""
2763 tagtype = ""
2764
2764
2765 for t, n in l:
2765 for t, n in l:
2766 if ui.quiet:
2766 if ui.quiet:
2767 ui.write("%s\n" % t)
2767 ui.write("%s\n" % t)
2768 continue
2768 continue
2769
2769
2770 try:
2770 try:
2771 hn = hexfunc(n)
2771 hn = hexfunc(n)
2772 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2772 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2773 except revlog.LookupError:
2773 except revlog.LookupError:
2774 r = " ?:%s" % hn
2774 r = " ?:%s" % hn
2775 else:
2775 else:
2776 spaces = " " * (30 - util.locallen(t))
2776 spaces = " " * (30 - util.locallen(t))
2777 if ui.verbose:
2777 if ui.verbose:
2778 if repo.tagtype(t) == 'local':
2778 if repo.tagtype(t) == 'local':
2779 tagtype = " local"
2779 tagtype = " local"
2780 else:
2780 else:
2781 tagtype = ""
2781 tagtype = ""
2782 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2782 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2783
2783
2784 def tip(ui, repo, **opts):
2784 def tip(ui, repo, **opts):
2785 """show the tip revision
2785 """show the tip revision
2786
2786
2787 The tip revision (usually just called the tip) is the most
2787 The tip revision (usually just called the tip) is the most
2788 recently added changeset in the repository, the most recently
2788 recently added changeset in the repository, the most recently
2789 changed head.
2789 changed head.
2790
2790
2791 If you have just made a commit, that commit will be the tip. If
2791 If you have just made a commit, that commit will be the tip. If
2792 you have just pulled changes from another repository, the tip of
2792 you have just pulled changes from another repository, the tip of
2793 that repository becomes the current tip. The "tip" tag is special
2793 that repository becomes the current tip. The "tip" tag is special
2794 and cannot be renamed or assigned to a different changeset.
2794 and cannot be renamed or assigned to a different changeset.
2795 """
2795 """
2796 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2796 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2797
2797
2798 def unbundle(ui, repo, fname1, *fnames, **opts):
2798 def unbundle(ui, repo, fname1, *fnames, **opts):
2799 """apply one or more changegroup files
2799 """apply one or more changegroup files
2800
2800
2801 Apply one or more compressed changegroup files generated by the
2801 Apply one or more compressed changegroup files generated by the
2802 bundle command.
2802 bundle command.
2803 """
2803 """
2804 fnames = (fname1,) + fnames
2804 fnames = (fname1,) + fnames
2805
2805
2806 lock = None
2806 lock = None
2807 try:
2807 try:
2808 lock = repo.lock()
2808 lock = repo.lock()
2809 for fname in fnames:
2809 for fname in fnames:
2810 if os.path.exists(fname):
2810 if os.path.exists(fname):
2811 f = open(fname, "rb")
2811 f = open(fname, "rb")
2812 else:
2812 else:
2813 f = urllib.urlopen(fname)
2813 f = urllib.urlopen(fname)
2814 gen = changegroup.readbundle(f, fname)
2814 gen = changegroup.readbundle(f, fname)
2815 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2815 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2816 finally:
2816 finally:
2817 del lock
2817 del lock
2818
2818
2819 return postincoming(ui, repo, modheads, opts['update'], None)
2819 return postincoming(ui, repo, modheads, opts['update'], None)
2820
2820
2821 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2821 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2822 """update working directory
2822 """update working directory
2823
2823
2824 Update the working directory to the specified revision, or the
2824 Update the working directory to the specified revision, or the
2825 tip of the current branch if none is specified.
2825 tip of the current branch if none is specified.
2826
2826
2827 If the requested revision is a descendant of the working
2827 If the requested revision is a descendant of the working
2828 directory, any outstanding changes in the working directory will
2828 directory, any outstanding changes in the working directory will
2829 be merged into the result. If it is not directly descended but is
2829 be merged into the result. If it is not directly descended but is
2830 on the same named branch, update aborts with a suggestion to use
2830 on the same named branch, update aborts with a suggestion to use
2831 merge or update -C instead.
2831 merge or update -C instead.
2832
2832
2833 If the requested revision is on a different named branch and the
2833 If the requested revision is on a different named branch and the
2834 working directory is clean, update quietly switches branches.
2834 working directory is clean, update quietly switches branches.
2835
2835
2836 See 'hg help dates' for a list of formats valid for --date.
2836 See 'hg help dates' for a list of formats valid for --date.
2837 """
2837 """
2838 if rev and node:
2838 if rev and node:
2839 raise util.Abort(_("please specify just one revision"))
2839 raise util.Abort(_("please specify just one revision"))
2840
2840
2841 if not rev:
2841 if not rev:
2842 rev = node
2842 rev = node
2843
2843
2844 if date:
2844 if date:
2845 if rev:
2845 if rev:
2846 raise util.Abort(_("you can't specify a revision and a date"))
2846 raise util.Abort(_("you can't specify a revision and a date"))
2847 rev = cmdutil.finddate(ui, repo, date)
2847 rev = cmdutil.finddate(ui, repo, date)
2848
2848
2849 if clean:
2849 if clean:
2850 return hg.clean(repo, rev)
2850 return hg.clean(repo, rev)
2851 else:
2851 else:
2852 return hg.update(repo, rev)
2852 return hg.update(repo, rev)
2853
2853
2854 def verify(ui, repo):
2854 def verify(ui, repo):
2855 """verify the integrity of the repository
2855 """verify the integrity of the repository
2856
2856
2857 Verify the integrity of the current repository.
2857 Verify the integrity of the current repository.
2858
2858
2859 This will perform an extensive check of the repository's
2859 This will perform an extensive check of the repository's
2860 integrity, validating the hashes and checksums of each entry in
2860 integrity, validating the hashes and checksums of each entry in
2861 the changelog, manifest, and tracked files, as well as the
2861 the changelog, manifest, and tracked files, as well as the
2862 integrity of their crosslinks and indices.
2862 integrity of their crosslinks and indices.
2863 """
2863 """
2864 return hg.verify(repo)
2864 return hg.verify(repo)
2865
2865
2866 def version_(ui):
2866 def version_(ui):
2867 """output version and copyright information"""
2867 """output version and copyright information"""
2868 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2868 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2869 % version.get_version())
2869 % version.get_version())
2870 ui.status(_(
2870 ui.status(_(
2871 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2871 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2872 "This is free software; see the source for copying conditions. "
2872 "This is free software; see the source for copying conditions. "
2873 "There is NO\nwarranty; "
2873 "There is NO\nwarranty; "
2874 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2874 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2875 ))
2875 ))
2876
2876
2877 # Command options and aliases are listed here, alphabetically
2877 # Command options and aliases are listed here, alphabetically
2878
2878
2879 globalopts = [
2879 globalopts = [
2880 ('R', 'repository', '',
2880 ('R', 'repository', '',
2881 _('repository root directory or symbolic path name')),
2881 _('repository root directory or symbolic path name')),
2882 ('', 'cwd', '', _('change working directory')),
2882 ('', 'cwd', '', _('change working directory')),
2883 ('y', 'noninteractive', None,
2883 ('y', 'noninteractive', None,
2884 _('do not prompt, assume \'yes\' for any required answers')),
2884 _('do not prompt, assume \'yes\' for any required answers')),
2885 ('q', 'quiet', None, _('suppress output')),
2885 ('q', 'quiet', None, _('suppress output')),
2886 ('v', 'verbose', None, _('enable additional output')),
2886 ('v', 'verbose', None, _('enable additional output')),
2887 ('', 'config', [], _('set/override config option')),
2887 ('', 'config', [], _('set/override config option')),
2888 ('', 'debug', None, _('enable debugging output')),
2888 ('', 'debug', None, _('enable debugging output')),
2889 ('', 'debugger', None, _('start debugger')),
2889 ('', 'debugger', None, _('start debugger')),
2890 ('', 'encoding', util._encoding, _('set the charset encoding')),
2890 ('', 'encoding', util._encoding, _('set the charset encoding')),
2891 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2891 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2892 ('', 'lsprof', None, _('print improved command execution profile')),
2892 ('', 'lsprof', None, _('print improved command execution profile')),
2893 ('', 'traceback', None, _('print traceback on exception')),
2893 ('', 'traceback', None, _('print traceback on exception')),
2894 ('', 'time', None, _('time how long the command takes')),
2894 ('', 'time', None, _('time how long the command takes')),
2895 ('', 'profile', None, _('print command execution profile')),
2895 ('', 'profile', None, _('print command execution profile')),
2896 ('', 'version', None, _('output version information and exit')),
2896 ('', 'version', None, _('output version information and exit')),
2897 ('h', 'help', None, _('display help and exit')),
2897 ('h', 'help', None, _('display help and exit')),
2898 ]
2898 ]
2899
2899
2900 dryrunopts = [('n', 'dry-run', None,
2900 dryrunopts = [('n', 'dry-run', None,
2901 _('do not perform actions, just print output'))]
2901 _('do not perform actions, just print output'))]
2902
2902
2903 remoteopts = [
2903 remoteopts = [
2904 ('e', 'ssh', '', _('specify ssh command to use')),
2904 ('e', 'ssh', '', _('specify ssh command to use')),
2905 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2905 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2906 ]
2906 ]
2907
2907
2908 walkopts = [
2908 walkopts = [
2909 ('I', 'include', [], _('include names matching the given patterns')),
2909 ('I', 'include', [], _('include names matching the given patterns')),
2910 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2910 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2911 ]
2911 ]
2912
2912
2913 commitopts = [
2913 commitopts = [
2914 ('m', 'message', '', _('use <text> as commit message')),
2914 ('m', 'message', '', _('use <text> as commit message')),
2915 ('l', 'logfile', '', _('read commit message from <file>')),
2915 ('l', 'logfile', '', _('read commit message from <file>')),
2916 ]
2916 ]
2917
2917
2918 commitopts2 = [
2918 commitopts2 = [
2919 ('d', 'date', '', _('record datecode as commit date')),
2919 ('d', 'date', '', _('record datecode as commit date')),
2920 ('u', 'user', '', _('record user as committer')),
2920 ('u', 'user', '', _('record user as committer')),
2921 ]
2921 ]
2922
2922
2923 templateopts = [
2923 templateopts = [
2924 ('', 'style', '', _('display using template map file')),
2924 ('', 'style', '', _('display using template map file')),
2925 ('', 'template', '', _('display with template')),
2925 ('', 'template', '', _('display with template')),
2926 ]
2926 ]
2927
2927
2928 logopts = [
2928 logopts = [
2929 ('p', 'patch', None, _('show patch')),
2929 ('p', 'patch', None, _('show patch')),
2930 ('l', 'limit', '', _('limit number of changes displayed')),
2930 ('l', 'limit', '', _('limit number of changes displayed')),
2931 ('M', 'no-merges', None, _('do not show merges')),
2931 ('M', 'no-merges', None, _('do not show merges')),
2932 ] + templateopts
2932 ] + templateopts
2933
2933
2934 table = {
2934 table = {
2935 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2935 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2936 "addremove":
2936 "addremove":
2937 (addremove,
2937 (addremove,
2938 [('s', 'similarity', '',
2938 [('s', 'similarity', '',
2939 _('guess renamed files by similarity (0<=s<=100)')),
2939 _('guess renamed files by similarity (0<=s<=100)')),
2940 ] + walkopts + dryrunopts,
2940 ] + walkopts + dryrunopts,
2941 _('hg addremove [OPTION]... [FILE]...')),
2941 _('hg addremove [OPTION]... [FILE]...')),
2942 "^annotate|blame":
2942 "^annotate|blame":
2943 (annotate,
2943 (annotate,
2944 [('r', 'rev', '', _('annotate the specified revision')),
2944 [('r', 'rev', '', _('annotate the specified revision')),
2945 ('f', 'follow', None, _('follow file copies and renames')),
2945 ('f', 'follow', None, _('follow file copies and renames')),
2946 ('a', 'text', None, _('treat all files as text')),
2946 ('a', 'text', None, _('treat all files as text')),
2947 ('u', 'user', None, _('list the author (long with -v)')),
2947 ('u', 'user', None, _('list the author (long with -v)')),
2948 ('d', 'date', None, _('list the date (short with -q)')),
2948 ('d', 'date', None, _('list the date (short with -q)')),
2949 ('n', 'number', None, _('list the revision number (default)')),
2949 ('n', 'number', None, _('list the revision number (default)')),
2950 ('c', 'changeset', None, _('list the changeset')),
2950 ('c', 'changeset', None, _('list the changeset')),
2951 ('l', 'line-number', None,
2951 ('l', 'line-number', None,
2952 _('show line number at the first appearance'))
2952 _('show line number at the first appearance'))
2953 ] + walkopts,
2953 ] + walkopts,
2954 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2954 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2955 "archive":
2955 "archive":
2956 (archive,
2956 (archive,
2957 [('', 'no-decode', None, _('do not pass files through decoders')),
2957 [('', 'no-decode', None, _('do not pass files through decoders')),
2958 ('p', 'prefix', '', _('directory prefix for files in archive')),
2958 ('p', 'prefix', '', _('directory prefix for files in archive')),
2959 ('r', 'rev', '', _('revision to distribute')),
2959 ('r', 'rev', '', _('revision to distribute')),
2960 ('t', 'type', '', _('type of distribution to create')),
2960 ('t', 'type', '', _('type of distribution to create')),
2961 ] + walkopts,
2961 ] + walkopts,
2962 _('hg archive [OPTION]... DEST')),
2962 _('hg archive [OPTION]... DEST')),
2963 "backout":
2963 "backout":
2964 (backout,
2964 (backout,
2965 [('', 'merge', None,
2965 [('', 'merge', None,
2966 _('merge with old dirstate parent after backout')),
2966 _('merge with old dirstate parent after backout')),
2967 ('', 'parent', '', _('parent to choose when backing out merge')),
2967 ('', 'parent', '', _('parent to choose when backing out merge')),
2968 ('r', 'rev', '', _('revision to backout')),
2968 ('r', 'rev', '', _('revision to backout')),
2969 ] + walkopts + commitopts + commitopts2,
2969 ] + walkopts + commitopts + commitopts2,
2970 _('hg backout [OPTION]... [-r] REV')),
2970 _('hg backout [OPTION]... [-r] REV')),
2971 "bisect":
2971 "bisect":
2972 (bisect,
2972 (bisect,
2973 [('r', 'reset', False, _('reset bisect state')),
2973 [('r', 'reset', False, _('reset bisect state')),
2974 ('g', 'good', False, _('mark changeset good')),
2974 ('g', 'good', False, _('mark changeset good')),
2975 ('b', 'bad', False, _('mark changeset bad')),
2975 ('b', 'bad', False, _('mark changeset bad')),
2976 ('s', 'skip', False, _('skip testing changeset')),
2976 ('s', 'skip', False, _('skip testing changeset')),
2977 ('U', 'noupdate', False, _('do not update to target'))],
2977 ('U', 'noupdate', False, _('do not update to target'))],
2978 _("hg bisect [-gbsr] [REV]")),
2978 _("hg bisect [-gbsr] [REV]")),
2979 "branch":
2979 "branch":
2980 (branch,
2980 (branch,
2981 [('f', 'force', None,
2981 [('f', 'force', None,
2982 _('set branch name even if it shadows an existing branch'))],
2982 _('set branch name even if it shadows an existing branch'))],
2983 _('hg branch [-f] [NAME]')),
2983 _('hg branch [-f] [NAME]')),
2984 "branches":
2984 "branches":
2985 (branches,
2985 (branches,
2986 [('a', 'active', False,
2986 [('a', 'active', False,
2987 _('show only branches that have unmerged heads'))],
2987 _('show only branches that have unmerged heads'))],
2988 _('hg branches [-a]')),
2988 _('hg branches [-a]')),
2989 "bundle":
2989 "bundle":
2990 (bundle,
2990 (bundle,
2991 [('f', 'force', None,
2991 [('f', 'force', None,
2992 _('run even when remote repository is unrelated')),
2992 _('run even when remote repository is unrelated')),
2993 ('r', 'rev', [],
2993 ('r', 'rev', [],
2994 _('a changeset up to which you would like to bundle')),
2994 _('a changeset up to which you would like to bundle')),
2995 ('', 'base', [],
2995 ('', 'base', [],
2996 _('a base changeset to specify instead of a destination')),
2996 _('a base changeset to specify instead of a destination')),
2997 ('a', 'all', None, _('bundle all changesets in the repository')),
2997 ('a', 'all', None, _('bundle all changesets in the repository')),
2998 ('t', 'type', 'bzip2', _('bundle compression type to use')),
2998 ('t', 'type', 'bzip2', _('bundle compression type to use')),
2999 ] + remoteopts,
2999 ] + remoteopts,
3000 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3000 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3001 "cat":
3001 "cat":
3002 (cat,
3002 (cat,
3003 [('o', 'output', '', _('print output to file with formatted name')),
3003 [('o', 'output', '', _('print output to file with formatted name')),
3004 ('r', 'rev', '', _('print the given revision')),
3004 ('r', 'rev', '', _('print the given revision')),
3005 ('', 'decode', None, _('apply any matching decode filter')),
3005 ('', 'decode', None, _('apply any matching decode filter')),
3006 ] + walkopts,
3006 ] + walkopts,
3007 _('hg cat [OPTION]... FILE...')),
3007 _('hg cat [OPTION]... FILE...')),
3008 "^clone":
3008 "^clone":
3009 (clone,
3009 (clone,
3010 [('U', 'noupdate', None, _('do not update the new working directory')),
3010 [('U', 'noupdate', None, _('do not update the new working directory')),
3011 ('r', 'rev', [],
3011 ('r', 'rev', [],
3012 _('a changeset you would like to have after cloning')),
3012 _('a changeset you would like to have after cloning')),
3013 ('', 'pull', None, _('use pull protocol to copy metadata')),
3013 ('', 'pull', None, _('use pull protocol to copy metadata')),
3014 ('', 'uncompressed', None,
3014 ('', 'uncompressed', None,
3015 _('use uncompressed transfer (fast over LAN)')),
3015 _('use uncompressed transfer (fast over LAN)')),
3016 ] + remoteopts,
3016 ] + remoteopts,
3017 _('hg clone [OPTION]... SOURCE [DEST]')),
3017 _('hg clone [OPTION]... SOURCE [DEST]')),
3018 "^commit|ci":
3018 "^commit|ci":
3019 (commit,
3019 (commit,
3020 [('A', 'addremove', None,
3020 [('A', 'addremove', None,
3021 _('mark new/missing files as added/removed before committing')),
3021 _('mark new/missing files as added/removed before committing')),
3022 ] + walkopts + commitopts + commitopts2,
3022 ] + walkopts + commitopts + commitopts2,
3023 _('hg commit [OPTION]... [FILE]...')),
3023 _('hg commit [OPTION]... [FILE]...')),
3024 "copy|cp":
3024 "copy|cp":
3025 (copy,
3025 (copy,
3026 [('A', 'after', None, _('record a copy that has already occurred')),
3026 [('A', 'after', None, _('record a copy that has already occurred')),
3027 ('f', 'force', None,
3027 ('f', 'force', None,
3028 _('forcibly copy over an existing managed file')),
3028 _('forcibly copy over an existing managed file')),
3029 ] + walkopts + dryrunopts,
3029 ] + walkopts + dryrunopts,
3030 _('hg copy [OPTION]... [SOURCE]... DEST')),
3030 _('hg copy [OPTION]... [SOURCE]... DEST')),
3031 "debugancestor": (debugancestor, [],
3031 "debugancestor": (debugancestor, [],
3032 _('hg debugancestor [INDEX] REV1 REV2')),
3032 _('hg debugancestor [INDEX] REV1 REV2')),
3033 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
3033 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
3034 "debugcomplete":
3034 "debugcomplete":
3035 (debugcomplete,
3035 (debugcomplete,
3036 [('o', 'options', None, _('show the command options'))],
3036 [('o', 'options', None, _('show the command options'))],
3037 _('hg debugcomplete [-o] CMD')),
3037 _('hg debugcomplete [-o] CMD')),
3038 "debugdate":
3038 "debugdate":
3039 (debugdate,
3039 (debugdate,
3040 [('e', 'extended', None, _('try extended date formats'))],
3040 [('e', 'extended', None, _('try extended date formats'))],
3041 _('hg debugdate [-e] DATE [RANGE]')),
3041 _('hg debugdate [-e] DATE [RANGE]')),
3042 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
3042 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
3043 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
3043 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
3044 "debugindex": (debugindex, [], _('hg debugindex FILE')),
3044 "debugindex": (debugindex, [], _('hg debugindex FILE')),
3045 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
3045 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
3046 "debuginstall": (debuginstall, [], _('hg debuginstall')),
3046 "debuginstall": (debuginstall, [], _('hg debuginstall')),
3047 "debugrawcommit|rawcommit":
3047 "debugrawcommit|rawcommit":
3048 (rawcommit,
3048 (rawcommit,
3049 [('p', 'parent', [], _('parent')),
3049 [('p', 'parent', [], _('parent')),
3050 ('F', 'files', '', _('file list'))
3050 ('F', 'files', '', _('file list'))
3051 ] + commitopts + commitopts2,
3051 ] + commitopts + commitopts2,
3052 _('hg debugrawcommit [OPTION]... [FILE]...')),
3052 _('hg debugrawcommit [OPTION]... [FILE]...')),
3053 "debugrebuildstate":
3053 "debugrebuildstate":
3054 (debugrebuildstate,
3054 (debugrebuildstate,
3055 [('r', 'rev', '', _('revision to rebuild to'))],
3055 [('r', 'rev', '', _('revision to rebuild to'))],
3056 _('hg debugrebuildstate [-r REV] [REV]')),
3056 _('hg debugrebuildstate [-r REV] [REV]')),
3057 "debugrename":
3057 "debugrename":
3058 (debugrename,
3058 (debugrename,
3059 [('r', 'rev', '', _('revision to debug'))],
3059 [('r', 'rev', '', _('revision to debug'))],
3060 _('hg debugrename [-r REV] FILE')),
3060 _('hg debugrename [-r REV] FILE')),
3061 "debugsetparents":
3061 "debugsetparents":
3062 (debugsetparents,
3062 (debugsetparents,
3063 [],
3063 [],
3064 _('hg debugsetparents REV1 [REV2]')),
3064 _('hg debugsetparents REV1 [REV2]')),
3065 "debugstate":
3065 "debugstate":
3066 (debugstate,
3066 (debugstate,
3067 [('', 'nodates', None, _('do not display the saved mtime'))],
3067 [('', 'nodates', None, _('do not display the saved mtime'))],
3068 _('hg debugstate [OPTS]')),
3068 _('hg debugstate [OPTS]')),
3069 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3069 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3070 "^diff":
3070 "^diff":
3071 (diff,
3071 (diff,
3072 [('r', 'rev', [], _('revision')),
3072 [('r', 'rev', [], _('revision')),
3073 ('a', 'text', None, _('treat all files as text')),
3073 ('a', 'text', None, _('treat all files as text')),
3074 ('p', 'show-function', None,
3074 ('p', 'show-function', None,
3075 _('show which function each change is in')),
3075 _('show which function each change is in')),
3076 ('g', 'git', None, _('use git extended diff format')),
3076 ('g', 'git', None, _('use git extended diff format')),
3077 ('', 'nodates', None, _("don't include dates in diff headers")),
3077 ('', 'nodates', None, _("don't include dates in diff headers")),
3078 ('w', 'ignore-all-space', None,
3078 ('w', 'ignore-all-space', None,
3079 _('ignore white space when comparing lines')),
3079 _('ignore white space when comparing lines')),
3080 ('b', 'ignore-space-change', None,
3080 ('b', 'ignore-space-change', None,
3081 _('ignore changes in the amount of white space')),
3081 _('ignore changes in the amount of white space')),
3082 ('B', 'ignore-blank-lines', None,
3082 ('B', 'ignore-blank-lines', None,
3083 _('ignore changes whose lines are all blank')),
3083 _('ignore changes whose lines are all blank')),
3084 ('U', 'unified', '',
3084 ('U', 'unified', '',
3085 _('number of lines of context to show'))
3085 _('number of lines of context to show'))
3086 ] + walkopts,
3086 ] + walkopts,
3087 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3087 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3088 "^export":
3088 "^export":
3089 (export,
3089 (export,
3090 [('o', 'output', '', _('print output to file with formatted name')),
3090 [('o', 'output', '', _('print output to file with formatted name')),
3091 ('a', 'text', None, _('treat all files as text')),
3091 ('a', 'text', None, _('treat all files as text')),
3092 ('g', 'git', None, _('use git extended diff format')),
3092 ('g', 'git', None, _('use git extended diff format')),
3093 ('', 'nodates', None, _("don't include dates in diff headers")),
3093 ('', 'nodates', None, _("don't include dates in diff headers")),
3094 ('', 'switch-parent', None, _('diff against the second parent'))],
3094 ('', 'switch-parent', None, _('diff against the second parent'))],
3095 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3095 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3096 "grep":
3096 "grep":
3097 (grep,
3097 (grep,
3098 [('0', 'print0', None, _('end fields with NUL')),
3098 [('0', 'print0', None, _('end fields with NUL')),
3099 ('', 'all', None, _('print all revisions that match')),
3099 ('', 'all', None, _('print all revisions that match')),
3100 ('f', 'follow', None,
3100 ('f', 'follow', None,
3101 _('follow changeset history, or file history across copies and renames')),
3101 _('follow changeset history, or file history across copies and renames')),
3102 ('i', 'ignore-case', None, _('ignore case when matching')),
3102 ('i', 'ignore-case', None, _('ignore case when matching')),
3103 ('l', 'files-with-matches', None,
3103 ('l', 'files-with-matches', None,
3104 _('print only filenames and revs that match')),
3104 _('print only filenames and revs that match')),
3105 ('n', 'line-number', None, _('print matching line numbers')),
3105 ('n', 'line-number', None, _('print matching line numbers')),
3106 ('r', 'rev', [], _('search in given revision range')),
3106 ('r', 'rev', [], _('search in given revision range')),
3107 ('u', 'user', None, _('list the author (long with -v)')),
3107 ('u', 'user', None, _('list the author (long with -v)')),
3108 ('d', 'date', None, _('list the date (short with -q)')),
3108 ('d', 'date', None, _('list the date (short with -q)')),
3109 ] + walkopts,
3109 ] + walkopts,
3110 _('hg grep [OPTION]... PATTERN [FILE]...')),
3110 _('hg grep [OPTION]... PATTERN [FILE]...')),
3111 "heads":
3111 "heads":
3112 (heads,
3112 (heads,
3113 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3113 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3114 ] + templateopts,
3114 ] + templateopts,
3115 _('hg heads [-r REV] [REV]...')),
3115 _('hg heads [-r REV] [REV]...')),
3116 "help": (help_, [], _('hg help [COMMAND]')),
3116 "help": (help_, [], _('hg help [COMMAND]')),
3117 "identify|id":
3117 "identify|id":
3118 (identify,
3118 (identify,
3119 [('r', 'rev', '', _('identify the specified rev')),
3119 [('r', 'rev', '', _('identify the specified rev')),
3120 ('n', 'num', None, _('show local revision number')),
3120 ('n', 'num', None, _('show local revision number')),
3121 ('i', 'id', None, _('show global revision id')),
3121 ('i', 'id', None, _('show global revision id')),
3122 ('b', 'branch', None, _('show branch')),
3122 ('b', 'branch', None, _('show branch')),
3123 ('t', 'tags', None, _('show tags'))],
3123 ('t', 'tags', None, _('show tags'))],
3124 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3124 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3125 "import|patch":
3125 "import|patch":
3126 (import_,
3126 (import_,
3127 [('p', 'strip', 1,
3127 [('p', 'strip', 1,
3128 _('directory strip option for patch. This has the same\n'
3128 _('directory strip option for patch. This has the same\n'
3129 'meaning as the corresponding patch option')),
3129 'meaning as the corresponding patch option')),
3130 ('b', 'base', '', _('base path')),
3130 ('b', 'base', '', _('base path')),
3131 ('f', 'force', None,
3131 ('f', 'force', None,
3132 _('skip check for outstanding uncommitted changes')),
3132 _('skip check for outstanding uncommitted changes')),
3133 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3133 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3134 ('', 'exact', None,
3134 ('', 'exact', None,
3135 _('apply patch to the nodes from which it was generated')),
3135 _('apply patch to the nodes from which it was generated')),
3136 ('', 'import-branch', None,
3136 ('', 'import-branch', None,
3137 _('Use any branch information in patch (implied by --exact)'))] +
3137 _('Use any branch information in patch (implied by --exact)'))] +
3138 commitopts + commitopts2,
3138 commitopts + commitopts2,
3139 _('hg import [OPTION]... PATCH...')),
3139 _('hg import [OPTION]... PATCH...')),
3140 "incoming|in":
3140 "incoming|in":
3141 (incoming,
3141 (incoming,
3142 [('f', 'force', None,
3142 [('f', 'force', None,
3143 _('run even when remote repository is unrelated')),
3143 _('run even when remote repository is unrelated')),
3144 ('n', 'newest-first', None, _('show newest record first')),
3144 ('n', 'newest-first', None, _('show newest record first')),
3145 ('', 'bundle', '', _('file to store the bundles into')),
3145 ('', 'bundle', '', _('file to store the bundles into')),
3146 ('r', 'rev', [],
3146 ('r', 'rev', [],
3147 _('a specific revision up to which you would like to pull')),
3147 _('a specific revision up to which you would like to pull')),
3148 ] + logopts + remoteopts,
3148 ] + logopts + remoteopts,
3149 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3149 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3150 ' [--bundle FILENAME] [SOURCE]')),
3150 ' [--bundle FILENAME] [SOURCE]')),
3151 "^init":
3151 "^init":
3152 (init,
3152 (init,
3153 remoteopts,
3153 remoteopts,
3154 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3154 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3155 "locate":
3155 "locate":
3156 (locate,
3156 (locate,
3157 [('r', 'rev', '', _('search the repository as it stood at rev')),
3157 [('r', 'rev', '', _('search the repository as it stood at rev')),
3158 ('0', 'print0', None,
3158 ('0', 'print0', None,
3159 _('end filenames with NUL, for use with xargs')),
3159 _('end filenames with NUL, for use with xargs')),
3160 ('f', 'fullpath', None,
3160 ('f', 'fullpath', None,
3161 _('print complete paths from the filesystem root')),
3161 _('print complete paths from the filesystem root')),
3162 ] + walkopts,
3162 ] + walkopts,
3163 _('hg locate [OPTION]... [PATTERN]...')),
3163 _('hg locate [OPTION]... [PATTERN]...')),
3164 "^log|history":
3164 "^log|history":
3165 (log,
3165 (log,
3166 [('f', 'follow', None,
3166 [('f', 'follow', None,
3167 _('follow changeset history, or file history across copies and renames')),
3167 _('follow changeset history, or file history across copies and renames')),
3168 ('', 'follow-first', None,
3168 ('', 'follow-first', None,
3169 _('only follow the first parent of merge changesets')),
3169 _('only follow the first parent of merge changesets')),
3170 ('d', 'date', '', _('show revs matching date spec')),
3170 ('d', 'date', '', _('show revs matching date spec')),
3171 ('C', 'copies', None, _('show copied files')),
3171 ('C', 'copies', None, _('show copied files')),
3172 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3172 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3173 ('r', 'rev', [], _('show the specified revision or range')),
3173 ('r', 'rev', [], _('show the specified revision or range')),
3174 ('', 'removed', None, _('include revs where files were removed')),
3174 ('', 'removed', None, _('include revs where files were removed')),
3175 ('m', 'only-merges', None, _('show only merges')),
3175 ('m', 'only-merges', None, _('show only merges')),
3176 ('b', 'only-branch', [],
3176 ('b', 'only-branch', [],
3177 _('show only changesets within the given named branch')),
3177 _('show only changesets within the given named branch')),
3178 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3178 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3179 ] + logopts + walkopts,
3179 ] + logopts + walkopts,
3180 _('hg log [OPTION]... [FILE]')),
3180 _('hg log [OPTION]... [FILE]')),
3181 "manifest":
3181 "manifest":
3182 (manifest,
3182 (manifest,
3183 [('r', 'rev', '', _('revision to display'))],
3183 [('r', 'rev', '', _('revision to display'))],
3184 _('hg manifest [-r REV]')),
3184 _('hg manifest [-r REV]')),
3185 "^merge":
3185 "^merge":
3186 (merge,
3186 (merge,
3187 [('f', 'force', None, _('force a merge with outstanding changes')),
3187 [('f', 'force', None, _('force a merge with outstanding changes')),
3188 ('r', 'rev', '', _('revision to merge')),
3188 ('r', 'rev', '', _('revision to merge')),
3189 ],
3189 ],
3190 _('hg merge [-f] [[-r] REV]')),
3190 _('hg merge [-f] [[-r] REV]')),
3191 "outgoing|out":
3191 "outgoing|out":
3192 (outgoing,
3192 (outgoing,
3193 [('f', 'force', None,
3193 [('f', 'force', None,
3194 _('run even when remote repository is unrelated')),
3194 _('run even when remote repository is unrelated')),
3195 ('r', 'rev', [],
3195 ('r', 'rev', [],
3196 _('a specific revision up to which you would like to push')),
3196 _('a specific revision up to which you would like to push')),
3197 ('n', 'newest-first', None, _('show newest record first')),
3197 ('n', 'newest-first', None, _('show newest record first')),
3198 ] + logopts + remoteopts,
3198 ] + logopts + remoteopts,
3199 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3199 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3200 "^parents":
3200 "^parents":
3201 (parents,
3201 (parents,
3202 [('r', 'rev', '', _('show parents from the specified rev')),
3202 [('r', 'rev', '', _('show parents from the specified rev')),
3203 ] + templateopts,
3203 ] + templateopts,
3204 _('hg parents [-r REV] [FILE]')),
3204 _('hg parents [-r REV] [FILE]')),
3205 "paths": (paths, [], _('hg paths [NAME]')),
3205 "paths": (paths, [], _('hg paths [NAME]')),
3206 "^pull":
3206 "^pull":
3207 (pull,
3207 (pull,
3208 [('u', 'update', None,
3208 [('u', 'update', None,
3209 _('update to new tip if changesets were pulled')),
3209 _('update to new tip if changesets were pulled')),
3210 ('f', 'force', None,
3210 ('f', 'force', None,
3211 _('run even when remote repository is unrelated')),
3211 _('run even when remote repository is unrelated')),
3212 ('r', 'rev', [],
3212 ('r', 'rev', [],
3213 _('a specific revision up to which you would like to pull')),
3213 _('a specific revision up to which you would like to pull')),
3214 ] + remoteopts,
3214 ] + remoteopts,
3215 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3215 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3216 "^push":
3216 "^push":
3217 (push,
3217 (push,
3218 [('f', 'force', None, _('force push')),
3218 [('f', 'force', None, _('force push')),
3219 ('r', 'rev', [],
3219 ('r', 'rev', [],
3220 _('a specific revision up to which you would like to push')),
3220 _('a specific revision up to which you would like to push')),
3221 ] + remoteopts,
3221 ] + remoteopts,
3222 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3222 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3223 "recover": (recover, [], _('hg recover')),
3223 "recover": (recover, [], _('hg recover')),
3224 "^remove|rm":
3224 "^remove|rm":
3225 (remove,
3225 (remove,
3226 [('A', 'after', None, _('record delete for missing files')),
3226 [('A', 'after', None, _('record delete for missing files')),
3227 ('f', 'force', None,
3227 ('f', 'force', None,
3228 _('remove (and delete) file even if added or modified')),
3228 _('remove (and delete) file even if added or modified')),
3229 ] + walkopts,
3229 ] + walkopts,
3230 _('hg remove [OPTION]... FILE...')),
3230 _('hg remove [OPTION]... FILE...')),
3231 "rename|mv":
3231 "rename|mv":
3232 (rename,
3232 (rename,
3233 [('A', 'after', None, _('record a rename that has already occurred')),
3233 [('A', 'after', None, _('record a rename that has already occurred')),
3234 ('f', 'force', None,
3234 ('f', 'force', None,
3235 _('forcibly copy over an existing managed file')),
3235 _('forcibly copy over an existing managed file')),
3236 ] + walkopts + dryrunopts,
3236 ] + walkopts + dryrunopts,
3237 _('hg rename [OPTION]... SOURCE... DEST')),
3237 _('hg rename [OPTION]... SOURCE... DEST')),
3238 "resolve":
3238 "resolve":
3239 (resolve,
3239 (resolve,
3240 [('l', 'list', None, _('list state of files needing merge')),
3240 [('l', 'list', None, _('list state of files needing merge')),
3241 ('m', 'mark', None, _('mark files as resolved')),
3241 ('m', 'mark', None, _('mark files as resolved')),
3242 ('u', 'unmark', None, _('unmark files as resolved'))],
3242 ('u', 'unmark', None, _('unmark files as resolved'))],
3243 ('hg resolve [OPTION] [FILES...]')),
3243 ('hg resolve [OPTION] [FILES...]')),
3244 "revert":
3244 "revert":
3245 (revert,
3245 (revert,
3246 [('a', 'all', None, _('revert all changes when no arguments given')),
3246 [('a', 'all', None, _('revert all changes when no arguments given')),
3247 ('d', 'date', '', _('tipmost revision matching date')),
3247 ('d', 'date', '', _('tipmost revision matching date')),
3248 ('r', 'rev', '', _('revision to revert to')),
3248 ('r', 'rev', '', _('revision to revert to')),
3249 ('', 'no-backup', None, _('do not save backup copies of files')),
3249 ('', 'no-backup', None, _('do not save backup copies of files')),
3250 ] + walkopts + dryrunopts,
3250 ] + walkopts + dryrunopts,
3251 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3251 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3252 "rollback": (rollback, [], _('hg rollback')),
3252 "rollback": (rollback, [], _('hg rollback')),
3253 "root": (root, [], _('hg root')),
3253 "root": (root, [], _('hg root')),
3254 "^serve":
3254 "^serve":
3255 (serve,
3255 (serve,
3256 [('A', 'accesslog', '', _('name of access log file to write to')),
3256 [('A', 'accesslog', '', _('name of access log file to write to')),
3257 ('d', 'daemon', None, _('run server in background')),
3257 ('d', 'daemon', None, _('run server in background')),
3258 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3258 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3259 ('E', 'errorlog', '', _('name of error log file to write to')),
3259 ('E', 'errorlog', '', _('name of error log file to write to')),
3260 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3260 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3261 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3261 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3262 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3262 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3263 ('n', 'name', '',
3263 ('n', 'name', '',
3264 _('name to show in web pages (default: working dir)')),
3264 _('name to show in web pages (default: working dir)')),
3265 ('', 'webdir-conf', '', _('name of the webdir config file'
3265 ('', 'webdir-conf', '', _('name of the webdir config file'
3266 ' (serve more than one repo)')),
3266 ' (serve more than one repo)')),
3267 ('', 'pid-file', '', _('name of file to write process ID to')),
3267 ('', 'pid-file', '', _('name of file to write process ID to')),
3268 ('', 'stdio', None, _('for remote clients')),
3268 ('', 'stdio', None, _('for remote clients')),
3269 ('t', 'templates', '', _('web templates to use')),
3269 ('t', 'templates', '', _('web templates to use')),
3270 ('', 'style', '', _('template style to use')),
3270 ('', 'style', '', _('template style to use')),
3271 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3271 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3272 ('', 'certificate', '', _('SSL certificate file'))],
3272 ('', 'certificate', '', _('SSL certificate file'))],
3273 _('hg serve [OPTION]...')),
3273 _('hg serve [OPTION]...')),
3274 "showconfig|debugconfig":
3274 "showconfig|debugconfig":
3275 (showconfig,
3275 (showconfig,
3276 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3276 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3277 _('hg showconfig [-u] [NAME]...')),
3277 _('hg showconfig [-u] [NAME]...')),
3278 "^status|st":
3278 "^status|st":
3279 (status,
3279 (status,
3280 [('A', 'all', None, _('show status of all files')),
3280 [('A', 'all', None, _('show status of all files')),
3281 ('m', 'modified', None, _('show only modified files')),
3281 ('m', 'modified', None, _('show only modified files')),
3282 ('a', 'added', None, _('show only added files')),
3282 ('a', 'added', None, _('show only added files')),
3283 ('r', 'removed', None, _('show only removed files')),
3283 ('r', 'removed', None, _('show only removed files')),
3284 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3284 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3285 ('c', 'clean', None, _('show only files without changes')),
3285 ('c', 'clean', None, _('show only files without changes')),
3286 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3286 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3287 ('i', 'ignored', None, _('show only ignored files')),
3287 ('i', 'ignored', None, _('show only ignored files')),
3288 ('n', 'no-status', None, _('hide status prefix')),
3288 ('n', 'no-status', None, _('hide status prefix')),
3289 ('C', 'copies', None, _('show source of copied files')),
3289 ('C', 'copies', None, _('show source of copied files')),
3290 ('0', 'print0', None,
3290 ('0', 'print0', None,
3291 _('end filenames with NUL, for use with xargs')),
3291 _('end filenames with NUL, for use with xargs')),
3292 ('', 'rev', [], _('show difference from revision')),
3292 ('', 'rev', [], _('show difference from revision')),
3293 ] + walkopts,
3293 ] + walkopts,
3294 _('hg status [OPTION]... [FILE]...')),
3294 _('hg status [OPTION]... [FILE]...')),
3295 "tag":
3295 "tag":
3296 (tag,
3296 (tag,
3297 [('f', 'force', None, _('replace existing tag')),
3297 [('f', 'force', None, _('replace existing tag')),
3298 ('l', 'local', None, _('make the tag local')),
3298 ('l', 'local', None, _('make the tag local')),
3299 ('r', 'rev', '', _('revision to tag')),
3299 ('r', 'rev', '', _('revision to tag')),
3300 ('', 'remove', None, _('remove a tag')),
3300 ('', 'remove', None, _('remove a tag')),
3301 # -l/--local is already there, commitopts cannot be used
3301 # -l/--local is already there, commitopts cannot be used
3302 ('m', 'message', '', _('use <text> as commit message')),
3302 ('m', 'message', '', _('use <text> as commit message')),
3303 ] + commitopts2,
3303 ] + commitopts2,
3304 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3304 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3305 "tags": (tags, [], _('hg tags')),
3305 "tags": (tags, [], _('hg tags')),
3306 "tip":
3306 "tip":
3307 (tip,
3307 (tip,
3308 [('p', 'patch', None, _('show patch')),
3308 [('p', 'patch', None, _('show patch')),
3309 ] + templateopts,
3309 ] + templateopts,
3310 _('hg tip [-p]')),
3310 _('hg tip [-p]')),
3311 "unbundle":
3311 "unbundle":
3312 (unbundle,
3312 (unbundle,
3313 [('u', 'update', None,
3313 [('u', 'update', None,
3314 _('update to new tip if changesets were unbundled'))],
3314 _('update to new tip if changesets were unbundled'))],
3315 _('hg unbundle [-u] FILE...')),
3315 _('hg unbundle [-u] FILE...')),
3316 "^update|up|checkout|co":
3316 "^update|up|checkout|co":
3317 (update,
3317 (update,
3318 [('C', 'clean', None, _('overwrite locally modified files')),
3318 [('C', 'clean', None, _('overwrite locally modified files')),
3319 ('d', 'date', '', _('tipmost revision matching date')),
3319 ('d', 'date', '', _('tipmost revision matching date')),
3320 ('r', 'rev', '', _('revision'))],
3320 ('r', 'rev', '', _('revision'))],
3321 _('hg update [-C] [-d DATE] [[-r] REV]')),
3321 _('hg update [-C] [-d DATE] [[-r] REV]')),
3322 "verify": (verify, [], _('hg verify')),
3322 "verify": (verify, [], _('hg verify')),
3323 "version": (version_, [], _('hg version')),
3323 "version": (version_, [], _('hg version')),
3324 }
3324 }
3325
3325
3326 norepo = ("clone init version help debugcomplete debugdata"
3326 norepo = ("clone init version help debugcomplete debugdata"
3327 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3327 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3328 optionalrepo = ("identify paths serve showconfig debugancestor")
3328 optionalrepo = ("identify paths serve showconfig debugancestor")
General Comments 0
You need to be logged in to leave comments. Login now