##// END OF EJS Templates
diff: add -b/-B options
Haakon Riiser -
r2580:a20a1bb0 default
parent child Browse files
Show More
@@ -1,3612 +1,3626 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 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 demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 demandload(globals(), "archival cStringIO changegroup email.Parser")
15 demandload(globals(), "archival cStringIO changegroup email.Parser")
16 demandload(globals(), "hgweb.server sshserver")
16 demandload(globals(), "hgweb.server sshserver")
17
17
18 class UnknownCommand(Exception):
18 class UnknownCommand(Exception):
19 """Exception raised if command is not in the command table."""
19 """Exception raised if command is not in the command table."""
20 class AmbiguousCommand(Exception):
20 class AmbiguousCommand(Exception):
21 """Exception raised if command shortcut matches more than one command."""
21 """Exception raised if command shortcut matches more than one command."""
22
22
23 def bail_if_changed(repo):
23 def bail_if_changed(repo):
24 modified, added, removed, deleted, unknown = repo.changes()
24 modified, added, removed, deleted, unknown = repo.changes()
25 if modified or added or removed or deleted:
25 if modified or added or removed or deleted:
26 raise util.Abort(_("outstanding uncommitted changes"))
26 raise util.Abort(_("outstanding uncommitted changes"))
27
27
28 def filterfiles(filters, files):
28 def filterfiles(filters, files):
29 l = [x for x in files if x in filters]
29 l = [x for x in files if x in filters]
30
30
31 for t in filters:
31 for t in filters:
32 if t and t[-1] != "/":
32 if t and t[-1] != "/":
33 t += "/"
33 t += "/"
34 l += [x for x in files if x.startswith(t)]
34 l += [x for x in files if x.startswith(t)]
35 return l
35 return l
36
36
37 def relpath(repo, args):
37 def relpath(repo, args):
38 cwd = repo.getcwd()
38 cwd = repo.getcwd()
39 if cwd:
39 if cwd:
40 return [util.normpath(os.path.join(cwd, x)) for x in args]
40 return [util.normpath(os.path.join(cwd, x)) for x in args]
41 return args
41 return args
42
42
43 def matchpats(repo, pats=[], opts={}, head=''):
43 def matchpats(repo, pats=[], opts={}, head=''):
44 cwd = repo.getcwd()
44 cwd = repo.getcwd()
45 if not pats and cwd:
45 if not pats and cwd:
46 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
46 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
47 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
47 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
48 cwd = ''
48 cwd = ''
49 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
49 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
50 opts.get('exclude'), head)
50 opts.get('exclude'), head)
51
51
52 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
52 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
53 files, matchfn, anypats = matchpats(repo, pats, opts, head)
53 files, matchfn, anypats = matchpats(repo, pats, opts, head)
54 exact = dict(zip(files, files))
54 exact = dict(zip(files, files))
55 def walk():
55 def walk():
56 for src, fn in repo.walk(node=node, files=files, match=matchfn,
56 for src, fn in repo.walk(node=node, files=files, match=matchfn,
57 badmatch=badmatch):
57 badmatch=badmatch):
58 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
58 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
59 return files, matchfn, walk()
59 return files, matchfn, walk()
60
60
61 def walk(repo, pats, opts, node=None, head='', badmatch=None):
61 def walk(repo, pats, opts, node=None, head='', badmatch=None):
62 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
62 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
63 for r in results:
63 for r in results:
64 yield r
64 yield r
65
65
66 def walkchangerevs(ui, repo, pats, opts):
66 def walkchangerevs(ui, repo, pats, opts):
67 '''Iterate over files and the revs they changed in.
67 '''Iterate over files and the revs they changed in.
68
68
69 Callers most commonly need to iterate backwards over the history
69 Callers most commonly need to iterate backwards over the history
70 it is interested in. Doing so has awful (quadratic-looking)
70 it is interested in. Doing so has awful (quadratic-looking)
71 performance, so we use iterators in a "windowed" way.
71 performance, so we use iterators in a "windowed" way.
72
72
73 We walk a window of revisions in the desired order. Within the
73 We walk a window of revisions in the desired order. Within the
74 window, we first walk forwards to gather data, then in the desired
74 window, we first walk forwards to gather data, then in the desired
75 order (usually backwards) to display it.
75 order (usually backwards) to display it.
76
76
77 This function returns an (iterator, getchange, matchfn) tuple. The
77 This function returns an (iterator, getchange, matchfn) tuple. The
78 getchange function returns the changelog entry for a numeric
78 getchange function returns the changelog entry for a numeric
79 revision. The iterator yields 3-tuples. They will be of one of
79 revision. The iterator yields 3-tuples. They will be of one of
80 the following forms:
80 the following forms:
81
81
82 "window", incrementing, lastrev: stepping through a window,
82 "window", incrementing, lastrev: stepping through a window,
83 positive if walking forwards through revs, last rev in the
83 positive if walking forwards through revs, last rev in the
84 sequence iterated over - use to reset state for the current window
84 sequence iterated over - use to reset state for the current window
85
85
86 "add", rev, fns: out-of-order traversal of the given file names
86 "add", rev, fns: out-of-order traversal of the given file names
87 fns, which changed during revision rev - use to gather data for
87 fns, which changed during revision rev - use to gather data for
88 possible display
88 possible display
89
89
90 "iter", rev, None: in-order traversal of the revs earlier iterated
90 "iter", rev, None: in-order traversal of the revs earlier iterated
91 over with "add" - use to display data'''
91 over with "add" - use to display data'''
92
92
93 def increasing_windows(start, end, windowsize=8, sizelimit=512):
93 def increasing_windows(start, end, windowsize=8, sizelimit=512):
94 if start < end:
94 if start < end:
95 while start < end:
95 while start < end:
96 yield start, min(windowsize, end-start)
96 yield start, min(windowsize, end-start)
97 start += windowsize
97 start += windowsize
98 if windowsize < sizelimit:
98 if windowsize < sizelimit:
99 windowsize *= 2
99 windowsize *= 2
100 else:
100 else:
101 while start > end:
101 while start > end:
102 yield start, min(windowsize, start-end-1)
102 yield start, min(windowsize, start-end-1)
103 start -= windowsize
103 start -= windowsize
104 if windowsize < sizelimit:
104 if windowsize < sizelimit:
105 windowsize *= 2
105 windowsize *= 2
106
106
107
107
108 files, matchfn, anypats = matchpats(repo, pats, opts)
108 files, matchfn, anypats = matchpats(repo, pats, opts)
109
109
110 if repo.changelog.count() == 0:
110 if repo.changelog.count() == 0:
111 return [], False, matchfn
111 return [], False, matchfn
112
112
113 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
113 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
114 wanted = {}
114 wanted = {}
115 slowpath = anypats
115 slowpath = anypats
116 fncache = {}
116 fncache = {}
117
117
118 chcache = {}
118 chcache = {}
119 def getchange(rev):
119 def getchange(rev):
120 ch = chcache.get(rev)
120 ch = chcache.get(rev)
121 if ch is None:
121 if ch is None:
122 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
122 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
123 return ch
123 return ch
124
124
125 if not slowpath and not files:
125 if not slowpath and not files:
126 # No files, no patterns. Display all revs.
126 # No files, no patterns. Display all revs.
127 wanted = dict(zip(revs, revs))
127 wanted = dict(zip(revs, revs))
128 if not slowpath:
128 if not slowpath:
129 # Only files, no patterns. Check the history of each file.
129 # Only files, no patterns. Check the history of each file.
130 def filerevgen(filelog):
130 def filerevgen(filelog):
131 for i, window in increasing_windows(filelog.count()-1, -1):
131 for i, window in increasing_windows(filelog.count()-1, -1):
132 revs = []
132 revs = []
133 for j in xrange(i - window, i + 1):
133 for j in xrange(i - window, i + 1):
134 revs.append(filelog.linkrev(filelog.node(j)))
134 revs.append(filelog.linkrev(filelog.node(j)))
135 revs.reverse()
135 revs.reverse()
136 for rev in revs:
136 for rev in revs:
137 yield rev
137 yield rev
138
138
139 minrev, maxrev = min(revs), max(revs)
139 minrev, maxrev = min(revs), max(revs)
140 for file_ in files:
140 for file_ in files:
141 filelog = repo.file(file_)
141 filelog = repo.file(file_)
142 # A zero count may be a directory or deleted file, so
142 # A zero count may be a directory or deleted file, so
143 # try to find matching entries on the slow path.
143 # try to find matching entries on the slow path.
144 if filelog.count() == 0:
144 if filelog.count() == 0:
145 slowpath = True
145 slowpath = True
146 break
146 break
147 for rev in filerevgen(filelog):
147 for rev in filerevgen(filelog):
148 if rev <= maxrev:
148 if rev <= maxrev:
149 if rev < minrev:
149 if rev < minrev:
150 break
150 break
151 fncache.setdefault(rev, [])
151 fncache.setdefault(rev, [])
152 fncache[rev].append(file_)
152 fncache[rev].append(file_)
153 wanted[rev] = 1
153 wanted[rev] = 1
154 if slowpath:
154 if slowpath:
155 # The slow path checks files modified in every changeset.
155 # The slow path checks files modified in every changeset.
156 def changerevgen():
156 def changerevgen():
157 for i, window in increasing_windows(repo.changelog.count()-1, -1):
157 for i, window in increasing_windows(repo.changelog.count()-1, -1):
158 for j in xrange(i - window, i + 1):
158 for j in xrange(i - window, i + 1):
159 yield j, getchange(j)[3]
159 yield j, getchange(j)[3]
160
160
161 for rev, changefiles in changerevgen():
161 for rev, changefiles in changerevgen():
162 matches = filter(matchfn, changefiles)
162 matches = filter(matchfn, changefiles)
163 if matches:
163 if matches:
164 fncache[rev] = matches
164 fncache[rev] = matches
165 wanted[rev] = 1
165 wanted[rev] = 1
166
166
167 def iterate():
167 def iterate():
168 for i, window in increasing_windows(0, len(revs)):
168 for i, window in increasing_windows(0, len(revs)):
169 yield 'window', revs[0] < revs[-1], revs[-1]
169 yield 'window', revs[0] < revs[-1], revs[-1]
170 nrevs = [rev for rev in revs[i:i+window]
170 nrevs = [rev for rev in revs[i:i+window]
171 if rev in wanted]
171 if rev in wanted]
172 srevs = list(nrevs)
172 srevs = list(nrevs)
173 srevs.sort()
173 srevs.sort()
174 for rev in srevs:
174 for rev in srevs:
175 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
175 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
176 yield 'add', rev, fns
176 yield 'add', rev, fns
177 for rev in nrevs:
177 for rev in nrevs:
178 yield 'iter', rev, None
178 yield 'iter', rev, None
179 return iterate(), getchange, matchfn
179 return iterate(), getchange, matchfn
180
180
181 revrangesep = ':'
181 revrangesep = ':'
182
182
183 def revfix(repo, val, defval):
183 def revfix(repo, val, defval):
184 '''turn user-level id of changeset into rev number.
184 '''turn user-level id of changeset into rev number.
185 user-level id can be tag, changeset, rev number, or negative rev
185 user-level id can be tag, changeset, rev number, or negative rev
186 number relative to number of revs (-1 is tip, etc).'''
186 number relative to number of revs (-1 is tip, etc).'''
187 if not val:
187 if not val:
188 return defval
188 return defval
189 try:
189 try:
190 num = int(val)
190 num = int(val)
191 if str(num) != val:
191 if str(num) != val:
192 raise ValueError
192 raise ValueError
193 if num < 0:
193 if num < 0:
194 num += repo.changelog.count()
194 num += repo.changelog.count()
195 if num < 0:
195 if num < 0:
196 num = 0
196 num = 0
197 elif num >= repo.changelog.count():
197 elif num >= repo.changelog.count():
198 raise ValueError
198 raise ValueError
199 except ValueError:
199 except ValueError:
200 try:
200 try:
201 num = repo.changelog.rev(repo.lookup(val))
201 num = repo.changelog.rev(repo.lookup(val))
202 except KeyError:
202 except KeyError:
203 raise util.Abort(_('invalid revision identifier %s'), val)
203 raise util.Abort(_('invalid revision identifier %s'), val)
204 return num
204 return num
205
205
206 def revpair(ui, repo, revs):
206 def revpair(ui, repo, revs):
207 '''return pair of nodes, given list of revisions. second item can
207 '''return pair of nodes, given list of revisions. second item can
208 be None, meaning use working dir.'''
208 be None, meaning use working dir.'''
209 if not revs:
209 if not revs:
210 return repo.dirstate.parents()[0], None
210 return repo.dirstate.parents()[0], None
211 end = None
211 end = None
212 if len(revs) == 1:
212 if len(revs) == 1:
213 start = revs[0]
213 start = revs[0]
214 if revrangesep in start:
214 if revrangesep in start:
215 start, end = start.split(revrangesep, 1)
215 start, end = start.split(revrangesep, 1)
216 start = revfix(repo, start, 0)
216 start = revfix(repo, start, 0)
217 end = revfix(repo, end, repo.changelog.count() - 1)
217 end = revfix(repo, end, repo.changelog.count() - 1)
218 else:
218 else:
219 start = revfix(repo, start, None)
219 start = revfix(repo, start, None)
220 elif len(revs) == 2:
220 elif len(revs) == 2:
221 if revrangesep in revs[0] or revrangesep in revs[1]:
221 if revrangesep in revs[0] or revrangesep in revs[1]:
222 raise util.Abort(_('too many revisions specified'))
222 raise util.Abort(_('too many revisions specified'))
223 start = revfix(repo, revs[0], None)
223 start = revfix(repo, revs[0], None)
224 end = revfix(repo, revs[1], None)
224 end = revfix(repo, revs[1], None)
225 else:
225 else:
226 raise util.Abort(_('too many revisions specified'))
226 raise util.Abort(_('too many revisions specified'))
227 if end is not None: end = repo.lookup(str(end))
227 if end is not None: end = repo.lookup(str(end))
228 return repo.lookup(str(start)), end
228 return repo.lookup(str(start)), end
229
229
230 def revrange(ui, repo, revs):
230 def revrange(ui, repo, revs):
231 """Yield revision as strings from a list of revision specifications."""
231 """Yield revision as strings from a list of revision specifications."""
232 seen = {}
232 seen = {}
233 for spec in revs:
233 for spec in revs:
234 if revrangesep in spec:
234 if revrangesep in spec:
235 start, end = spec.split(revrangesep, 1)
235 start, end = spec.split(revrangesep, 1)
236 start = revfix(repo, start, 0)
236 start = revfix(repo, start, 0)
237 end = revfix(repo, end, repo.changelog.count() - 1)
237 end = revfix(repo, end, repo.changelog.count() - 1)
238 step = start > end and -1 or 1
238 step = start > end and -1 or 1
239 for rev in xrange(start, end+step, step):
239 for rev in xrange(start, end+step, step):
240 if rev in seen:
240 if rev in seen:
241 continue
241 continue
242 seen[rev] = 1
242 seen[rev] = 1
243 yield str(rev)
243 yield str(rev)
244 else:
244 else:
245 rev = revfix(repo, spec, None)
245 rev = revfix(repo, spec, None)
246 if rev in seen:
246 if rev in seen:
247 continue
247 continue
248 seen[rev] = 1
248 seen[rev] = 1
249 yield str(rev)
249 yield str(rev)
250
250
251 def make_filename(repo, pat, node,
251 def make_filename(repo, pat, node,
252 total=None, seqno=None, revwidth=None, pathname=None):
252 total=None, seqno=None, revwidth=None, pathname=None):
253 node_expander = {
253 node_expander = {
254 'H': lambda: hex(node),
254 'H': lambda: hex(node),
255 'R': lambda: str(repo.changelog.rev(node)),
255 'R': lambda: str(repo.changelog.rev(node)),
256 'h': lambda: short(node),
256 'h': lambda: short(node),
257 }
257 }
258 expander = {
258 expander = {
259 '%': lambda: '%',
259 '%': lambda: '%',
260 'b': lambda: os.path.basename(repo.root),
260 'b': lambda: os.path.basename(repo.root),
261 }
261 }
262
262
263 try:
263 try:
264 if node:
264 if node:
265 expander.update(node_expander)
265 expander.update(node_expander)
266 if node and revwidth is not None:
266 if node and revwidth is not None:
267 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
267 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
268 if total is not None:
268 if total is not None:
269 expander['N'] = lambda: str(total)
269 expander['N'] = lambda: str(total)
270 if seqno is not None:
270 if seqno is not None:
271 expander['n'] = lambda: str(seqno)
271 expander['n'] = lambda: str(seqno)
272 if total is not None and seqno is not None:
272 if total is not None and seqno is not None:
273 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
273 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
274 if pathname is not None:
274 if pathname is not None:
275 expander['s'] = lambda: os.path.basename(pathname)
275 expander['s'] = lambda: os.path.basename(pathname)
276 expander['d'] = lambda: os.path.dirname(pathname) or '.'
276 expander['d'] = lambda: os.path.dirname(pathname) or '.'
277 expander['p'] = lambda: pathname
277 expander['p'] = lambda: pathname
278
278
279 newname = []
279 newname = []
280 patlen = len(pat)
280 patlen = len(pat)
281 i = 0
281 i = 0
282 while i < patlen:
282 while i < patlen:
283 c = pat[i]
283 c = pat[i]
284 if c == '%':
284 if c == '%':
285 i += 1
285 i += 1
286 c = pat[i]
286 c = pat[i]
287 c = expander[c]()
287 c = expander[c]()
288 newname.append(c)
288 newname.append(c)
289 i += 1
289 i += 1
290 return ''.join(newname)
290 return ''.join(newname)
291 except KeyError, inst:
291 except KeyError, inst:
292 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
292 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
293 inst.args[0])
293 inst.args[0])
294
294
295 def make_file(repo, pat, node=None,
295 def make_file(repo, pat, node=None,
296 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
296 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
297 if not pat or pat == '-':
297 if not pat or pat == '-':
298 return 'w' in mode and sys.stdout or sys.stdin
298 return 'w' in mode and sys.stdout or sys.stdin
299 if hasattr(pat, 'write') and 'w' in mode:
299 if hasattr(pat, 'write') and 'w' in mode:
300 return pat
300 return pat
301 if hasattr(pat, 'read') and 'r' in mode:
301 if hasattr(pat, 'read') and 'r' in mode:
302 return pat
302 return pat
303 return open(make_filename(repo, pat, node, total, seqno, revwidth,
303 return open(make_filename(repo, pat, node, total, seqno, revwidth,
304 pathname),
304 pathname),
305 mode)
305 mode)
306
306
307 def write_bundle(cg, filename=None, compress=True):
307 def write_bundle(cg, filename=None, compress=True):
308 """Write a bundle file and return its filename.
308 """Write a bundle file and return its filename.
309
309
310 Existing files will not be overwritten.
310 Existing files will not be overwritten.
311 If no filename is specified, a temporary file is created.
311 If no filename is specified, a temporary file is created.
312 bz2 compression can be turned off.
312 bz2 compression can be turned off.
313 The bundle file will be deleted in case of errors.
313 The bundle file will be deleted in case of errors.
314 """
314 """
315 class nocompress(object):
315 class nocompress(object):
316 def compress(self, x):
316 def compress(self, x):
317 return x
317 return x
318 def flush(self):
318 def flush(self):
319 return ""
319 return ""
320
320
321 fh = None
321 fh = None
322 cleanup = None
322 cleanup = None
323 try:
323 try:
324 if filename:
324 if filename:
325 if os.path.exists(filename):
325 if os.path.exists(filename):
326 raise util.Abort(_("file '%s' already exists"), filename)
326 raise util.Abort(_("file '%s' already exists"), filename)
327 fh = open(filename, "wb")
327 fh = open(filename, "wb")
328 else:
328 else:
329 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
329 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
330 fh = os.fdopen(fd, "wb")
330 fh = os.fdopen(fd, "wb")
331 cleanup = filename
331 cleanup = filename
332
332
333 if compress:
333 if compress:
334 fh.write("HG10")
334 fh.write("HG10")
335 z = bz2.BZ2Compressor(9)
335 z = bz2.BZ2Compressor(9)
336 else:
336 else:
337 fh.write("HG10UN")
337 fh.write("HG10UN")
338 z = nocompress()
338 z = nocompress()
339 # parse the changegroup data, otherwise we will block
339 # parse the changegroup data, otherwise we will block
340 # in case of sshrepo because we don't know the end of the stream
340 # in case of sshrepo because we don't know the end of the stream
341
341
342 # an empty chunkiter is the end of the changegroup
342 # an empty chunkiter is the end of the changegroup
343 empty = False
343 empty = False
344 while not empty:
344 while not empty:
345 empty = True
345 empty = True
346 for chunk in changegroup.chunkiter(cg):
346 for chunk in changegroup.chunkiter(cg):
347 empty = False
347 empty = False
348 fh.write(z.compress(changegroup.genchunk(chunk)))
348 fh.write(z.compress(changegroup.genchunk(chunk)))
349 fh.write(z.compress(changegroup.closechunk()))
349 fh.write(z.compress(changegroup.closechunk()))
350 fh.write(z.flush())
350 fh.write(z.flush())
351 cleanup = None
351 cleanup = None
352 return filename
352 return filename
353 finally:
353 finally:
354 if fh is not None:
354 if fh is not None:
355 fh.close()
355 fh.close()
356 if cleanup is not None:
356 if cleanup is not None:
357 os.unlink(cleanup)
357 os.unlink(cleanup)
358
358
359 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
359 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
360 changes=None, text=False, opts={}):
360 changes=None, text=False, opts={}):
361 if not node1:
361 if not node1:
362 node1 = repo.dirstate.parents()[0]
362 node1 = repo.dirstate.parents()[0]
363 # reading the data for node1 early allows it to play nicely
363 # reading the data for node1 early allows it to play nicely
364 # with repo.changes and the revlog cache.
364 # with repo.changes and the revlog cache.
365 change = repo.changelog.read(node1)
365 change = repo.changelog.read(node1)
366 mmap = repo.manifest.read(change[0])
366 mmap = repo.manifest.read(change[0])
367 date1 = util.datestr(change[2])
367 date1 = util.datestr(change[2])
368
368
369 if not changes:
369 if not changes:
370 changes = repo.changes(node1, node2, files, match=match)
370 changes = repo.changes(node1, node2, files, match=match)
371 modified, added, removed, deleted, unknown = changes
371 modified, added, removed, deleted, unknown = changes
372 if files:
372 if files:
373 modified, added, removed = map(lambda x: filterfiles(files, x),
373 modified, added, removed = map(lambda x: filterfiles(files, x),
374 (modified, added, removed))
374 (modified, added, removed))
375
375
376 if not modified and not added and not removed:
376 if not modified and not added and not removed:
377 return
377 return
378
378
379 if node2:
379 if node2:
380 change = repo.changelog.read(node2)
380 change = repo.changelog.read(node2)
381 mmap2 = repo.manifest.read(change[0])
381 mmap2 = repo.manifest.read(change[0])
382 _date2 = util.datestr(change[2])
382 _date2 = util.datestr(change[2])
383 def date2(f):
383 def date2(f):
384 return _date2
384 return _date2
385 def read(f):
385 def read(f):
386 return repo.file(f).read(mmap2[f])
386 return repo.file(f).read(mmap2[f])
387 else:
387 else:
388 tz = util.makedate()[1]
388 tz = util.makedate()[1]
389 _date2 = util.datestr()
389 _date2 = util.datestr()
390 def date2(f):
390 def date2(f):
391 try:
391 try:
392 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
392 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
393 except OSError, err:
393 except OSError, err:
394 if err.errno != errno.ENOENT: raise
394 if err.errno != errno.ENOENT: raise
395 return _date2
395 return _date2
396 def read(f):
396 def read(f):
397 return repo.wread(f)
397 return repo.wread(f)
398
398
399 if ui.quiet:
399 if ui.quiet:
400 r = None
400 r = None
401 else:
401 else:
402 hexfunc = ui.verbose and hex or short
402 hexfunc = ui.verbose and hex or short
403 r = [hexfunc(node) for node in [node1, node2] if node]
403 r = [hexfunc(node) for node in [node1, node2] if node]
404
404
405 diffopts = ui.diffopts()
405 diffopts = ui.diffopts()
406 showfunc = opts.get('show_function') or diffopts['showfunc']
406 showfunc = opts.get('show_function') or diffopts['showfunc']
407 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
407 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
408 ignorewsamount = opts.get('ignore_space_change') or \
409 diffopts['ignorewsamount']
410 ignoreblanklines = opts.get('ignore_blank_lines') or \
411 diffopts['ignoreblanklines']
408 for f in modified:
412 for f in modified:
409 to = None
413 to = None
410 if f in mmap:
414 if f in mmap:
411 to = repo.file(f).read(mmap[f])
415 to = repo.file(f).read(mmap[f])
412 tn = read(f)
416 tn = read(f)
413 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
417 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
414 showfunc=showfunc, ignorews=ignorews))
418 showfunc=showfunc, ignorews=ignorews,
419 ignorewsamount=ignorewsamount,
420 ignoreblanklines=ignoreblanklines))
415 for f in added:
421 for f in added:
416 to = None
422 to = None
417 tn = read(f)
423 tn = read(f)
418 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
424 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
419 showfunc=showfunc, ignorews=ignorews))
425 showfunc=showfunc, ignorews=ignorews,
426 ignorewsamount=ignorewsamount,
427 ignoreblanklines=ignoreblanklines))
420 for f in removed:
428 for f in removed:
421 to = repo.file(f).read(mmap[f])
429 to = repo.file(f).read(mmap[f])
422 tn = None
430 tn = None
423 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
431 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
424 showfunc=showfunc, ignorews=ignorews))
432 showfunc=showfunc, ignorews=ignorews,
433 ignorewsamount=ignorewsamount,
434 ignoreblanklines=ignoreblanklines))
425
435
426 def trimuser(ui, name, rev, revcache):
436 def trimuser(ui, name, rev, revcache):
427 """trim the name of the user who committed a change"""
437 """trim the name of the user who committed a change"""
428 user = revcache.get(rev)
438 user = revcache.get(rev)
429 if user is None:
439 if user is None:
430 user = revcache[rev] = ui.shortuser(name)
440 user = revcache[rev] = ui.shortuser(name)
431 return user
441 return user
432
442
433 class changeset_printer(object):
443 class changeset_printer(object):
434 '''show changeset information when templating not requested.'''
444 '''show changeset information when templating not requested.'''
435
445
436 def __init__(self, ui, repo):
446 def __init__(self, ui, repo):
437 self.ui = ui
447 self.ui = ui
438 self.repo = repo
448 self.repo = repo
439
449
440 def show(self, rev=0, changenode=None, brinfo=None):
450 def show(self, rev=0, changenode=None, brinfo=None):
441 '''show a single changeset or file revision'''
451 '''show a single changeset or file revision'''
442 log = self.repo.changelog
452 log = self.repo.changelog
443 if changenode is None:
453 if changenode is None:
444 changenode = log.node(rev)
454 changenode = log.node(rev)
445 elif not rev:
455 elif not rev:
446 rev = log.rev(changenode)
456 rev = log.rev(changenode)
447
457
448 if self.ui.quiet:
458 if self.ui.quiet:
449 self.ui.write("%d:%s\n" % (rev, short(changenode)))
459 self.ui.write("%d:%s\n" % (rev, short(changenode)))
450 return
460 return
451
461
452 changes = log.read(changenode)
462 changes = log.read(changenode)
453 date = util.datestr(changes[2])
463 date = util.datestr(changes[2])
454
464
455 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
465 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
456 for p in log.parents(changenode)
466 for p in log.parents(changenode)
457 if self.ui.debugflag or p != nullid]
467 if self.ui.debugflag or p != nullid]
458 if (not self.ui.debugflag and len(parents) == 1 and
468 if (not self.ui.debugflag and len(parents) == 1 and
459 parents[0][0] == rev-1):
469 parents[0][0] == rev-1):
460 parents = []
470 parents = []
461
471
462 if self.ui.verbose:
472 if self.ui.verbose:
463 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
473 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
464 else:
474 else:
465 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
475 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
466
476
467 for tag in self.repo.nodetags(changenode):
477 for tag in self.repo.nodetags(changenode):
468 self.ui.status(_("tag: %s\n") % tag)
478 self.ui.status(_("tag: %s\n") % tag)
469 for parent in parents:
479 for parent in parents:
470 self.ui.write(_("parent: %d:%s\n") % parent)
480 self.ui.write(_("parent: %d:%s\n") % parent)
471
481
472 if brinfo and changenode in brinfo:
482 if brinfo and changenode in brinfo:
473 br = brinfo[changenode]
483 br = brinfo[changenode]
474 self.ui.write(_("branch: %s\n") % " ".join(br))
484 self.ui.write(_("branch: %s\n") % " ".join(br))
475
485
476 self.ui.debug(_("manifest: %d:%s\n") %
486 self.ui.debug(_("manifest: %d:%s\n") %
477 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
487 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
478 self.ui.status(_("user: %s\n") % changes[1])
488 self.ui.status(_("user: %s\n") % changes[1])
479 self.ui.status(_("date: %s\n") % date)
489 self.ui.status(_("date: %s\n") % date)
480
490
481 if self.ui.debugflag:
491 if self.ui.debugflag:
482 files = self.repo.changes(log.parents(changenode)[0], changenode)
492 files = self.repo.changes(log.parents(changenode)[0], changenode)
483 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
493 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
484 files):
494 files):
485 if value:
495 if value:
486 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
496 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
487 else:
497 else:
488 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
498 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
489
499
490 description = changes[4].strip()
500 description = changes[4].strip()
491 if description:
501 if description:
492 if self.ui.verbose:
502 if self.ui.verbose:
493 self.ui.status(_("description:\n"))
503 self.ui.status(_("description:\n"))
494 self.ui.status(description)
504 self.ui.status(description)
495 self.ui.status("\n\n")
505 self.ui.status("\n\n")
496 else:
506 else:
497 self.ui.status(_("summary: %s\n") %
507 self.ui.status(_("summary: %s\n") %
498 description.splitlines()[0])
508 description.splitlines()[0])
499 self.ui.status("\n")
509 self.ui.status("\n")
500
510
501 def show_changeset(ui, repo, opts):
511 def show_changeset(ui, repo, opts):
502 '''show one changeset. uses template or regular display. caller
512 '''show one changeset. uses template or regular display. caller
503 can pass in 'style' and 'template' options in opts.'''
513 can pass in 'style' and 'template' options in opts.'''
504
514
505 tmpl = opts.get('template')
515 tmpl = opts.get('template')
506 if tmpl:
516 if tmpl:
507 tmpl = templater.parsestring(tmpl, quoted=False)
517 tmpl = templater.parsestring(tmpl, quoted=False)
508 else:
518 else:
509 tmpl = ui.config('ui', 'logtemplate')
519 tmpl = ui.config('ui', 'logtemplate')
510 if tmpl: tmpl = templater.parsestring(tmpl)
520 if tmpl: tmpl = templater.parsestring(tmpl)
511 mapfile = opts.get('style') or ui.config('ui', 'style')
521 mapfile = opts.get('style') or ui.config('ui', 'style')
512 if tmpl or mapfile:
522 if tmpl or mapfile:
513 if mapfile:
523 if mapfile:
514 if not os.path.isfile(mapfile):
524 if not os.path.isfile(mapfile):
515 mapname = templater.templatepath('map-cmdline.' + mapfile)
525 mapname = templater.templatepath('map-cmdline.' + mapfile)
516 if not mapname: mapname = templater.templatepath(mapfile)
526 if not mapname: mapname = templater.templatepath(mapfile)
517 if mapname: mapfile = mapname
527 if mapname: mapfile = mapname
518 try:
528 try:
519 t = templater.changeset_templater(ui, repo, mapfile)
529 t = templater.changeset_templater(ui, repo, mapfile)
520 except SyntaxError, inst:
530 except SyntaxError, inst:
521 raise util.Abort(inst.args[0])
531 raise util.Abort(inst.args[0])
522 if tmpl: t.use_template(tmpl)
532 if tmpl: t.use_template(tmpl)
523 return t
533 return t
524 return changeset_printer(ui, repo)
534 return changeset_printer(ui, repo)
525
535
526 def show_version(ui):
536 def show_version(ui):
527 """output version and copyright information"""
537 """output version and copyright information"""
528 ui.write(_("Mercurial Distributed SCM (version %s)\n")
538 ui.write(_("Mercurial Distributed SCM (version %s)\n")
529 % version.get_version())
539 % version.get_version())
530 ui.status(_(
540 ui.status(_(
531 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
541 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
532 "This is free software; see the source for copying conditions. "
542 "This is free software; see the source for copying conditions. "
533 "There is NO\nwarranty; "
543 "There is NO\nwarranty; "
534 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
544 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
535 ))
545 ))
536
546
537 def help_(ui, name=None, with_version=False):
547 def help_(ui, name=None, with_version=False):
538 """show help for a command, extension, or list of commands
548 """show help for a command, extension, or list of commands
539
549
540 With no arguments, print a list of commands and short help.
550 With no arguments, print a list of commands and short help.
541
551
542 Given a command name, print help for that command.
552 Given a command name, print help for that command.
543
553
544 Given an extension name, print help for that extension, and the
554 Given an extension name, print help for that extension, and the
545 commands it provides."""
555 commands it provides."""
546 option_lists = []
556 option_lists = []
547
557
548 def helpcmd(name):
558 def helpcmd(name):
549 if with_version:
559 if with_version:
550 show_version(ui)
560 show_version(ui)
551 ui.write('\n')
561 ui.write('\n')
552 aliases, i = findcmd(name)
562 aliases, i = findcmd(name)
553 # synopsis
563 # synopsis
554 ui.write("%s\n\n" % i[2])
564 ui.write("%s\n\n" % i[2])
555
565
556 # description
566 # description
557 doc = i[0].__doc__
567 doc = i[0].__doc__
558 if not doc:
568 if not doc:
559 doc = _("(No help text available)")
569 doc = _("(No help text available)")
560 if ui.quiet:
570 if ui.quiet:
561 doc = doc.splitlines(0)[0]
571 doc = doc.splitlines(0)[0]
562 ui.write("%s\n" % doc.rstrip())
572 ui.write("%s\n" % doc.rstrip())
563
573
564 if not ui.quiet:
574 if not ui.quiet:
565 # aliases
575 # aliases
566 if len(aliases) > 1:
576 if len(aliases) > 1:
567 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
577 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
568
578
569 # options
579 # options
570 if i[1]:
580 if i[1]:
571 option_lists.append(("options", i[1]))
581 option_lists.append(("options", i[1]))
572
582
573 def helplist(select=None):
583 def helplist(select=None):
574 h = {}
584 h = {}
575 cmds = {}
585 cmds = {}
576 for c, e in table.items():
586 for c, e in table.items():
577 f = c.split("|", 1)[0]
587 f = c.split("|", 1)[0]
578 if select and not select(f):
588 if select and not select(f):
579 continue
589 continue
580 if name == "shortlist" and not f.startswith("^"):
590 if name == "shortlist" and not f.startswith("^"):
581 continue
591 continue
582 f = f.lstrip("^")
592 f = f.lstrip("^")
583 if not ui.debugflag and f.startswith("debug"):
593 if not ui.debugflag and f.startswith("debug"):
584 continue
594 continue
585 doc = e[0].__doc__
595 doc = e[0].__doc__
586 if not doc:
596 if not doc:
587 doc = _("(No help text available)")
597 doc = _("(No help text available)")
588 h[f] = doc.splitlines(0)[0].rstrip()
598 h[f] = doc.splitlines(0)[0].rstrip()
589 cmds[f] = c.lstrip("^")
599 cmds[f] = c.lstrip("^")
590
600
591 fns = h.keys()
601 fns = h.keys()
592 fns.sort()
602 fns.sort()
593 m = max(map(len, fns))
603 m = max(map(len, fns))
594 for f in fns:
604 for f in fns:
595 if ui.verbose:
605 if ui.verbose:
596 commands = cmds[f].replace("|",", ")
606 commands = cmds[f].replace("|",", ")
597 ui.write(" %s:\n %s\n"%(commands, h[f]))
607 ui.write(" %s:\n %s\n"%(commands, h[f]))
598 else:
608 else:
599 ui.write(' %-*s %s\n' % (m, f, h[f]))
609 ui.write(' %-*s %s\n' % (m, f, h[f]))
600
610
601 def helpext(name):
611 def helpext(name):
602 try:
612 try:
603 mod = findext(name)
613 mod = findext(name)
604 except KeyError:
614 except KeyError:
605 raise UnknownCommand(name)
615 raise UnknownCommand(name)
606
616
607 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
617 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
608 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
618 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
609 for d in doc[1:]:
619 for d in doc[1:]:
610 ui.write(d, '\n')
620 ui.write(d, '\n')
611
621
612 ui.status('\n')
622 ui.status('\n')
613 if ui.verbose:
623 if ui.verbose:
614 ui.status(_('list of commands:\n\n'))
624 ui.status(_('list of commands:\n\n'))
615 else:
625 else:
616 ui.status(_('list of commands (use "hg help -v %s" '
626 ui.status(_('list of commands (use "hg help -v %s" '
617 'to show aliases and global options):\n\n') % name)
627 'to show aliases and global options):\n\n') % name)
618
628
619 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
629 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
620 helplist(modcmds.has_key)
630 helplist(modcmds.has_key)
621
631
622 if name and name != 'shortlist':
632 if name and name != 'shortlist':
623 try:
633 try:
624 helpcmd(name)
634 helpcmd(name)
625 except UnknownCommand:
635 except UnknownCommand:
626 helpext(name)
636 helpext(name)
627
637
628 else:
638 else:
629 # program name
639 # program name
630 if ui.verbose or with_version:
640 if ui.verbose or with_version:
631 show_version(ui)
641 show_version(ui)
632 else:
642 else:
633 ui.status(_("Mercurial Distributed SCM\n"))
643 ui.status(_("Mercurial Distributed SCM\n"))
634 ui.status('\n')
644 ui.status('\n')
635
645
636 # list of commands
646 # list of commands
637 if name == "shortlist":
647 if name == "shortlist":
638 ui.status(_('basic commands (use "hg help" '
648 ui.status(_('basic commands (use "hg help" '
639 'for the full list or option "-v" for details):\n\n'))
649 'for the full list or option "-v" for details):\n\n'))
640 elif ui.verbose:
650 elif ui.verbose:
641 ui.status(_('list of commands:\n\n'))
651 ui.status(_('list of commands:\n\n'))
642 else:
652 else:
643 ui.status(_('list of commands (use "hg help -v" '
653 ui.status(_('list of commands (use "hg help -v" '
644 'to show aliases and global options):\n\n'))
654 'to show aliases and global options):\n\n'))
645
655
646 helplist()
656 helplist()
647
657
648 # global options
658 # global options
649 if ui.verbose:
659 if ui.verbose:
650 option_lists.append(("global options", globalopts))
660 option_lists.append(("global options", globalopts))
651
661
652 # list all option lists
662 # list all option lists
653 opt_output = []
663 opt_output = []
654 for title, options in option_lists:
664 for title, options in option_lists:
655 opt_output.append(("\n%s:\n" % title, None))
665 opt_output.append(("\n%s:\n" % title, None))
656 for shortopt, longopt, default, desc in options:
666 for shortopt, longopt, default, desc in options:
657 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
667 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
658 longopt and " --%s" % longopt),
668 longopt and " --%s" % longopt),
659 "%s%s" % (desc,
669 "%s%s" % (desc,
660 default
670 default
661 and _(" (default: %s)") % default
671 and _(" (default: %s)") % default
662 or "")))
672 or "")))
663
673
664 if opt_output:
674 if opt_output:
665 opts_len = max([len(line[0]) for line in opt_output if line[1]])
675 opts_len = max([len(line[0]) for line in opt_output if line[1]])
666 for first, second in opt_output:
676 for first, second in opt_output:
667 if second:
677 if second:
668 ui.write(" %-*s %s\n" % (opts_len, first, second))
678 ui.write(" %-*s %s\n" % (opts_len, first, second))
669 else:
679 else:
670 ui.write("%s\n" % first)
680 ui.write("%s\n" % first)
671
681
672 # Commands start here, listed alphabetically
682 # Commands start here, listed alphabetically
673
683
674 def add(ui, repo, *pats, **opts):
684 def add(ui, repo, *pats, **opts):
675 """add the specified files on the next commit
685 """add the specified files on the next commit
676
686
677 Schedule files to be version controlled and added to the repository.
687 Schedule files to be version controlled and added to the repository.
678
688
679 The files will be added to the repository at the next commit.
689 The files will be added to the repository at the next commit.
680
690
681 If no names are given, add all files in the repository.
691 If no names are given, add all files in the repository.
682 """
692 """
683
693
684 names = []
694 names = []
685 for src, abs, rel, exact in walk(repo, pats, opts):
695 for src, abs, rel, exact in walk(repo, pats, opts):
686 if exact:
696 if exact:
687 if ui.verbose:
697 if ui.verbose:
688 ui.status(_('adding %s\n') % rel)
698 ui.status(_('adding %s\n') % rel)
689 names.append(abs)
699 names.append(abs)
690 elif repo.dirstate.state(abs) == '?':
700 elif repo.dirstate.state(abs) == '?':
691 ui.status(_('adding %s\n') % rel)
701 ui.status(_('adding %s\n') % rel)
692 names.append(abs)
702 names.append(abs)
693 if not opts.get('dry_run'):
703 if not opts.get('dry_run'):
694 repo.add(names)
704 repo.add(names)
695
705
696 def addremove(ui, repo, *pats, **opts):
706 def addremove(ui, repo, *pats, **opts):
697 """add all new files, delete all missing files (DEPRECATED)
707 """add all new files, delete all missing files (DEPRECATED)
698
708
699 (DEPRECATED)
709 (DEPRECATED)
700 Add all new files and remove all missing files from the repository.
710 Add all new files and remove all missing files from the repository.
701
711
702 New files are ignored if they match any of the patterns in .hgignore. As
712 New files are ignored if they match any of the patterns in .hgignore. As
703 with add, these changes take effect at the next commit.
713 with add, these changes take effect at the next commit.
704
714
705 This command is now deprecated and will be removed in a future
715 This command is now deprecated and will be removed in a future
706 release. Please use add and remove --after instead.
716 release. Please use add and remove --after instead.
707 """
717 """
708 ui.warn(_('(the addremove command is deprecated; use add and remove '
718 ui.warn(_('(the addremove command is deprecated; use add and remove '
709 '--after instead)\n'))
719 '--after instead)\n'))
710 return addremove_lock(ui, repo, pats, opts)
720 return addremove_lock(ui, repo, pats, opts)
711
721
712 def addremove_lock(ui, repo, pats, opts, wlock=None):
722 def addremove_lock(ui, repo, pats, opts, wlock=None):
713 add, remove = [], []
723 add, remove = [], []
714 for src, abs, rel, exact in walk(repo, pats, opts):
724 for src, abs, rel, exact in walk(repo, pats, opts):
715 if src == 'f' and repo.dirstate.state(abs) == '?':
725 if src == 'f' and repo.dirstate.state(abs) == '?':
716 add.append(abs)
726 add.append(abs)
717 if ui.verbose or not exact:
727 if ui.verbose or not exact:
718 ui.status(_('adding %s\n') % ((pats and rel) or abs))
728 ui.status(_('adding %s\n') % ((pats and rel) or abs))
719 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
729 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
720 remove.append(abs)
730 remove.append(abs)
721 if ui.verbose or not exact:
731 if ui.verbose or not exact:
722 ui.status(_('removing %s\n') % ((pats and rel) or abs))
732 ui.status(_('removing %s\n') % ((pats and rel) or abs))
723 if not opts.get('dry_run'):
733 if not opts.get('dry_run'):
724 repo.add(add, wlock=wlock)
734 repo.add(add, wlock=wlock)
725 repo.remove(remove, wlock=wlock)
735 repo.remove(remove, wlock=wlock)
726
736
727 def annotate(ui, repo, *pats, **opts):
737 def annotate(ui, repo, *pats, **opts):
728 """show changeset information per file line
738 """show changeset information per file line
729
739
730 List changes in files, showing the revision id responsible for each line
740 List changes in files, showing the revision id responsible for each line
731
741
732 This command is useful to discover who did a change or when a change took
742 This command is useful to discover who did a change or when a change took
733 place.
743 place.
734
744
735 Without the -a option, annotate will avoid processing files it
745 Without the -a option, annotate will avoid processing files it
736 detects as binary. With -a, annotate will generate an annotation
746 detects as binary. With -a, annotate will generate an annotation
737 anyway, probably with undesirable results.
747 anyway, probably with undesirable results.
738 """
748 """
739 def getnode(rev):
749 def getnode(rev):
740 return short(repo.changelog.node(rev))
750 return short(repo.changelog.node(rev))
741
751
742 ucache = {}
752 ucache = {}
743 def getname(rev):
753 def getname(rev):
744 try:
754 try:
745 return ucache[rev]
755 return ucache[rev]
746 except:
756 except:
747 u = trimuser(ui, repo.changectx(rev).user(), rev, ucache)
757 u = trimuser(ui, repo.changectx(rev).user(), rev, ucache)
748 ucache[rev] = u
758 ucache[rev] = u
749 return u
759 return u
750
760
751 dcache = {}
761 dcache = {}
752 def getdate(rev):
762 def getdate(rev):
753 datestr = dcache.get(rev)
763 datestr = dcache.get(rev)
754 if datestr is None:
764 if datestr is None:
755 datestr = dcache[rev] = util.datestr(repo.changectx(rev).date())
765 datestr = dcache[rev] = util.datestr(repo.changectx(rev).date())
756 return datestr
766 return datestr
757
767
758 if not pats:
768 if not pats:
759 raise util.Abort(_('at least one file name or pattern required'))
769 raise util.Abort(_('at least one file name or pattern required'))
760
770
761 opmap = [['user', getname], ['number', str], ['changeset', getnode],
771 opmap = [['user', getname], ['number', str], ['changeset', getnode],
762 ['date', getdate]]
772 ['date', getdate]]
763 if not opts['user'] and not opts['changeset'] and not opts['date']:
773 if not opts['user'] and not opts['changeset'] and not opts['date']:
764 opts['number'] = 1
774 opts['number'] = 1
765
775
766 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
776 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
767
777
768 for src, abs, rel, exact in walk(repo, pats, opts, node=ctx.node()):
778 for src, abs, rel, exact in walk(repo, pats, opts, node=ctx.node()):
769 fctx = ctx.filectx(abs)
779 fctx = ctx.filectx(abs)
770 if not opts['text'] and util.binary(fctx.data()):
780 if not opts['text'] and util.binary(fctx.data()):
771 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
781 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
772 continue
782 continue
773
783
774 lines = fctx.annotate()
784 lines = fctx.annotate()
775 pieces = []
785 pieces = []
776
786
777 for o, f in opmap:
787 for o, f in opmap:
778 if opts[o]:
788 if opts[o]:
779 l = [f(n) for n, dummy in lines]
789 l = [f(n) for n, dummy in lines]
780 if l:
790 if l:
781 m = max(map(len, l))
791 m = max(map(len, l))
782 pieces.append(["%*s" % (m, x) for x in l])
792 pieces.append(["%*s" % (m, x) for x in l])
783
793
784 if pieces:
794 if pieces:
785 for p, l in zip(zip(*pieces), lines):
795 for p, l in zip(zip(*pieces), lines):
786 ui.write("%s: %s" % (" ".join(p), l[1]))
796 ui.write("%s: %s" % (" ".join(p), l[1]))
787
797
788 def archive(ui, repo, dest, **opts):
798 def archive(ui, repo, dest, **opts):
789 '''create unversioned archive of a repository revision
799 '''create unversioned archive of a repository revision
790
800
791 By default, the revision used is the parent of the working
801 By default, the revision used is the parent of the working
792 directory; use "-r" to specify a different revision.
802 directory; use "-r" to specify a different revision.
793
803
794 To specify the type of archive to create, use "-t". Valid
804 To specify the type of archive to create, use "-t". Valid
795 types are:
805 types are:
796
806
797 "files" (default): a directory full of files
807 "files" (default): a directory full of files
798 "tar": tar archive, uncompressed
808 "tar": tar archive, uncompressed
799 "tbz2": tar archive, compressed using bzip2
809 "tbz2": tar archive, compressed using bzip2
800 "tgz": tar archive, compressed using gzip
810 "tgz": tar archive, compressed using gzip
801 "uzip": zip archive, uncompressed
811 "uzip": zip archive, uncompressed
802 "zip": zip archive, compressed using deflate
812 "zip": zip archive, compressed using deflate
803
813
804 The exact name of the destination archive or directory is given
814 The exact name of the destination archive or directory is given
805 using a format string; see "hg help export" for details.
815 using a format string; see "hg help export" for details.
806
816
807 Each member added to an archive file has a directory prefix
817 Each member added to an archive file has a directory prefix
808 prepended. Use "-p" to specify a format string for the prefix.
818 prepended. Use "-p" to specify a format string for the prefix.
809 The default is the basename of the archive, with suffixes removed.
819 The default is the basename of the archive, with suffixes removed.
810 '''
820 '''
811
821
812 if opts['rev']:
822 if opts['rev']:
813 node = repo.lookup(opts['rev'])
823 node = repo.lookup(opts['rev'])
814 else:
824 else:
815 node, p2 = repo.dirstate.parents()
825 node, p2 = repo.dirstate.parents()
816 if p2 != nullid:
826 if p2 != nullid:
817 raise util.Abort(_('uncommitted merge - please provide a '
827 raise util.Abort(_('uncommitted merge - please provide a '
818 'specific revision'))
828 'specific revision'))
819
829
820 dest = make_filename(repo, dest, node)
830 dest = make_filename(repo, dest, node)
821 if os.path.realpath(dest) == repo.root:
831 if os.path.realpath(dest) == repo.root:
822 raise util.Abort(_('repository root cannot be destination'))
832 raise util.Abort(_('repository root cannot be destination'))
823 dummy, matchfn, dummy = matchpats(repo, [], opts)
833 dummy, matchfn, dummy = matchpats(repo, [], opts)
824 kind = opts.get('type') or 'files'
834 kind = opts.get('type') or 'files'
825 prefix = opts['prefix']
835 prefix = opts['prefix']
826 if dest == '-':
836 if dest == '-':
827 if kind == 'files':
837 if kind == 'files':
828 raise util.Abort(_('cannot archive plain files to stdout'))
838 raise util.Abort(_('cannot archive plain files to stdout'))
829 dest = sys.stdout
839 dest = sys.stdout
830 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
840 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
831 prefix = make_filename(repo, prefix, node)
841 prefix = make_filename(repo, prefix, node)
832 archival.archive(repo, dest, node, kind, not opts['no_decode'],
842 archival.archive(repo, dest, node, kind, not opts['no_decode'],
833 matchfn, prefix)
843 matchfn, prefix)
834
844
835 def backout(ui, repo, rev, **opts):
845 def backout(ui, repo, rev, **opts):
836 '''reverse effect of earlier changeset
846 '''reverse effect of earlier changeset
837
847
838 Commit the backed out changes as a new changeset. The new
848 Commit the backed out changes as a new changeset. The new
839 changeset is a child of the backed out changeset.
849 changeset is a child of the backed out changeset.
840
850
841 If you back out a changeset other than the tip, a new head is
851 If you back out a changeset other than the tip, a new head is
842 created. This head is the parent of the working directory. If
852 created. This head is the parent of the working directory. If
843 you back out an old changeset, your working directory will appear
853 you back out an old changeset, your working directory will appear
844 old after the backout. You should merge the backout changeset
854 old after the backout. You should merge the backout changeset
845 with another head.
855 with another head.
846
856
847 The --merge option remembers the parent of the working directory
857 The --merge option remembers the parent of the working directory
848 before starting the backout, then merges the new head with that
858 before starting the backout, then merges the new head with that
849 changeset afterwards. This saves you from doing the merge by
859 changeset afterwards. This saves you from doing the merge by
850 hand. The result of this merge is not committed, as for a normal
860 hand. The result of this merge is not committed, as for a normal
851 merge.'''
861 merge.'''
852
862
853 bail_if_changed(repo)
863 bail_if_changed(repo)
854 op1, op2 = repo.dirstate.parents()
864 op1, op2 = repo.dirstate.parents()
855 if op2 != nullid:
865 if op2 != nullid:
856 raise util.Abort(_('outstanding uncommitted merge'))
866 raise util.Abort(_('outstanding uncommitted merge'))
857 node = repo.lookup(rev)
867 node = repo.lookup(rev)
858 parent, p2 = repo.changelog.parents(node)
868 parent, p2 = repo.changelog.parents(node)
859 if parent == nullid:
869 if parent == nullid:
860 raise util.Abort(_('cannot back out a change with no parents'))
870 raise util.Abort(_('cannot back out a change with no parents'))
861 if p2 != nullid:
871 if p2 != nullid:
862 raise util.Abort(_('cannot back out a merge'))
872 raise util.Abort(_('cannot back out a merge'))
863 repo.update(node, force=True, show_stats=False)
873 repo.update(node, force=True, show_stats=False)
864 revert_opts = opts.copy()
874 revert_opts = opts.copy()
865 revert_opts['rev'] = hex(parent)
875 revert_opts['rev'] = hex(parent)
866 revert(ui, repo, **revert_opts)
876 revert(ui, repo, **revert_opts)
867 commit_opts = opts.copy()
877 commit_opts = opts.copy()
868 commit_opts['addremove'] = False
878 commit_opts['addremove'] = False
869 if not commit_opts['message'] and not commit_opts['logfile']:
879 if not commit_opts['message'] and not commit_opts['logfile']:
870 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
880 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
871 commit_opts['force_editor'] = True
881 commit_opts['force_editor'] = True
872 commit(ui, repo, **commit_opts)
882 commit(ui, repo, **commit_opts)
873 def nice(node):
883 def nice(node):
874 return '%d:%s' % (repo.changelog.rev(node), short(node))
884 return '%d:%s' % (repo.changelog.rev(node), short(node))
875 ui.status(_('changeset %s backs out changeset %s\n') %
885 ui.status(_('changeset %s backs out changeset %s\n') %
876 (nice(repo.changelog.tip()), nice(node)))
886 (nice(repo.changelog.tip()), nice(node)))
877 if op1 != node:
887 if op1 != node:
878 if opts['merge']:
888 if opts['merge']:
879 ui.status(_('merging with changeset %s\n') % nice(op1))
889 ui.status(_('merging with changeset %s\n') % nice(op1))
880 doupdate(ui, repo, hex(op1), **opts)
890 doupdate(ui, repo, hex(op1), **opts)
881 else:
891 else:
882 ui.status(_('the backout changeset is a new head - '
892 ui.status(_('the backout changeset is a new head - '
883 'do not forget to merge\n'))
893 'do not forget to merge\n'))
884 ui.status(_('(use "backout -m" if you want to auto-merge)\n'))
894 ui.status(_('(use "backout -m" if you want to auto-merge)\n'))
885
895
886 def bundle(ui, repo, fname, dest=None, **opts):
896 def bundle(ui, repo, fname, dest=None, **opts):
887 """create a changegroup file
897 """create a changegroup file
888
898
889 Generate a compressed changegroup file collecting all changesets
899 Generate a compressed changegroup file collecting all changesets
890 not found in the other repository.
900 not found in the other repository.
891
901
892 This file can then be transferred using conventional means and
902 This file can then be transferred using conventional means and
893 applied to another repository with the unbundle command. This is
903 applied to another repository with the unbundle command. This is
894 useful when native push and pull are not available or when
904 useful when native push and pull are not available or when
895 exporting an entire repository is undesirable. The standard file
905 exporting an entire repository is undesirable. The standard file
896 extension is ".hg".
906 extension is ".hg".
897
907
898 Unlike import/export, this exactly preserves all changeset
908 Unlike import/export, this exactly preserves all changeset
899 contents including permissions, rename data, and revision history.
909 contents including permissions, rename data, and revision history.
900 """
910 """
901 dest = ui.expandpath(dest or 'default-push', dest or 'default')
911 dest = ui.expandpath(dest or 'default-push', dest or 'default')
902 other = hg.repository(ui, dest)
912 other = hg.repository(ui, dest)
903 o = repo.findoutgoing(other, force=opts['force'])
913 o = repo.findoutgoing(other, force=opts['force'])
904 cg = repo.changegroup(o, 'bundle')
914 cg = repo.changegroup(o, 'bundle')
905 write_bundle(cg, fname)
915 write_bundle(cg, fname)
906
916
907 def cat(ui, repo, file1, *pats, **opts):
917 def cat(ui, repo, file1, *pats, **opts):
908 """output the latest or given revisions of files
918 """output the latest or given revisions of files
909
919
910 Print the specified files as they were at the given revision.
920 Print the specified files as they were at the given revision.
911 If no revision is given then the tip is used.
921 If no revision is given then the tip is used.
912
922
913 Output may be to a file, in which case the name of the file is
923 Output may be to a file, in which case the name of the file is
914 given using a format string. The formatting rules are the same as
924 given using a format string. The formatting rules are the same as
915 for the export command, with the following additions:
925 for the export command, with the following additions:
916
926
917 %s basename of file being printed
927 %s basename of file being printed
918 %d dirname of file being printed, or '.' if in repo root
928 %d dirname of file being printed, or '.' if in repo root
919 %p root-relative path name of file being printed
929 %p root-relative path name of file being printed
920 """
930 """
921 ctx = repo.changectx(opts['rev'] or -1)
931 ctx = repo.changectx(opts['rev'] or -1)
922 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
932 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
923 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs)
933 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs)
924 fp.write(ctx.filectx(abs).data())
934 fp.write(ctx.filectx(abs).data())
925
935
926 def clone(ui, source, dest=None, **opts):
936 def clone(ui, source, dest=None, **opts):
927 """make a copy of an existing repository
937 """make a copy of an existing repository
928
938
929 Create a copy of an existing repository in a new directory.
939 Create a copy of an existing repository in a new directory.
930
940
931 If no destination directory name is specified, it defaults to the
941 If no destination directory name is specified, it defaults to the
932 basename of the source.
942 basename of the source.
933
943
934 The location of the source is added to the new repository's
944 The location of the source is added to the new repository's
935 .hg/hgrc file, as the default to be used for future pulls.
945 .hg/hgrc file, as the default to be used for future pulls.
936
946
937 For efficiency, hardlinks are used for cloning whenever the source
947 For efficiency, hardlinks are used for cloning whenever the source
938 and destination are on the same filesystem. Some filesystems,
948 and destination are on the same filesystem. Some filesystems,
939 such as AFS, implement hardlinking incorrectly, but do not report
949 such as AFS, implement hardlinking incorrectly, but do not report
940 errors. In these cases, use the --pull option to avoid
950 errors. In these cases, use the --pull option to avoid
941 hardlinking.
951 hardlinking.
942
952
943 See pull for valid source format details.
953 See pull for valid source format details.
944 """
954 """
945 if dest is None:
955 if dest is None:
946 dest = os.path.basename(os.path.normpath(source))
956 dest = os.path.basename(os.path.normpath(source))
947
957
948 if os.path.exists(dest):
958 if os.path.exists(dest):
949 raise util.Abort(_("destination '%s' already exists"), dest)
959 raise util.Abort(_("destination '%s' already exists"), dest)
950
960
951 class Dircleanup(object):
961 class Dircleanup(object):
952 def __init__(self, dir_):
962 def __init__(self, dir_):
953 self.rmtree = shutil.rmtree
963 self.rmtree = shutil.rmtree
954 self.dir_ = dir_
964 self.dir_ = dir_
955 def close(self):
965 def close(self):
956 self.dir_ = None
966 self.dir_ = None
957 def __del__(self):
967 def __del__(self):
958 if self.dir_:
968 if self.dir_:
959 self.rmtree(self.dir_, True)
969 self.rmtree(self.dir_, True)
960
970
961 if opts['ssh']:
971 if opts['ssh']:
962 ui.setconfig("ui", "ssh", opts['ssh'])
972 ui.setconfig("ui", "ssh", opts['ssh'])
963 if opts['remotecmd']:
973 if opts['remotecmd']:
964 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
974 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
965
975
966 source = ui.expandpath(source)
976 source = ui.expandpath(source)
967 src_repo = hg.repository(ui, source)
977 src_repo = hg.repository(ui, source)
968
978
969 dest_repo = None
979 dest_repo = None
970 try:
980 try:
971 dest_repo = hg.repository(ui, dest)
981 dest_repo = hg.repository(ui, dest)
972 raise util.Abort(_("destination '%s' already exists." % dest))
982 raise util.Abort(_("destination '%s' already exists." % dest))
973 except hg.RepoError:
983 except hg.RepoError:
974 dest_repo = hg.repository(ui, dest, create=1)
984 dest_repo = hg.repository(ui, dest, create=1)
975
985
976 dest_path = None
986 dest_path = None
977 d = None
987 d = None
978 if dest_repo.local():
988 if dest_repo.local():
979 dest_path = os.path.realpath(dest)
989 dest_path = os.path.realpath(dest)
980 d = Dircleanup(dest_path)
990 d = Dircleanup(dest_path)
981
991
982 abspath = source
992 abspath = source
983 copy = False
993 copy = False
984 if src_repo.local() and dest_repo.local():
994 if src_repo.local() and dest_repo.local():
985 abspath = os.path.abspath(source)
995 abspath = os.path.abspath(source)
986 if not opts['pull'] and not opts['rev']:
996 if not opts['pull'] and not opts['rev']:
987 copy = True
997 copy = True
988
998
989 if copy:
999 if copy:
990 try:
1000 try:
991 # we use a lock here because if we race with commit, we
1001 # we use a lock here because if we race with commit, we
992 # can end up with extra data in the cloned revlogs that's
1002 # can end up with extra data in the cloned revlogs that's
993 # not pointed to by changesets, thus causing verify to
1003 # not pointed to by changesets, thus causing verify to
994 # fail
1004 # fail
995 l1 = src_repo.lock()
1005 l1 = src_repo.lock()
996 except lock.LockException:
1006 except lock.LockException:
997 copy = False
1007 copy = False
998
1008
999 if copy:
1009 if copy:
1000 # we lock here to avoid premature writing to the target
1010 # we lock here to avoid premature writing to the target
1001 l2 = lock.lock(os.path.join(dest_path, ".hg", "lock"))
1011 l2 = lock.lock(os.path.join(dest_path, ".hg", "lock"))
1002
1012
1003 # we need to remove the (empty) data dir in dest so copyfiles can do it's work
1013 # we need to remove the (empty) data dir in dest so copyfiles can do it's work
1004 os.rmdir( os.path.join(dest_path, ".hg", "data") )
1014 os.rmdir( os.path.join(dest_path, ".hg", "data") )
1005 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
1015 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
1006 for f in files.split():
1016 for f in files.split():
1007 src = os.path.join(source, ".hg", f)
1017 src = os.path.join(source, ".hg", f)
1008 dst = os.path.join(dest_path, ".hg", f)
1018 dst = os.path.join(dest_path, ".hg", f)
1009 try:
1019 try:
1010 util.copyfiles(src, dst)
1020 util.copyfiles(src, dst)
1011 except OSError, inst:
1021 except OSError, inst:
1012 if inst.errno != errno.ENOENT:
1022 if inst.errno != errno.ENOENT:
1013 raise
1023 raise
1014
1024
1015 # we need to re-init the repo after manually copying the data into it
1025 # we need to re-init the repo after manually copying the data into it
1016 dest_repo = hg.repository(ui, dest)
1026 dest_repo = hg.repository(ui, dest)
1017
1027
1018 else:
1028 else:
1019 revs = None
1029 revs = None
1020 if opts['rev']:
1030 if opts['rev']:
1021 if not src_repo.local():
1031 if not src_repo.local():
1022 error = _("clone -r not supported yet for remote repositories.")
1032 error = _("clone -r not supported yet for remote repositories.")
1023 raise util.Abort(error)
1033 raise util.Abort(error)
1024 else:
1034 else:
1025 revs = [src_repo.lookup(rev) for rev in opts['rev']]
1035 revs = [src_repo.lookup(rev) for rev in opts['rev']]
1026
1036
1027 if dest_repo.local():
1037 if dest_repo.local():
1028 dest_repo.pull(src_repo, heads = revs)
1038 dest_repo.pull(src_repo, heads = revs)
1029 elif src_repo.local():
1039 elif src_repo.local():
1030 src_repo.push(dest_repo, revs = revs)
1040 src_repo.push(dest_repo, revs = revs)
1031 else:
1041 else:
1032 error = _("clone from remote to remote not supported.")
1042 error = _("clone from remote to remote not supported.")
1033 raise util.Abort(error)
1043 raise util.Abort(error)
1034
1044
1035 if dest_repo.local():
1045 if dest_repo.local():
1036 f = dest_repo.opener("hgrc", "w", text=True)
1046 f = dest_repo.opener("hgrc", "w", text=True)
1037 f.write("[paths]\n")
1047 f.write("[paths]\n")
1038 f.write("default = %s\n" % abspath)
1048 f.write("default = %s\n" % abspath)
1039 f.close()
1049 f.close()
1040
1050
1041 if not opts['noupdate']:
1051 if not opts['noupdate']:
1042 doupdate(dest_repo.ui, dest_repo)
1052 doupdate(dest_repo.ui, dest_repo)
1043
1053
1044 if d:
1054 if d:
1045 d.close()
1055 d.close()
1046
1056
1047 def commit(ui, repo, *pats, **opts):
1057 def commit(ui, repo, *pats, **opts):
1048 """commit the specified files or all outstanding changes
1058 """commit the specified files or all outstanding changes
1049
1059
1050 Commit changes to the given files into the repository.
1060 Commit changes to the given files into the repository.
1051
1061
1052 If a list of files is omitted, all changes reported by "hg status"
1062 If a list of files is omitted, all changes reported by "hg status"
1053 will be committed.
1063 will be committed.
1054
1064
1055 If no commit message is specified, the editor configured in your hgrc
1065 If no commit message is specified, the editor configured in your hgrc
1056 or in the EDITOR environment variable is started to enter a message.
1066 or in the EDITOR environment variable is started to enter a message.
1057 """
1067 """
1058 message = opts['message']
1068 message = opts['message']
1059 logfile = opts['logfile']
1069 logfile = opts['logfile']
1060
1070
1061 if message and logfile:
1071 if message and logfile:
1062 raise util.Abort(_('options --message and --logfile are mutually '
1072 raise util.Abort(_('options --message and --logfile are mutually '
1063 'exclusive'))
1073 'exclusive'))
1064 if not message and logfile:
1074 if not message and logfile:
1065 try:
1075 try:
1066 if logfile == '-':
1076 if logfile == '-':
1067 message = sys.stdin.read()
1077 message = sys.stdin.read()
1068 else:
1078 else:
1069 message = open(logfile).read()
1079 message = open(logfile).read()
1070 except IOError, inst:
1080 except IOError, inst:
1071 raise util.Abort(_("can't read commit message '%s': %s") %
1081 raise util.Abort(_("can't read commit message '%s': %s") %
1072 (logfile, inst.strerror))
1082 (logfile, inst.strerror))
1073
1083
1074 if opts['addremove']:
1084 if opts['addremove']:
1075 addremove_lock(ui, repo, pats, opts)
1085 addremove_lock(ui, repo, pats, opts)
1076 fns, match, anypats = matchpats(repo, pats, opts)
1086 fns, match, anypats = matchpats(repo, pats, opts)
1077 if pats:
1087 if pats:
1078 modified, added, removed, deleted, unknown = (
1088 modified, added, removed, deleted, unknown = (
1079 repo.changes(files=fns, match=match))
1089 repo.changes(files=fns, match=match))
1080 files = modified + added + removed
1090 files = modified + added + removed
1081 else:
1091 else:
1082 files = []
1092 files = []
1083 try:
1093 try:
1084 repo.commit(files, message, opts['user'], opts['date'], match,
1094 repo.commit(files, message, opts['user'], opts['date'], match,
1085 force_editor=opts.get('force_editor'))
1095 force_editor=opts.get('force_editor'))
1086 except ValueError, inst:
1096 except ValueError, inst:
1087 raise util.Abort(str(inst))
1097 raise util.Abort(str(inst))
1088
1098
1089 def docopy(ui, repo, pats, opts, wlock):
1099 def docopy(ui, repo, pats, opts, wlock):
1090 # called with the repo lock held
1100 # called with the repo lock held
1091 cwd = repo.getcwd()
1101 cwd = repo.getcwd()
1092 errors = 0
1102 errors = 0
1093 copied = []
1103 copied = []
1094 targets = {}
1104 targets = {}
1095
1105
1096 def okaytocopy(abs, rel, exact):
1106 def okaytocopy(abs, rel, exact):
1097 reasons = {'?': _('is not managed'),
1107 reasons = {'?': _('is not managed'),
1098 'a': _('has been marked for add'),
1108 'a': _('has been marked for add'),
1099 'r': _('has been marked for remove')}
1109 'r': _('has been marked for remove')}
1100 state = repo.dirstate.state(abs)
1110 state = repo.dirstate.state(abs)
1101 reason = reasons.get(state)
1111 reason = reasons.get(state)
1102 if reason:
1112 if reason:
1103 if state == 'a':
1113 if state == 'a':
1104 origsrc = repo.dirstate.copied(abs)
1114 origsrc = repo.dirstate.copied(abs)
1105 if origsrc is not None:
1115 if origsrc is not None:
1106 return origsrc
1116 return origsrc
1107 if exact:
1117 if exact:
1108 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1118 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1109 else:
1119 else:
1110 return abs
1120 return abs
1111
1121
1112 def copy(origsrc, abssrc, relsrc, target, exact):
1122 def copy(origsrc, abssrc, relsrc, target, exact):
1113 abstarget = util.canonpath(repo.root, cwd, target)
1123 abstarget = util.canonpath(repo.root, cwd, target)
1114 reltarget = util.pathto(cwd, abstarget)
1124 reltarget = util.pathto(cwd, abstarget)
1115 prevsrc = targets.get(abstarget)
1125 prevsrc = targets.get(abstarget)
1116 if prevsrc is not None:
1126 if prevsrc is not None:
1117 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1127 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1118 (reltarget, abssrc, prevsrc))
1128 (reltarget, abssrc, prevsrc))
1119 return
1129 return
1120 if (not opts['after'] and os.path.exists(reltarget) or
1130 if (not opts['after'] and os.path.exists(reltarget) or
1121 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1131 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1122 if not opts['force']:
1132 if not opts['force']:
1123 ui.warn(_('%s: not overwriting - file exists\n') %
1133 ui.warn(_('%s: not overwriting - file exists\n') %
1124 reltarget)
1134 reltarget)
1125 return
1135 return
1126 if not opts['after'] and not opts.get('dry_run'):
1136 if not opts['after'] and not opts.get('dry_run'):
1127 os.unlink(reltarget)
1137 os.unlink(reltarget)
1128 if opts['after']:
1138 if opts['after']:
1129 if not os.path.exists(reltarget):
1139 if not os.path.exists(reltarget):
1130 return
1140 return
1131 else:
1141 else:
1132 targetdir = os.path.dirname(reltarget) or '.'
1142 targetdir = os.path.dirname(reltarget) or '.'
1133 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1143 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1134 os.makedirs(targetdir)
1144 os.makedirs(targetdir)
1135 try:
1145 try:
1136 restore = repo.dirstate.state(abstarget) == 'r'
1146 restore = repo.dirstate.state(abstarget) == 'r'
1137 if restore and not opts.get('dry_run'):
1147 if restore and not opts.get('dry_run'):
1138 repo.undelete([abstarget], wlock)
1148 repo.undelete([abstarget], wlock)
1139 try:
1149 try:
1140 if not opts.get('dry_run'):
1150 if not opts.get('dry_run'):
1141 shutil.copyfile(relsrc, reltarget)
1151 shutil.copyfile(relsrc, reltarget)
1142 shutil.copymode(relsrc, reltarget)
1152 shutil.copymode(relsrc, reltarget)
1143 restore = False
1153 restore = False
1144 finally:
1154 finally:
1145 if restore:
1155 if restore:
1146 repo.remove([abstarget], wlock)
1156 repo.remove([abstarget], wlock)
1147 except shutil.Error, inst:
1157 except shutil.Error, inst:
1148 raise util.Abort(str(inst))
1158 raise util.Abort(str(inst))
1149 except IOError, inst:
1159 except IOError, inst:
1150 if inst.errno == errno.ENOENT:
1160 if inst.errno == errno.ENOENT:
1151 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1161 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1152 else:
1162 else:
1153 ui.warn(_('%s: cannot copy - %s\n') %
1163 ui.warn(_('%s: cannot copy - %s\n') %
1154 (relsrc, inst.strerror))
1164 (relsrc, inst.strerror))
1155 errors += 1
1165 errors += 1
1156 return
1166 return
1157 if ui.verbose or not exact:
1167 if ui.verbose or not exact:
1158 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1168 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1159 targets[abstarget] = abssrc
1169 targets[abstarget] = abssrc
1160 if abstarget != origsrc and not opts.get('dry_run'):
1170 if abstarget != origsrc and not opts.get('dry_run'):
1161 repo.copy(origsrc, abstarget, wlock)
1171 repo.copy(origsrc, abstarget, wlock)
1162 copied.append((abssrc, relsrc, exact))
1172 copied.append((abssrc, relsrc, exact))
1163
1173
1164 def targetpathfn(pat, dest, srcs):
1174 def targetpathfn(pat, dest, srcs):
1165 if os.path.isdir(pat):
1175 if os.path.isdir(pat):
1166 abspfx = util.canonpath(repo.root, cwd, pat)
1176 abspfx = util.canonpath(repo.root, cwd, pat)
1167 if destdirexists:
1177 if destdirexists:
1168 striplen = len(os.path.split(abspfx)[0])
1178 striplen = len(os.path.split(abspfx)[0])
1169 else:
1179 else:
1170 striplen = len(abspfx)
1180 striplen = len(abspfx)
1171 if striplen:
1181 if striplen:
1172 striplen += len(os.sep)
1182 striplen += len(os.sep)
1173 res = lambda p: os.path.join(dest, p[striplen:])
1183 res = lambda p: os.path.join(dest, p[striplen:])
1174 elif destdirexists:
1184 elif destdirexists:
1175 res = lambda p: os.path.join(dest, os.path.basename(p))
1185 res = lambda p: os.path.join(dest, os.path.basename(p))
1176 else:
1186 else:
1177 res = lambda p: dest
1187 res = lambda p: dest
1178 return res
1188 return res
1179
1189
1180 def targetpathafterfn(pat, dest, srcs):
1190 def targetpathafterfn(pat, dest, srcs):
1181 if util.patkind(pat, None)[0]:
1191 if util.patkind(pat, None)[0]:
1182 # a mercurial pattern
1192 # a mercurial pattern
1183 res = lambda p: os.path.join(dest, os.path.basename(p))
1193 res = lambda p: os.path.join(dest, os.path.basename(p))
1184 else:
1194 else:
1185 abspfx = util.canonpath(repo.root, cwd, pat)
1195 abspfx = util.canonpath(repo.root, cwd, pat)
1186 if len(abspfx) < len(srcs[0][0]):
1196 if len(abspfx) < len(srcs[0][0]):
1187 # A directory. Either the target path contains the last
1197 # A directory. Either the target path contains the last
1188 # component of the source path or it does not.
1198 # component of the source path or it does not.
1189 def evalpath(striplen):
1199 def evalpath(striplen):
1190 score = 0
1200 score = 0
1191 for s in srcs:
1201 for s in srcs:
1192 t = os.path.join(dest, s[0][striplen:])
1202 t = os.path.join(dest, s[0][striplen:])
1193 if os.path.exists(t):
1203 if os.path.exists(t):
1194 score += 1
1204 score += 1
1195 return score
1205 return score
1196
1206
1197 striplen = len(abspfx)
1207 striplen = len(abspfx)
1198 if striplen:
1208 if striplen:
1199 striplen += len(os.sep)
1209 striplen += len(os.sep)
1200 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1210 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1201 score = evalpath(striplen)
1211 score = evalpath(striplen)
1202 striplen1 = len(os.path.split(abspfx)[0])
1212 striplen1 = len(os.path.split(abspfx)[0])
1203 if striplen1:
1213 if striplen1:
1204 striplen1 += len(os.sep)
1214 striplen1 += len(os.sep)
1205 if evalpath(striplen1) > score:
1215 if evalpath(striplen1) > score:
1206 striplen = striplen1
1216 striplen = striplen1
1207 res = lambda p: os.path.join(dest, p[striplen:])
1217 res = lambda p: os.path.join(dest, p[striplen:])
1208 else:
1218 else:
1209 # a file
1219 # a file
1210 if destdirexists:
1220 if destdirexists:
1211 res = lambda p: os.path.join(dest, os.path.basename(p))
1221 res = lambda p: os.path.join(dest, os.path.basename(p))
1212 else:
1222 else:
1213 res = lambda p: dest
1223 res = lambda p: dest
1214 return res
1224 return res
1215
1225
1216
1226
1217 pats = list(pats)
1227 pats = list(pats)
1218 if not pats:
1228 if not pats:
1219 raise util.Abort(_('no source or destination specified'))
1229 raise util.Abort(_('no source or destination specified'))
1220 if len(pats) == 1:
1230 if len(pats) == 1:
1221 raise util.Abort(_('no destination specified'))
1231 raise util.Abort(_('no destination specified'))
1222 dest = pats.pop()
1232 dest = pats.pop()
1223 destdirexists = os.path.isdir(dest)
1233 destdirexists = os.path.isdir(dest)
1224 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1234 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1225 raise util.Abort(_('with multiple sources, destination must be an '
1235 raise util.Abort(_('with multiple sources, destination must be an '
1226 'existing directory'))
1236 'existing directory'))
1227 if opts['after']:
1237 if opts['after']:
1228 tfn = targetpathafterfn
1238 tfn = targetpathafterfn
1229 else:
1239 else:
1230 tfn = targetpathfn
1240 tfn = targetpathfn
1231 copylist = []
1241 copylist = []
1232 for pat in pats:
1242 for pat in pats:
1233 srcs = []
1243 srcs = []
1234 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1244 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1235 origsrc = okaytocopy(abssrc, relsrc, exact)
1245 origsrc = okaytocopy(abssrc, relsrc, exact)
1236 if origsrc:
1246 if origsrc:
1237 srcs.append((origsrc, abssrc, relsrc, exact))
1247 srcs.append((origsrc, abssrc, relsrc, exact))
1238 if not srcs:
1248 if not srcs:
1239 continue
1249 continue
1240 copylist.append((tfn(pat, dest, srcs), srcs))
1250 copylist.append((tfn(pat, dest, srcs), srcs))
1241 if not copylist:
1251 if not copylist:
1242 raise util.Abort(_('no files to copy'))
1252 raise util.Abort(_('no files to copy'))
1243
1253
1244 for targetpath, srcs in copylist:
1254 for targetpath, srcs in copylist:
1245 for origsrc, abssrc, relsrc, exact in srcs:
1255 for origsrc, abssrc, relsrc, exact in srcs:
1246 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1256 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1247
1257
1248 if errors:
1258 if errors:
1249 ui.warn(_('(consider using --after)\n'))
1259 ui.warn(_('(consider using --after)\n'))
1250 return errors, copied
1260 return errors, copied
1251
1261
1252 def copy(ui, repo, *pats, **opts):
1262 def copy(ui, repo, *pats, **opts):
1253 """mark files as copied for the next commit
1263 """mark files as copied for the next commit
1254
1264
1255 Mark dest as having copies of source files. If dest is a
1265 Mark dest as having copies of source files. If dest is a
1256 directory, copies are put in that directory. If dest is a file,
1266 directory, copies are put in that directory. If dest is a file,
1257 there can only be one source.
1267 there can only be one source.
1258
1268
1259 By default, this command copies the contents of files as they
1269 By default, this command copies the contents of files as they
1260 stand in the working directory. If invoked with --after, the
1270 stand in the working directory. If invoked with --after, the
1261 operation is recorded, but no copying is performed.
1271 operation is recorded, but no copying is performed.
1262
1272
1263 This command takes effect in the next commit.
1273 This command takes effect in the next commit.
1264
1274
1265 NOTE: This command should be treated as experimental. While it
1275 NOTE: This command should be treated as experimental. While it
1266 should properly record copied files, this information is not yet
1276 should properly record copied files, this information is not yet
1267 fully used by merge, nor fully reported by log.
1277 fully used by merge, nor fully reported by log.
1268 """
1278 """
1269 wlock = repo.wlock(0)
1279 wlock = repo.wlock(0)
1270 errs, copied = docopy(ui, repo, pats, opts, wlock)
1280 errs, copied = docopy(ui, repo, pats, opts, wlock)
1271 return errs
1281 return errs
1272
1282
1273 def debugancestor(ui, index, rev1, rev2):
1283 def debugancestor(ui, index, rev1, rev2):
1274 """find the ancestor revision of two revisions in a given index"""
1284 """find the ancestor revision of two revisions in a given index"""
1275 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1285 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1276 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1286 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1277 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1287 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1278
1288
1279 def debugcomplete(ui, cmd='', **opts):
1289 def debugcomplete(ui, cmd='', **opts):
1280 """returns the completion list associated with the given command"""
1290 """returns the completion list associated with the given command"""
1281
1291
1282 if opts['options']:
1292 if opts['options']:
1283 options = []
1293 options = []
1284 otables = [globalopts]
1294 otables = [globalopts]
1285 if cmd:
1295 if cmd:
1286 aliases, entry = findcmd(cmd)
1296 aliases, entry = findcmd(cmd)
1287 otables.append(entry[1])
1297 otables.append(entry[1])
1288 for t in otables:
1298 for t in otables:
1289 for o in t:
1299 for o in t:
1290 if o[0]:
1300 if o[0]:
1291 options.append('-%s' % o[0])
1301 options.append('-%s' % o[0])
1292 options.append('--%s' % o[1])
1302 options.append('--%s' % o[1])
1293 ui.write("%s\n" % "\n".join(options))
1303 ui.write("%s\n" % "\n".join(options))
1294 return
1304 return
1295
1305
1296 clist = findpossible(cmd).keys()
1306 clist = findpossible(cmd).keys()
1297 clist.sort()
1307 clist.sort()
1298 ui.write("%s\n" % "\n".join(clist))
1308 ui.write("%s\n" % "\n".join(clist))
1299
1309
1300 def debugrebuildstate(ui, repo, rev=None):
1310 def debugrebuildstate(ui, repo, rev=None):
1301 """rebuild the dirstate as it would look like for the given revision"""
1311 """rebuild the dirstate as it would look like for the given revision"""
1302 if not rev:
1312 if not rev:
1303 rev = repo.changelog.tip()
1313 rev = repo.changelog.tip()
1304 else:
1314 else:
1305 rev = repo.lookup(rev)
1315 rev = repo.lookup(rev)
1306 change = repo.changelog.read(rev)
1316 change = repo.changelog.read(rev)
1307 n = change[0]
1317 n = change[0]
1308 files = repo.manifest.readflags(n)
1318 files = repo.manifest.readflags(n)
1309 wlock = repo.wlock()
1319 wlock = repo.wlock()
1310 repo.dirstate.rebuild(rev, files.iteritems())
1320 repo.dirstate.rebuild(rev, files.iteritems())
1311
1321
1312 def debugcheckstate(ui, repo):
1322 def debugcheckstate(ui, repo):
1313 """validate the correctness of the current dirstate"""
1323 """validate the correctness of the current dirstate"""
1314 parent1, parent2 = repo.dirstate.parents()
1324 parent1, parent2 = repo.dirstate.parents()
1315 repo.dirstate.read()
1325 repo.dirstate.read()
1316 dc = repo.dirstate.map
1326 dc = repo.dirstate.map
1317 keys = dc.keys()
1327 keys = dc.keys()
1318 keys.sort()
1328 keys.sort()
1319 m1n = repo.changelog.read(parent1)[0]
1329 m1n = repo.changelog.read(parent1)[0]
1320 m2n = repo.changelog.read(parent2)[0]
1330 m2n = repo.changelog.read(parent2)[0]
1321 m1 = repo.manifest.read(m1n)
1331 m1 = repo.manifest.read(m1n)
1322 m2 = repo.manifest.read(m2n)
1332 m2 = repo.manifest.read(m2n)
1323 errors = 0
1333 errors = 0
1324 for f in dc:
1334 for f in dc:
1325 state = repo.dirstate.state(f)
1335 state = repo.dirstate.state(f)
1326 if state in "nr" and f not in m1:
1336 if state in "nr" and f not in m1:
1327 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1337 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1328 errors += 1
1338 errors += 1
1329 if state in "a" and f in m1:
1339 if state in "a" and f in m1:
1330 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1340 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1331 errors += 1
1341 errors += 1
1332 if state in "m" and f not in m1 and f not in m2:
1342 if state in "m" and f not in m1 and f not in m2:
1333 ui.warn(_("%s in state %s, but not in either manifest\n") %
1343 ui.warn(_("%s in state %s, but not in either manifest\n") %
1334 (f, state))
1344 (f, state))
1335 errors += 1
1345 errors += 1
1336 for f in m1:
1346 for f in m1:
1337 state = repo.dirstate.state(f)
1347 state = repo.dirstate.state(f)
1338 if state not in "nrm":
1348 if state not in "nrm":
1339 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1349 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1340 errors += 1
1350 errors += 1
1341 if errors:
1351 if errors:
1342 error = _(".hg/dirstate inconsistent with current parent's manifest")
1352 error = _(".hg/dirstate inconsistent with current parent's manifest")
1343 raise util.Abort(error)
1353 raise util.Abort(error)
1344
1354
1345 def debugconfig(ui, repo, *values):
1355 def debugconfig(ui, repo, *values):
1346 """show combined config settings from all hgrc files
1356 """show combined config settings from all hgrc files
1347
1357
1348 With no args, print names and values of all config items.
1358 With no args, print names and values of all config items.
1349
1359
1350 With one arg of the form section.name, print just the value of
1360 With one arg of the form section.name, print just the value of
1351 that config item.
1361 that config item.
1352
1362
1353 With multiple args, print names and values of all config items
1363 With multiple args, print names and values of all config items
1354 with matching section names."""
1364 with matching section names."""
1355
1365
1356 if values:
1366 if values:
1357 if len([v for v in values if '.' in v]) > 1:
1367 if len([v for v in values if '.' in v]) > 1:
1358 raise util.Abort(_('only one config item permitted'))
1368 raise util.Abort(_('only one config item permitted'))
1359 for section, name, value in ui.walkconfig():
1369 for section, name, value in ui.walkconfig():
1360 sectname = section + '.' + name
1370 sectname = section + '.' + name
1361 if values:
1371 if values:
1362 for v in values:
1372 for v in values:
1363 if v == section:
1373 if v == section:
1364 ui.write('%s=%s\n' % (sectname, value))
1374 ui.write('%s=%s\n' % (sectname, value))
1365 elif v == sectname:
1375 elif v == sectname:
1366 ui.write(value, '\n')
1376 ui.write(value, '\n')
1367 else:
1377 else:
1368 ui.write('%s=%s\n' % (sectname, value))
1378 ui.write('%s=%s\n' % (sectname, value))
1369
1379
1370 def debugsetparents(ui, repo, rev1, rev2=None):
1380 def debugsetparents(ui, repo, rev1, rev2=None):
1371 """manually set the parents of the current working directory
1381 """manually set the parents of the current working directory
1372
1382
1373 This is useful for writing repository conversion tools, but should
1383 This is useful for writing repository conversion tools, but should
1374 be used with care.
1384 be used with care.
1375 """
1385 """
1376
1386
1377 if not rev2:
1387 if not rev2:
1378 rev2 = hex(nullid)
1388 rev2 = hex(nullid)
1379
1389
1380 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1390 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1381
1391
1382 def debugstate(ui, repo):
1392 def debugstate(ui, repo):
1383 """show the contents of the current dirstate"""
1393 """show the contents of the current dirstate"""
1384 repo.dirstate.read()
1394 repo.dirstate.read()
1385 dc = repo.dirstate.map
1395 dc = repo.dirstate.map
1386 keys = dc.keys()
1396 keys = dc.keys()
1387 keys.sort()
1397 keys.sort()
1388 for file_ in keys:
1398 for file_ in keys:
1389 ui.write("%c %3o %10d %s %s\n"
1399 ui.write("%c %3o %10d %s %s\n"
1390 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1400 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1391 time.strftime("%x %X",
1401 time.strftime("%x %X",
1392 time.localtime(dc[file_][3])), file_))
1402 time.localtime(dc[file_][3])), file_))
1393 for f in repo.dirstate.copies:
1403 for f in repo.dirstate.copies:
1394 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1404 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1395
1405
1396 def debugdata(ui, file_, rev):
1406 def debugdata(ui, file_, rev):
1397 """dump the contents of an data file revision"""
1407 """dump the contents of an data file revision"""
1398 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1408 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1399 file_[:-2] + ".i", file_, 0)
1409 file_[:-2] + ".i", file_, 0)
1400 try:
1410 try:
1401 ui.write(r.revision(r.lookup(rev)))
1411 ui.write(r.revision(r.lookup(rev)))
1402 except KeyError:
1412 except KeyError:
1403 raise util.Abort(_('invalid revision identifier %s'), rev)
1413 raise util.Abort(_('invalid revision identifier %s'), rev)
1404
1414
1405 def debugindex(ui, file_):
1415 def debugindex(ui, file_):
1406 """dump the contents of an index file"""
1416 """dump the contents of an index file"""
1407 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1417 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1408 ui.write(" rev offset length base linkrev" +
1418 ui.write(" rev offset length base linkrev" +
1409 " nodeid p1 p2\n")
1419 " nodeid p1 p2\n")
1410 for i in range(r.count()):
1420 for i in range(r.count()):
1411 node = r.node(i)
1421 node = r.node(i)
1412 pp = r.parents(node)
1422 pp = r.parents(node)
1413 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1423 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1414 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1424 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1415 short(node), short(pp[0]), short(pp[1])))
1425 short(node), short(pp[0]), short(pp[1])))
1416
1426
1417 def debugindexdot(ui, file_):
1427 def debugindexdot(ui, file_):
1418 """dump an index DAG as a .dot file"""
1428 """dump an index DAG as a .dot file"""
1419 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1429 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1420 ui.write("digraph G {\n")
1430 ui.write("digraph G {\n")
1421 for i in range(r.count()):
1431 for i in range(r.count()):
1422 node = r.node(i)
1432 node = r.node(i)
1423 pp = r.parents(node)
1433 pp = r.parents(node)
1424 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1434 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1425 if pp[1] != nullid:
1435 if pp[1] != nullid:
1426 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1436 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1427 ui.write("}\n")
1437 ui.write("}\n")
1428
1438
1429 def debugrename(ui, repo, file, rev=None):
1439 def debugrename(ui, repo, file, rev=None):
1430 """dump rename information"""
1440 """dump rename information"""
1431 r = repo.file(relpath(repo, [file])[0])
1441 r = repo.file(relpath(repo, [file])[0])
1432 if rev:
1442 if rev:
1433 try:
1443 try:
1434 # assume all revision numbers are for changesets
1444 # assume all revision numbers are for changesets
1435 n = repo.lookup(rev)
1445 n = repo.lookup(rev)
1436 change = repo.changelog.read(n)
1446 change = repo.changelog.read(n)
1437 m = repo.manifest.read(change[0])
1447 m = repo.manifest.read(change[0])
1438 n = m[relpath(repo, [file])[0]]
1448 n = m[relpath(repo, [file])[0]]
1439 except (hg.RepoError, KeyError):
1449 except (hg.RepoError, KeyError):
1440 n = r.lookup(rev)
1450 n = r.lookup(rev)
1441 else:
1451 else:
1442 n = r.tip()
1452 n = r.tip()
1443 m = r.renamed(n)
1453 m = r.renamed(n)
1444 if m:
1454 if m:
1445 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1455 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1446 else:
1456 else:
1447 ui.write(_("not renamed\n"))
1457 ui.write(_("not renamed\n"))
1448
1458
1449 def debugwalk(ui, repo, *pats, **opts):
1459 def debugwalk(ui, repo, *pats, **opts):
1450 """show how files match on given patterns"""
1460 """show how files match on given patterns"""
1451 items = list(walk(repo, pats, opts))
1461 items = list(walk(repo, pats, opts))
1452 if not items:
1462 if not items:
1453 return
1463 return
1454 fmt = '%%s %%-%ds %%-%ds %%s' % (
1464 fmt = '%%s %%-%ds %%-%ds %%s' % (
1455 max([len(abs) for (src, abs, rel, exact) in items]),
1465 max([len(abs) for (src, abs, rel, exact) in items]),
1456 max([len(rel) for (src, abs, rel, exact) in items]))
1466 max([len(rel) for (src, abs, rel, exact) in items]))
1457 for src, abs, rel, exact in items:
1467 for src, abs, rel, exact in items:
1458 line = fmt % (src, abs, rel, exact and 'exact' or '')
1468 line = fmt % (src, abs, rel, exact and 'exact' or '')
1459 ui.write("%s\n" % line.rstrip())
1469 ui.write("%s\n" % line.rstrip())
1460
1470
1461 def diff(ui, repo, *pats, **opts):
1471 def diff(ui, repo, *pats, **opts):
1462 """diff repository (or selected files)
1472 """diff repository (or selected files)
1463
1473
1464 Show differences between revisions for the specified files.
1474 Show differences between revisions for the specified files.
1465
1475
1466 Differences between files are shown using the unified diff format.
1476 Differences between files are shown using the unified diff format.
1467
1477
1468 When two revision arguments are given, then changes are shown
1478 When two revision arguments are given, then changes are shown
1469 between those revisions. If only one revision is specified then
1479 between those revisions. If only one revision is specified then
1470 that revision is compared to the working directory, and, when no
1480 that revision is compared to the working directory, and, when no
1471 revisions are specified, the working directory files are compared
1481 revisions are specified, the working directory files are compared
1472 to its parent.
1482 to its parent.
1473
1483
1474 Without the -a option, diff will avoid generating diffs of files
1484 Without the -a option, diff will avoid generating diffs of files
1475 it detects as binary. With -a, diff will generate a diff anyway,
1485 it detects as binary. With -a, diff will generate a diff anyway,
1476 probably with undesirable results.
1486 probably with undesirable results.
1477 """
1487 """
1478 node1, node2 = revpair(ui, repo, opts['rev'])
1488 node1, node2 = revpair(ui, repo, opts['rev'])
1479
1489
1480 fns, matchfn, anypats = matchpats(repo, pats, opts)
1490 fns, matchfn, anypats = matchpats(repo, pats, opts)
1481
1491
1482 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1492 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1483 text=opts['text'], opts=opts)
1493 text=opts['text'], opts=opts)
1484
1494
1485 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1495 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1486 node = repo.lookup(changeset)
1496 node = repo.lookup(changeset)
1487 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1497 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1488 if opts['switch_parent']:
1498 if opts['switch_parent']:
1489 parents.reverse()
1499 parents.reverse()
1490 prev = (parents and parents[0]) or nullid
1500 prev = (parents and parents[0]) or nullid
1491 change = repo.changelog.read(node)
1501 change = repo.changelog.read(node)
1492
1502
1493 fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
1503 fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
1494 revwidth=revwidth)
1504 revwidth=revwidth)
1495 if fp != sys.stdout:
1505 if fp != sys.stdout:
1496 ui.note("%s\n" % fp.name)
1506 ui.note("%s\n" % fp.name)
1497
1507
1498 fp.write("# HG changeset patch\n")
1508 fp.write("# HG changeset patch\n")
1499 fp.write("# User %s\n" % change[1])
1509 fp.write("# User %s\n" % change[1])
1500 fp.write("# Date %d %d\n" % change[2])
1510 fp.write("# Date %d %d\n" % change[2])
1501 fp.write("# Node ID %s\n" % hex(node))
1511 fp.write("# Node ID %s\n" % hex(node))
1502 fp.write("# Parent %s\n" % hex(prev))
1512 fp.write("# Parent %s\n" % hex(prev))
1503 if len(parents) > 1:
1513 if len(parents) > 1:
1504 fp.write("# Parent %s\n" % hex(parents[1]))
1514 fp.write("# Parent %s\n" % hex(parents[1]))
1505 fp.write(change[4].rstrip())
1515 fp.write(change[4].rstrip())
1506 fp.write("\n\n")
1516 fp.write("\n\n")
1507
1517
1508 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1518 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1509 if fp != sys.stdout:
1519 if fp != sys.stdout:
1510 fp.close()
1520 fp.close()
1511
1521
1512 def export(ui, repo, *changesets, **opts):
1522 def export(ui, repo, *changesets, **opts):
1513 """dump the header and diffs for one or more changesets
1523 """dump the header and diffs for one or more changesets
1514
1524
1515 Print the changeset header and diffs for one or more revisions.
1525 Print the changeset header and diffs for one or more revisions.
1516
1526
1517 The information shown in the changeset header is: author,
1527 The information shown in the changeset header is: author,
1518 changeset hash, parent and commit comment.
1528 changeset hash, parent and commit comment.
1519
1529
1520 Output may be to a file, in which case the name of the file is
1530 Output may be to a file, in which case the name of the file is
1521 given using a format string. The formatting rules are as follows:
1531 given using a format string. The formatting rules are as follows:
1522
1532
1523 %% literal "%" character
1533 %% literal "%" character
1524 %H changeset hash (40 bytes of hexadecimal)
1534 %H changeset hash (40 bytes of hexadecimal)
1525 %N number of patches being generated
1535 %N number of patches being generated
1526 %R changeset revision number
1536 %R changeset revision number
1527 %b basename of the exporting repository
1537 %b basename of the exporting repository
1528 %h short-form changeset hash (12 bytes of hexadecimal)
1538 %h short-form changeset hash (12 bytes of hexadecimal)
1529 %n zero-padded sequence number, starting at 1
1539 %n zero-padded sequence number, starting at 1
1530 %r zero-padded changeset revision number
1540 %r zero-padded changeset revision number
1531
1541
1532 Without the -a option, export will avoid generating diffs of files
1542 Without the -a option, export will avoid generating diffs of files
1533 it detects as binary. With -a, export will generate a diff anyway,
1543 it detects as binary. With -a, export will generate a diff anyway,
1534 probably with undesirable results.
1544 probably with undesirable results.
1535
1545
1536 With the --switch-parent option, the diff will be against the second
1546 With the --switch-parent option, the diff will be against the second
1537 parent. It can be useful to review a merge.
1547 parent. It can be useful to review a merge.
1538 """
1548 """
1539 if not changesets:
1549 if not changesets:
1540 raise util.Abort(_("export requires at least one changeset"))
1550 raise util.Abort(_("export requires at least one changeset"))
1541 seqno = 0
1551 seqno = 0
1542 revs = list(revrange(ui, repo, changesets))
1552 revs = list(revrange(ui, repo, changesets))
1543 total = len(revs)
1553 total = len(revs)
1544 revwidth = max(map(len, revs))
1554 revwidth = max(map(len, revs))
1545 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1555 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1546 ui.note(msg)
1556 ui.note(msg)
1547 for cset in revs:
1557 for cset in revs:
1548 seqno += 1
1558 seqno += 1
1549 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1559 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1550
1560
1551 def forget(ui, repo, *pats, **opts):
1561 def forget(ui, repo, *pats, **opts):
1552 """don't add the specified files on the next commit (DEPRECATED)
1562 """don't add the specified files on the next commit (DEPRECATED)
1553
1563
1554 (DEPRECATED)
1564 (DEPRECATED)
1555 Undo an 'hg add' scheduled for the next commit.
1565 Undo an 'hg add' scheduled for the next commit.
1556
1566
1557 This command is now deprecated and will be removed in a future
1567 This command is now deprecated and will be removed in a future
1558 release. Please use revert instead.
1568 release. Please use revert instead.
1559 """
1569 """
1560 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1570 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1561 forget = []
1571 forget = []
1562 for src, abs, rel, exact in walk(repo, pats, opts):
1572 for src, abs, rel, exact in walk(repo, pats, opts):
1563 if repo.dirstate.state(abs) == 'a':
1573 if repo.dirstate.state(abs) == 'a':
1564 forget.append(abs)
1574 forget.append(abs)
1565 if ui.verbose or not exact:
1575 if ui.verbose or not exact:
1566 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1576 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1567 repo.forget(forget)
1577 repo.forget(forget)
1568
1578
1569 def grep(ui, repo, pattern, *pats, **opts):
1579 def grep(ui, repo, pattern, *pats, **opts):
1570 """search for a pattern in specified files and revisions
1580 """search for a pattern in specified files and revisions
1571
1581
1572 Search revisions of files for a regular expression.
1582 Search revisions of files for a regular expression.
1573
1583
1574 This command behaves differently than Unix grep. It only accepts
1584 This command behaves differently than Unix grep. It only accepts
1575 Python/Perl regexps. It searches repository history, not the
1585 Python/Perl regexps. It searches repository history, not the
1576 working directory. It always prints the revision number in which
1586 working directory. It always prints the revision number in which
1577 a match appears.
1587 a match appears.
1578
1588
1579 By default, grep only prints output for the first revision of a
1589 By default, grep only prints output for the first revision of a
1580 file in which it finds a match. To get it to print every revision
1590 file in which it finds a match. To get it to print every revision
1581 that contains a change in match status ("-" for a match that
1591 that contains a change in match status ("-" for a match that
1582 becomes a non-match, or "+" for a non-match that becomes a match),
1592 becomes a non-match, or "+" for a non-match that becomes a match),
1583 use the --all flag.
1593 use the --all flag.
1584 """
1594 """
1585 reflags = 0
1595 reflags = 0
1586 if opts['ignore_case']:
1596 if opts['ignore_case']:
1587 reflags |= re.I
1597 reflags |= re.I
1588 regexp = re.compile(pattern, reflags)
1598 regexp = re.compile(pattern, reflags)
1589 sep, eol = ':', '\n'
1599 sep, eol = ':', '\n'
1590 if opts['print0']:
1600 if opts['print0']:
1591 sep = eol = '\0'
1601 sep = eol = '\0'
1592
1602
1593 fcache = {}
1603 fcache = {}
1594 def getfile(fn):
1604 def getfile(fn):
1595 if fn not in fcache:
1605 if fn not in fcache:
1596 fcache[fn] = repo.file(fn)
1606 fcache[fn] = repo.file(fn)
1597 return fcache[fn]
1607 return fcache[fn]
1598
1608
1599 def matchlines(body):
1609 def matchlines(body):
1600 begin = 0
1610 begin = 0
1601 linenum = 0
1611 linenum = 0
1602 while True:
1612 while True:
1603 match = regexp.search(body, begin)
1613 match = regexp.search(body, begin)
1604 if not match:
1614 if not match:
1605 break
1615 break
1606 mstart, mend = match.span()
1616 mstart, mend = match.span()
1607 linenum += body.count('\n', begin, mstart) + 1
1617 linenum += body.count('\n', begin, mstart) + 1
1608 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1618 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1609 lend = body.find('\n', mend)
1619 lend = body.find('\n', mend)
1610 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1620 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1611 begin = lend + 1
1621 begin = lend + 1
1612
1622
1613 class linestate(object):
1623 class linestate(object):
1614 def __init__(self, line, linenum, colstart, colend):
1624 def __init__(self, line, linenum, colstart, colend):
1615 self.line = line
1625 self.line = line
1616 self.linenum = linenum
1626 self.linenum = linenum
1617 self.colstart = colstart
1627 self.colstart = colstart
1618 self.colend = colend
1628 self.colend = colend
1619 def __eq__(self, other):
1629 def __eq__(self, other):
1620 return self.line == other.line
1630 return self.line == other.line
1621 def __hash__(self):
1631 def __hash__(self):
1622 return hash(self.line)
1632 return hash(self.line)
1623
1633
1624 matches = {}
1634 matches = {}
1625 def grepbody(fn, rev, body):
1635 def grepbody(fn, rev, body):
1626 matches[rev].setdefault(fn, {})
1636 matches[rev].setdefault(fn, {})
1627 m = matches[rev][fn]
1637 m = matches[rev][fn]
1628 for lnum, cstart, cend, line in matchlines(body):
1638 for lnum, cstart, cend, line in matchlines(body):
1629 s = linestate(line, lnum, cstart, cend)
1639 s = linestate(line, lnum, cstart, cend)
1630 m[s] = s
1640 m[s] = s
1631
1641
1632 # FIXME: prev isn't used, why ?
1642 # FIXME: prev isn't used, why ?
1633 prev = {}
1643 prev = {}
1634 ucache = {}
1644 ucache = {}
1635 def display(fn, rev, states, prevstates):
1645 def display(fn, rev, states, prevstates):
1636 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1646 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1637 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1647 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1638 counts = {'-': 0, '+': 0}
1648 counts = {'-': 0, '+': 0}
1639 filerevmatches = {}
1649 filerevmatches = {}
1640 for l in diff:
1650 for l in diff:
1641 if incrementing or not opts['all']:
1651 if incrementing or not opts['all']:
1642 change = ((l in prevstates) and '-') or '+'
1652 change = ((l in prevstates) and '-') or '+'
1643 r = rev
1653 r = rev
1644 else:
1654 else:
1645 change = ((l in states) and '-') or '+'
1655 change = ((l in states) and '-') or '+'
1646 r = prev[fn]
1656 r = prev[fn]
1647 cols = [fn, str(rev)]
1657 cols = [fn, str(rev)]
1648 if opts['line_number']:
1658 if opts['line_number']:
1649 cols.append(str(l.linenum))
1659 cols.append(str(l.linenum))
1650 if opts['all']:
1660 if opts['all']:
1651 cols.append(change)
1661 cols.append(change)
1652 if opts['user']:
1662 if opts['user']:
1653 cols.append(trimuser(ui, getchange(rev)[1], rev,
1663 cols.append(trimuser(ui, getchange(rev)[1], rev,
1654 ucache))
1664 ucache))
1655 if opts['files_with_matches']:
1665 if opts['files_with_matches']:
1656 c = (fn, rev)
1666 c = (fn, rev)
1657 if c in filerevmatches:
1667 if c in filerevmatches:
1658 continue
1668 continue
1659 filerevmatches[c] = 1
1669 filerevmatches[c] = 1
1660 else:
1670 else:
1661 cols.append(l.line)
1671 cols.append(l.line)
1662 ui.write(sep.join(cols), eol)
1672 ui.write(sep.join(cols), eol)
1663 counts[change] += 1
1673 counts[change] += 1
1664 return counts['+'], counts['-']
1674 return counts['+'], counts['-']
1665
1675
1666 fstate = {}
1676 fstate = {}
1667 skip = {}
1677 skip = {}
1668 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1678 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1669 count = 0
1679 count = 0
1670 incrementing = False
1680 incrementing = False
1671 for st, rev, fns in changeiter:
1681 for st, rev, fns in changeiter:
1672 if st == 'window':
1682 if st == 'window':
1673 incrementing = rev
1683 incrementing = rev
1674 matches.clear()
1684 matches.clear()
1675 elif st == 'add':
1685 elif st == 'add':
1676 change = repo.changelog.read(repo.lookup(str(rev)))
1686 change = repo.changelog.read(repo.lookup(str(rev)))
1677 mf = repo.manifest.read(change[0])
1687 mf = repo.manifest.read(change[0])
1678 matches[rev] = {}
1688 matches[rev] = {}
1679 for fn in fns:
1689 for fn in fns:
1680 if fn in skip:
1690 if fn in skip:
1681 continue
1691 continue
1682 fstate.setdefault(fn, {})
1692 fstate.setdefault(fn, {})
1683 try:
1693 try:
1684 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1694 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1685 except KeyError:
1695 except KeyError:
1686 pass
1696 pass
1687 elif st == 'iter':
1697 elif st == 'iter':
1688 states = matches[rev].items()
1698 states = matches[rev].items()
1689 states.sort()
1699 states.sort()
1690 for fn, m in states:
1700 for fn, m in states:
1691 if fn in skip:
1701 if fn in skip:
1692 continue
1702 continue
1693 if incrementing or not opts['all'] or fstate[fn]:
1703 if incrementing or not opts['all'] or fstate[fn]:
1694 pos, neg = display(fn, rev, m, fstate[fn])
1704 pos, neg = display(fn, rev, m, fstate[fn])
1695 count += pos + neg
1705 count += pos + neg
1696 if pos and not opts['all']:
1706 if pos and not opts['all']:
1697 skip[fn] = True
1707 skip[fn] = True
1698 fstate[fn] = m
1708 fstate[fn] = m
1699 prev[fn] = rev
1709 prev[fn] = rev
1700
1710
1701 if not incrementing:
1711 if not incrementing:
1702 fstate = fstate.items()
1712 fstate = fstate.items()
1703 fstate.sort()
1713 fstate.sort()
1704 for fn, state in fstate:
1714 for fn, state in fstate:
1705 if fn in skip:
1715 if fn in skip:
1706 continue
1716 continue
1707 display(fn, rev, {}, state)
1717 display(fn, rev, {}, state)
1708 return (count == 0 and 1) or 0
1718 return (count == 0 and 1) or 0
1709
1719
1710 def heads(ui, repo, **opts):
1720 def heads(ui, repo, **opts):
1711 """show current repository heads
1721 """show current repository heads
1712
1722
1713 Show all repository head changesets.
1723 Show all repository head changesets.
1714
1724
1715 Repository "heads" are changesets that don't have children
1725 Repository "heads" are changesets that don't have children
1716 changesets. They are where development generally takes place and
1726 changesets. They are where development generally takes place and
1717 are the usual targets for update and merge operations.
1727 are the usual targets for update and merge operations.
1718 """
1728 """
1719 if opts['rev']:
1729 if opts['rev']:
1720 heads = repo.heads(repo.lookup(opts['rev']))
1730 heads = repo.heads(repo.lookup(opts['rev']))
1721 else:
1731 else:
1722 heads = repo.heads()
1732 heads = repo.heads()
1723 br = None
1733 br = None
1724 if opts['branches']:
1734 if opts['branches']:
1725 br = repo.branchlookup(heads)
1735 br = repo.branchlookup(heads)
1726 displayer = show_changeset(ui, repo, opts)
1736 displayer = show_changeset(ui, repo, opts)
1727 for n in heads:
1737 for n in heads:
1728 displayer.show(changenode=n, brinfo=br)
1738 displayer.show(changenode=n, brinfo=br)
1729
1739
1730 def identify(ui, repo):
1740 def identify(ui, repo):
1731 """print information about the working copy
1741 """print information about the working copy
1732
1742
1733 Print a short summary of the current state of the repo.
1743 Print a short summary of the current state of the repo.
1734
1744
1735 This summary identifies the repository state using one or two parent
1745 This summary identifies the repository state using one or two parent
1736 hash identifiers, followed by a "+" if there are uncommitted changes
1746 hash identifiers, followed by a "+" if there are uncommitted changes
1737 in the working directory, followed by a list of tags for this revision.
1747 in the working directory, followed by a list of tags for this revision.
1738 """
1748 """
1739 parents = [p for p in repo.dirstate.parents() if p != nullid]
1749 parents = [p for p in repo.dirstate.parents() if p != nullid]
1740 if not parents:
1750 if not parents:
1741 ui.write(_("unknown\n"))
1751 ui.write(_("unknown\n"))
1742 return
1752 return
1743
1753
1744 hexfunc = ui.verbose and hex or short
1754 hexfunc = ui.verbose and hex or short
1745 modified, added, removed, deleted, unknown = repo.changes()
1755 modified, added, removed, deleted, unknown = repo.changes()
1746 output = ["%s%s" %
1756 output = ["%s%s" %
1747 ('+'.join([hexfunc(parent) for parent in parents]),
1757 ('+'.join([hexfunc(parent) for parent in parents]),
1748 (modified or added or removed or deleted) and "+" or "")]
1758 (modified or added or removed or deleted) and "+" or "")]
1749
1759
1750 if not ui.quiet:
1760 if not ui.quiet:
1751 # multiple tags for a single parent separated by '/'
1761 # multiple tags for a single parent separated by '/'
1752 parenttags = ['/'.join(tags)
1762 parenttags = ['/'.join(tags)
1753 for tags in map(repo.nodetags, parents) if tags]
1763 for tags in map(repo.nodetags, parents) if tags]
1754 # tags for multiple parents separated by ' + '
1764 # tags for multiple parents separated by ' + '
1755 if parenttags:
1765 if parenttags:
1756 output.append(' + '.join(parenttags))
1766 output.append(' + '.join(parenttags))
1757
1767
1758 ui.write("%s\n" % ' '.join(output))
1768 ui.write("%s\n" % ' '.join(output))
1759
1769
1760 def import_(ui, repo, patch1, *patches, **opts):
1770 def import_(ui, repo, patch1, *patches, **opts):
1761 """import an ordered set of patches
1771 """import an ordered set of patches
1762
1772
1763 Import a list of patches and commit them individually.
1773 Import a list of patches and commit them individually.
1764
1774
1765 If there are outstanding changes in the working directory, import
1775 If there are outstanding changes in the working directory, import
1766 will abort unless given the -f flag.
1776 will abort unless given the -f flag.
1767
1777
1768 You can import a patch straight from a mail message. Even patches
1778 You can import a patch straight from a mail message. Even patches
1769 as attachments work (body part must be type text/plain or
1779 as attachments work (body part must be type text/plain or
1770 text/x-patch to be used). From and Subject headers of email
1780 text/x-patch to be used). From and Subject headers of email
1771 message are used as default committer and commit message. All
1781 message are used as default committer and commit message. All
1772 text/plain body parts before first diff are added to commit
1782 text/plain body parts before first diff are added to commit
1773 message.
1783 message.
1774
1784
1775 If imported patch was generated by hg export, user and description
1785 If imported patch was generated by hg export, user and description
1776 from patch override values from message headers and body. Values
1786 from patch override values from message headers and body. Values
1777 given on command line with -m and -u override these.
1787 given on command line with -m and -u override these.
1778
1788
1779 To read a patch from standard input, use patch name "-".
1789 To read a patch from standard input, use patch name "-".
1780 """
1790 """
1781 patches = (patch1,) + patches
1791 patches = (patch1,) + patches
1782
1792
1783 if not opts['force']:
1793 if not opts['force']:
1784 bail_if_changed(repo)
1794 bail_if_changed(repo)
1785
1795
1786 d = opts["base"]
1796 d = opts["base"]
1787 strip = opts["strip"]
1797 strip = opts["strip"]
1788
1798
1789 mailre = re.compile(r'(?:From |[\w-]+:)')
1799 mailre = re.compile(r'(?:From |[\w-]+:)')
1790
1800
1791 # attempt to detect the start of a patch
1801 # attempt to detect the start of a patch
1792 # (this heuristic is borrowed from quilt)
1802 # (this heuristic is borrowed from quilt)
1793 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1803 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1794 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1804 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1795 '(---|\*\*\*)[ \t])', re.MULTILINE)
1805 '(---|\*\*\*)[ \t])', re.MULTILINE)
1796
1806
1797 for patch in patches:
1807 for patch in patches:
1798 pf = os.path.join(d, patch)
1808 pf = os.path.join(d, patch)
1799
1809
1800 message = None
1810 message = None
1801 user = None
1811 user = None
1802 date = None
1812 date = None
1803 hgpatch = False
1813 hgpatch = False
1804
1814
1805 p = email.Parser.Parser()
1815 p = email.Parser.Parser()
1806 if pf == '-':
1816 if pf == '-':
1807 msg = p.parse(sys.stdin)
1817 msg = p.parse(sys.stdin)
1808 ui.status(_("applying patch from stdin\n"))
1818 ui.status(_("applying patch from stdin\n"))
1809 else:
1819 else:
1810 msg = p.parse(file(pf))
1820 msg = p.parse(file(pf))
1811 ui.status(_("applying %s\n") % patch)
1821 ui.status(_("applying %s\n") % patch)
1812
1822
1813 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1823 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1814 tmpfp = os.fdopen(fd, 'w')
1824 tmpfp = os.fdopen(fd, 'w')
1815 try:
1825 try:
1816 message = msg['Subject']
1826 message = msg['Subject']
1817 if message:
1827 if message:
1818 message = message.replace('\n\t', ' ')
1828 message = message.replace('\n\t', ' ')
1819 ui.debug('Subject: %s\n' % message)
1829 ui.debug('Subject: %s\n' % message)
1820 user = msg['From']
1830 user = msg['From']
1821 if user:
1831 if user:
1822 ui.debug('From: %s\n' % user)
1832 ui.debug('From: %s\n' % user)
1823 diffs_seen = 0
1833 diffs_seen = 0
1824 ok_types = ('text/plain', 'text/x-patch')
1834 ok_types = ('text/plain', 'text/x-patch')
1825 for part in msg.walk():
1835 for part in msg.walk():
1826 content_type = part.get_content_type()
1836 content_type = part.get_content_type()
1827 ui.debug('Content-Type: %s\n' % content_type)
1837 ui.debug('Content-Type: %s\n' % content_type)
1828 if content_type not in ok_types:
1838 if content_type not in ok_types:
1829 continue
1839 continue
1830 payload = part.get_payload(decode=True)
1840 payload = part.get_payload(decode=True)
1831 m = diffre.search(payload)
1841 m = diffre.search(payload)
1832 if m:
1842 if m:
1833 ui.debug(_('found patch at byte %d\n') % m.start(0))
1843 ui.debug(_('found patch at byte %d\n') % m.start(0))
1834 diffs_seen += 1
1844 diffs_seen += 1
1835 hgpatch = False
1845 hgpatch = False
1836 fp = cStringIO.StringIO()
1846 fp = cStringIO.StringIO()
1837 if message:
1847 if message:
1838 fp.write(message)
1848 fp.write(message)
1839 fp.write('\n')
1849 fp.write('\n')
1840 for line in payload[:m.start(0)].splitlines():
1850 for line in payload[:m.start(0)].splitlines():
1841 if line.startswith('# HG changeset patch'):
1851 if line.startswith('# HG changeset patch'):
1842 ui.debug(_('patch generated by hg export\n'))
1852 ui.debug(_('patch generated by hg export\n'))
1843 hgpatch = True
1853 hgpatch = True
1844 # drop earlier commit message content
1854 # drop earlier commit message content
1845 fp.seek(0)
1855 fp.seek(0)
1846 fp.truncate()
1856 fp.truncate()
1847 elif hgpatch:
1857 elif hgpatch:
1848 if line.startswith('# User '):
1858 if line.startswith('# User '):
1849 user = line[7:]
1859 user = line[7:]
1850 ui.debug('From: %s\n' % user)
1860 ui.debug('From: %s\n' % user)
1851 elif line.startswith("# Date "):
1861 elif line.startswith("# Date "):
1852 date = line[7:]
1862 date = line[7:]
1853 if not line.startswith('# '):
1863 if not line.startswith('# '):
1854 fp.write(line)
1864 fp.write(line)
1855 fp.write('\n')
1865 fp.write('\n')
1856 message = fp.getvalue()
1866 message = fp.getvalue()
1857 if tmpfp:
1867 if tmpfp:
1858 tmpfp.write(payload)
1868 tmpfp.write(payload)
1859 if not payload.endswith('\n'):
1869 if not payload.endswith('\n'):
1860 tmpfp.write('\n')
1870 tmpfp.write('\n')
1861 elif not diffs_seen and message and content_type == 'text/plain':
1871 elif not diffs_seen and message and content_type == 'text/plain':
1862 message += '\n' + payload
1872 message += '\n' + payload
1863
1873
1864 if opts['message']:
1874 if opts['message']:
1865 # pickup the cmdline msg
1875 # pickup the cmdline msg
1866 message = opts['message']
1876 message = opts['message']
1867 elif message:
1877 elif message:
1868 # pickup the patch msg
1878 # pickup the patch msg
1869 message = message.strip()
1879 message = message.strip()
1870 else:
1880 else:
1871 # launch the editor
1881 # launch the editor
1872 message = None
1882 message = None
1873 ui.debug(_('message:\n%s\n') % message)
1883 ui.debug(_('message:\n%s\n') % message)
1874
1884
1875 tmpfp.close()
1885 tmpfp.close()
1876 if not diffs_seen:
1886 if not diffs_seen:
1877 raise util.Abort(_('no diffs found'))
1887 raise util.Abort(_('no diffs found'))
1878
1888
1879 files = util.patch(strip, tmpname, ui)
1889 files = util.patch(strip, tmpname, ui)
1880 if len(files) > 0:
1890 if len(files) > 0:
1881 addremove_lock(ui, repo, files, {})
1891 addremove_lock(ui, repo, files, {})
1882 repo.commit(files, message, user, date)
1892 repo.commit(files, message, user, date)
1883 finally:
1893 finally:
1884 os.unlink(tmpname)
1894 os.unlink(tmpname)
1885
1895
1886 def incoming(ui, repo, source="default", **opts):
1896 def incoming(ui, repo, source="default", **opts):
1887 """show new changesets found in source
1897 """show new changesets found in source
1888
1898
1889 Show new changesets found in the specified path/URL or the default
1899 Show new changesets found in the specified path/URL or the default
1890 pull location. These are the changesets that would be pulled if a pull
1900 pull location. These are the changesets that would be pulled if a pull
1891 was requested.
1901 was requested.
1892
1902
1893 For remote repository, using --bundle avoids downloading the changesets
1903 For remote repository, using --bundle avoids downloading the changesets
1894 twice if the incoming is followed by a pull.
1904 twice if the incoming is followed by a pull.
1895
1905
1896 See pull for valid source format details.
1906 See pull for valid source format details.
1897 """
1907 """
1898 source = ui.expandpath(source)
1908 source = ui.expandpath(source)
1899 if opts['ssh']:
1909 if opts['ssh']:
1900 ui.setconfig("ui", "ssh", opts['ssh'])
1910 ui.setconfig("ui", "ssh", opts['ssh'])
1901 if opts['remotecmd']:
1911 if opts['remotecmd']:
1902 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1912 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1903
1913
1904 other = hg.repository(ui, source)
1914 other = hg.repository(ui, source)
1905 incoming = repo.findincoming(other, force=opts["force"])
1915 incoming = repo.findincoming(other, force=opts["force"])
1906 if not incoming:
1916 if not incoming:
1907 ui.status(_("no changes found\n"))
1917 ui.status(_("no changes found\n"))
1908 return
1918 return
1909
1919
1910 cleanup = None
1920 cleanup = None
1911 try:
1921 try:
1912 fname = opts["bundle"]
1922 fname = opts["bundle"]
1913 if fname or not other.local():
1923 if fname or not other.local():
1914 # create a bundle (uncompressed if other repo is not local)
1924 # create a bundle (uncompressed if other repo is not local)
1915 cg = other.changegroup(incoming, "incoming")
1925 cg = other.changegroup(incoming, "incoming")
1916 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1926 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1917 # keep written bundle?
1927 # keep written bundle?
1918 if opts["bundle"]:
1928 if opts["bundle"]:
1919 cleanup = None
1929 cleanup = None
1920 if not other.local():
1930 if not other.local():
1921 # use the created uncompressed bundlerepo
1931 # use the created uncompressed bundlerepo
1922 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1932 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1923
1933
1924 revs = None
1934 revs = None
1925 if opts['rev']:
1935 if opts['rev']:
1926 revs = [other.lookup(rev) for rev in opts['rev']]
1936 revs = [other.lookup(rev) for rev in opts['rev']]
1927 o = other.changelog.nodesbetween(incoming, revs)[0]
1937 o = other.changelog.nodesbetween(incoming, revs)[0]
1928 if opts['newest_first']:
1938 if opts['newest_first']:
1929 o.reverse()
1939 o.reverse()
1930 displayer = show_changeset(ui, other, opts)
1940 displayer = show_changeset(ui, other, opts)
1931 for n in o:
1941 for n in o:
1932 parents = [p for p in other.changelog.parents(n) if p != nullid]
1942 parents = [p for p in other.changelog.parents(n) if p != nullid]
1933 if opts['no_merges'] and len(parents) == 2:
1943 if opts['no_merges'] and len(parents) == 2:
1934 continue
1944 continue
1935 displayer.show(changenode=n)
1945 displayer.show(changenode=n)
1936 if opts['patch']:
1946 if opts['patch']:
1937 prev = (parents and parents[0]) or nullid
1947 prev = (parents and parents[0]) or nullid
1938 dodiff(ui, ui, other, prev, n)
1948 dodiff(ui, ui, other, prev, n)
1939 ui.write("\n")
1949 ui.write("\n")
1940 finally:
1950 finally:
1941 if hasattr(other, 'close'):
1951 if hasattr(other, 'close'):
1942 other.close()
1952 other.close()
1943 if cleanup:
1953 if cleanup:
1944 os.unlink(cleanup)
1954 os.unlink(cleanup)
1945
1955
1946 def init(ui, dest="."):
1956 def init(ui, dest="."):
1947 """create a new repository in the given directory
1957 """create a new repository in the given directory
1948
1958
1949 Initialize a new repository in the given directory. If the given
1959 Initialize a new repository in the given directory. If the given
1950 directory does not exist, it is created.
1960 directory does not exist, it is created.
1951
1961
1952 If no directory is given, the current directory is used.
1962 If no directory is given, the current directory is used.
1953 """
1963 """
1954 hg.repository(ui, dest, create=1)
1964 hg.repository(ui, dest, create=1)
1955
1965
1956 def locate(ui, repo, *pats, **opts):
1966 def locate(ui, repo, *pats, **opts):
1957 """locate files matching specific patterns
1967 """locate files matching specific patterns
1958
1968
1959 Print all files under Mercurial control whose names match the
1969 Print all files under Mercurial control whose names match the
1960 given patterns.
1970 given patterns.
1961
1971
1962 This command searches the current directory and its
1972 This command searches the current directory and its
1963 subdirectories. To search an entire repository, move to the root
1973 subdirectories. To search an entire repository, move to the root
1964 of the repository.
1974 of the repository.
1965
1975
1966 If no patterns are given to match, this command prints all file
1976 If no patterns are given to match, this command prints all file
1967 names.
1977 names.
1968
1978
1969 If you want to feed the output of this command into the "xargs"
1979 If you want to feed the output of this command into the "xargs"
1970 command, use the "-0" option to both this command and "xargs".
1980 command, use the "-0" option to both this command and "xargs".
1971 This will avoid the problem of "xargs" treating single filenames
1981 This will avoid the problem of "xargs" treating single filenames
1972 that contain white space as multiple filenames.
1982 that contain white space as multiple filenames.
1973 """
1983 """
1974 end = opts['print0'] and '\0' or '\n'
1984 end = opts['print0'] and '\0' or '\n'
1975 rev = opts['rev']
1985 rev = opts['rev']
1976 if rev:
1986 if rev:
1977 node = repo.lookup(rev)
1987 node = repo.lookup(rev)
1978 else:
1988 else:
1979 node = None
1989 node = None
1980
1990
1981 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1991 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1982 head='(?:.*/|)'):
1992 head='(?:.*/|)'):
1983 if not node and repo.dirstate.state(abs) == '?':
1993 if not node and repo.dirstate.state(abs) == '?':
1984 continue
1994 continue
1985 if opts['fullpath']:
1995 if opts['fullpath']:
1986 ui.write(os.path.join(repo.root, abs), end)
1996 ui.write(os.path.join(repo.root, abs), end)
1987 else:
1997 else:
1988 ui.write(((pats and rel) or abs), end)
1998 ui.write(((pats and rel) or abs), end)
1989
1999
1990 def log(ui, repo, *pats, **opts):
2000 def log(ui, repo, *pats, **opts):
1991 """show revision history of entire repository or files
2001 """show revision history of entire repository or files
1992
2002
1993 Print the revision history of the specified files or the entire project.
2003 Print the revision history of the specified files or the entire project.
1994
2004
1995 By default this command outputs: changeset id and hash, tags,
2005 By default this command outputs: changeset id and hash, tags,
1996 non-trivial parents, user, date and time, and a summary for each
2006 non-trivial parents, user, date and time, and a summary for each
1997 commit. When the -v/--verbose switch is used, the list of changed
2007 commit. When the -v/--verbose switch is used, the list of changed
1998 files and full commit message is shown.
2008 files and full commit message is shown.
1999 """
2009 """
2000 class dui(object):
2010 class dui(object):
2001 # Implement and delegate some ui protocol. Save hunks of
2011 # Implement and delegate some ui protocol. Save hunks of
2002 # output for later display in the desired order.
2012 # output for later display in the desired order.
2003 def __init__(self, ui):
2013 def __init__(self, ui):
2004 self.ui = ui
2014 self.ui = ui
2005 self.hunk = {}
2015 self.hunk = {}
2006 self.header = {}
2016 self.header = {}
2007 def bump(self, rev):
2017 def bump(self, rev):
2008 self.rev = rev
2018 self.rev = rev
2009 self.hunk[rev] = []
2019 self.hunk[rev] = []
2010 self.header[rev] = []
2020 self.header[rev] = []
2011 def note(self, *args):
2021 def note(self, *args):
2012 if self.verbose:
2022 if self.verbose:
2013 self.write(*args)
2023 self.write(*args)
2014 def status(self, *args):
2024 def status(self, *args):
2015 if not self.quiet:
2025 if not self.quiet:
2016 self.write(*args)
2026 self.write(*args)
2017 def write(self, *args):
2027 def write(self, *args):
2018 self.hunk[self.rev].append(args)
2028 self.hunk[self.rev].append(args)
2019 def write_header(self, *args):
2029 def write_header(self, *args):
2020 self.header[self.rev].append(args)
2030 self.header[self.rev].append(args)
2021 def debug(self, *args):
2031 def debug(self, *args):
2022 if self.debugflag:
2032 if self.debugflag:
2023 self.write(*args)
2033 self.write(*args)
2024 def __getattr__(self, key):
2034 def __getattr__(self, key):
2025 return getattr(self.ui, key)
2035 return getattr(self.ui, key)
2026
2036
2027 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
2037 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
2028
2038
2029 if opts['limit']:
2039 if opts['limit']:
2030 try:
2040 try:
2031 limit = int(opts['limit'])
2041 limit = int(opts['limit'])
2032 except ValueError:
2042 except ValueError:
2033 raise util.Abort(_('limit must be a positive integer'))
2043 raise util.Abort(_('limit must be a positive integer'))
2034 if limit <= 0: raise util.Abort(_('limit must be positive'))
2044 if limit <= 0: raise util.Abort(_('limit must be positive'))
2035 else:
2045 else:
2036 limit = sys.maxint
2046 limit = sys.maxint
2037 count = 0
2047 count = 0
2038
2048
2039 displayer = show_changeset(ui, repo, opts)
2049 displayer = show_changeset(ui, repo, opts)
2040 for st, rev, fns in changeiter:
2050 for st, rev, fns in changeiter:
2041 if st == 'window':
2051 if st == 'window':
2042 du = dui(ui)
2052 du = dui(ui)
2043 displayer.ui = du
2053 displayer.ui = du
2044 elif st == 'add':
2054 elif st == 'add':
2045 du.bump(rev)
2055 du.bump(rev)
2046 changenode = repo.changelog.node(rev)
2056 changenode = repo.changelog.node(rev)
2047 parents = [p for p in repo.changelog.parents(changenode)
2057 parents = [p for p in repo.changelog.parents(changenode)
2048 if p != nullid]
2058 if p != nullid]
2049 if opts['no_merges'] and len(parents) == 2:
2059 if opts['no_merges'] and len(parents) == 2:
2050 continue
2060 continue
2051 if opts['only_merges'] and len(parents) != 2:
2061 if opts['only_merges'] and len(parents) != 2:
2052 continue
2062 continue
2053
2063
2054 if opts['keyword']:
2064 if opts['keyword']:
2055 changes = getchange(rev)
2065 changes = getchange(rev)
2056 miss = 0
2066 miss = 0
2057 for k in [kw.lower() for kw in opts['keyword']]:
2067 for k in [kw.lower() for kw in opts['keyword']]:
2058 if not (k in changes[1].lower() or
2068 if not (k in changes[1].lower() or
2059 k in changes[4].lower() or
2069 k in changes[4].lower() or
2060 k in " ".join(changes[3][:20]).lower()):
2070 k in " ".join(changes[3][:20]).lower()):
2061 miss = 1
2071 miss = 1
2062 break
2072 break
2063 if miss:
2073 if miss:
2064 continue
2074 continue
2065
2075
2066 br = None
2076 br = None
2067 if opts['branches']:
2077 if opts['branches']:
2068 br = repo.branchlookup([repo.changelog.node(rev)])
2078 br = repo.branchlookup([repo.changelog.node(rev)])
2069
2079
2070 displayer.show(rev, brinfo=br)
2080 displayer.show(rev, brinfo=br)
2071 if opts['patch']:
2081 if opts['patch']:
2072 prev = (parents and parents[0]) or nullid
2082 prev = (parents and parents[0]) or nullid
2073 dodiff(du, du, repo, prev, changenode, match=matchfn)
2083 dodiff(du, du, repo, prev, changenode, match=matchfn)
2074 du.write("\n\n")
2084 du.write("\n\n")
2075 elif st == 'iter':
2085 elif st == 'iter':
2076 if count == limit: break
2086 if count == limit: break
2077 if du.header[rev]:
2087 if du.header[rev]:
2078 for args in du.header[rev]:
2088 for args in du.header[rev]:
2079 ui.write_header(*args)
2089 ui.write_header(*args)
2080 if du.hunk[rev]:
2090 if du.hunk[rev]:
2081 count += 1
2091 count += 1
2082 for args in du.hunk[rev]:
2092 for args in du.hunk[rev]:
2083 ui.write(*args)
2093 ui.write(*args)
2084
2094
2085 def manifest(ui, repo, rev=None):
2095 def manifest(ui, repo, rev=None):
2086 """output the latest or given revision of the project manifest
2096 """output the latest or given revision of the project manifest
2087
2097
2088 Print a list of version controlled files for the given revision.
2098 Print a list of version controlled files for the given revision.
2089
2099
2090 The manifest is the list of files being version controlled. If no revision
2100 The manifest is the list of files being version controlled. If no revision
2091 is given then the tip is used.
2101 is given then the tip is used.
2092 """
2102 """
2093 if rev:
2103 if rev:
2094 try:
2104 try:
2095 # assume all revision numbers are for changesets
2105 # assume all revision numbers are for changesets
2096 n = repo.lookup(rev)
2106 n = repo.lookup(rev)
2097 change = repo.changelog.read(n)
2107 change = repo.changelog.read(n)
2098 n = change[0]
2108 n = change[0]
2099 except hg.RepoError:
2109 except hg.RepoError:
2100 n = repo.manifest.lookup(rev)
2110 n = repo.manifest.lookup(rev)
2101 else:
2111 else:
2102 n = repo.manifest.tip()
2112 n = repo.manifest.tip()
2103 m = repo.manifest.read(n)
2113 m = repo.manifest.read(n)
2104 mf = repo.manifest.readflags(n)
2114 mf = repo.manifest.readflags(n)
2105 files = m.keys()
2115 files = m.keys()
2106 files.sort()
2116 files.sort()
2107
2117
2108 for f in files:
2118 for f in files:
2109 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
2119 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
2110
2120
2111 def merge(ui, repo, node=None, **opts):
2121 def merge(ui, repo, node=None, **opts):
2112 """Merge working directory with another revision
2122 """Merge working directory with another revision
2113
2123
2114 Merge the contents of the current working directory and the
2124 Merge the contents of the current working directory and the
2115 requested revision. Files that changed between either parent are
2125 requested revision. Files that changed between either parent are
2116 marked as changed for the next commit and a commit must be
2126 marked as changed for the next commit and a commit must be
2117 performed before any further updates are allowed.
2127 performed before any further updates are allowed.
2118 """
2128 """
2119 return doupdate(ui, repo, node=node, merge=True, **opts)
2129 return doupdate(ui, repo, node=node, merge=True, **opts)
2120
2130
2121 def outgoing(ui, repo, dest=None, **opts):
2131 def outgoing(ui, repo, dest=None, **opts):
2122 """show changesets not found in destination
2132 """show changesets not found in destination
2123
2133
2124 Show changesets not found in the specified destination repository or
2134 Show changesets not found in the specified destination repository or
2125 the default push location. These are the changesets that would be pushed
2135 the default push location. These are the changesets that would be pushed
2126 if a push was requested.
2136 if a push was requested.
2127
2137
2128 See pull for valid destination format details.
2138 See pull for valid destination format details.
2129 """
2139 """
2130 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2140 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2131 if opts['ssh']:
2141 if opts['ssh']:
2132 ui.setconfig("ui", "ssh", opts['ssh'])
2142 ui.setconfig("ui", "ssh", opts['ssh'])
2133 if opts['remotecmd']:
2143 if opts['remotecmd']:
2134 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2144 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2135 revs = None
2145 revs = None
2136 if opts['rev']:
2146 if opts['rev']:
2137 revs = [repo.lookup(rev) for rev in opts['rev']]
2147 revs = [repo.lookup(rev) for rev in opts['rev']]
2138
2148
2139 other = hg.repository(ui, dest)
2149 other = hg.repository(ui, dest)
2140 o = repo.findoutgoing(other, force=opts['force'])
2150 o = repo.findoutgoing(other, force=opts['force'])
2141 if not o:
2151 if not o:
2142 ui.status(_("no changes found\n"))
2152 ui.status(_("no changes found\n"))
2143 return
2153 return
2144 o = repo.changelog.nodesbetween(o, revs)[0]
2154 o = repo.changelog.nodesbetween(o, revs)[0]
2145 if opts['newest_first']:
2155 if opts['newest_first']:
2146 o.reverse()
2156 o.reverse()
2147 displayer = show_changeset(ui, repo, opts)
2157 displayer = show_changeset(ui, repo, opts)
2148 for n in o:
2158 for n in o:
2149 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2159 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2150 if opts['no_merges'] and len(parents) == 2:
2160 if opts['no_merges'] and len(parents) == 2:
2151 continue
2161 continue
2152 displayer.show(changenode=n)
2162 displayer.show(changenode=n)
2153 if opts['patch']:
2163 if opts['patch']:
2154 prev = (parents and parents[0]) or nullid
2164 prev = (parents and parents[0]) or nullid
2155 dodiff(ui, ui, repo, prev, n)
2165 dodiff(ui, ui, repo, prev, n)
2156 ui.write("\n")
2166 ui.write("\n")
2157
2167
2158 def parents(ui, repo, rev=None, branches=None, **opts):
2168 def parents(ui, repo, rev=None, branches=None, **opts):
2159 """show the parents of the working dir or revision
2169 """show the parents of the working dir or revision
2160
2170
2161 Print the working directory's parent revisions.
2171 Print the working directory's parent revisions.
2162 """
2172 """
2163 if rev:
2173 if rev:
2164 p = repo.changelog.parents(repo.lookup(rev))
2174 p = repo.changelog.parents(repo.lookup(rev))
2165 else:
2175 else:
2166 p = repo.dirstate.parents()
2176 p = repo.dirstate.parents()
2167
2177
2168 br = None
2178 br = None
2169 if branches is not None:
2179 if branches is not None:
2170 br = repo.branchlookup(p)
2180 br = repo.branchlookup(p)
2171 displayer = show_changeset(ui, repo, opts)
2181 displayer = show_changeset(ui, repo, opts)
2172 for n in p:
2182 for n in p:
2173 if n != nullid:
2183 if n != nullid:
2174 displayer.show(changenode=n, brinfo=br)
2184 displayer.show(changenode=n, brinfo=br)
2175
2185
2176 def paths(ui, repo, search=None):
2186 def paths(ui, repo, search=None):
2177 """show definition of symbolic path names
2187 """show definition of symbolic path names
2178
2188
2179 Show definition of symbolic path name NAME. If no name is given, show
2189 Show definition of symbolic path name NAME. If no name is given, show
2180 definition of available names.
2190 definition of available names.
2181
2191
2182 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2192 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2183 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2193 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2184 """
2194 """
2185 if search:
2195 if search:
2186 for name, path in ui.configitems("paths"):
2196 for name, path in ui.configitems("paths"):
2187 if name == search:
2197 if name == search:
2188 ui.write("%s\n" % path)
2198 ui.write("%s\n" % path)
2189 return
2199 return
2190 ui.warn(_("not found!\n"))
2200 ui.warn(_("not found!\n"))
2191 return 1
2201 return 1
2192 else:
2202 else:
2193 for name, path in ui.configitems("paths"):
2203 for name, path in ui.configitems("paths"):
2194 ui.write("%s = %s\n" % (name, path))
2204 ui.write("%s = %s\n" % (name, path))
2195
2205
2196 def postincoming(ui, repo, modheads, optupdate):
2206 def postincoming(ui, repo, modheads, optupdate):
2197 if modheads == 0:
2207 if modheads == 0:
2198 return
2208 return
2199 if optupdate:
2209 if optupdate:
2200 if modheads == 1:
2210 if modheads == 1:
2201 return doupdate(ui, repo)
2211 return doupdate(ui, repo)
2202 else:
2212 else:
2203 ui.status(_("not updating, since new heads added\n"))
2213 ui.status(_("not updating, since new heads added\n"))
2204 if modheads > 1:
2214 if modheads > 1:
2205 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2215 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2206 else:
2216 else:
2207 ui.status(_("(run 'hg update' to get a working copy)\n"))
2217 ui.status(_("(run 'hg update' to get a working copy)\n"))
2208
2218
2209 def pull(ui, repo, source="default", **opts):
2219 def pull(ui, repo, source="default", **opts):
2210 """pull changes from the specified source
2220 """pull changes from the specified source
2211
2221
2212 Pull changes from a remote repository to a local one.
2222 Pull changes from a remote repository to a local one.
2213
2223
2214 This finds all changes from the repository at the specified path
2224 This finds all changes from the repository at the specified path
2215 or URL and adds them to the local repository. By default, this
2225 or URL and adds them to the local repository. By default, this
2216 does not update the copy of the project in the working directory.
2226 does not update the copy of the project in the working directory.
2217
2227
2218 Valid URLs are of the form:
2228 Valid URLs are of the form:
2219
2229
2220 local/filesystem/path
2230 local/filesystem/path
2221 http://[user@]host[:port][/path]
2231 http://[user@]host[:port][/path]
2222 https://[user@]host[:port][/path]
2232 https://[user@]host[:port][/path]
2223 ssh://[user@]host[:port][/path]
2233 ssh://[user@]host[:port][/path]
2224
2234
2225 Some notes about using SSH with Mercurial:
2235 Some notes about using SSH with Mercurial:
2226 - SSH requires an accessible shell account on the destination machine
2236 - SSH requires an accessible shell account on the destination machine
2227 and a copy of hg in the remote path or specified with as remotecmd.
2237 and a copy of hg in the remote path or specified with as remotecmd.
2228 - /path is relative to the remote user's home directory by default.
2238 - /path is relative to the remote user's home directory by default.
2229 Use two slashes at the start of a path to specify an absolute path.
2239 Use two slashes at the start of a path to specify an absolute path.
2230 - Mercurial doesn't use its own compression via SSH; the right thing
2240 - Mercurial doesn't use its own compression via SSH; the right thing
2231 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2241 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2232 Host *.mylocalnetwork.example.com
2242 Host *.mylocalnetwork.example.com
2233 Compression off
2243 Compression off
2234 Host *
2244 Host *
2235 Compression on
2245 Compression on
2236 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2246 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2237 with the --ssh command line option.
2247 with the --ssh command line option.
2238 """
2248 """
2239 source = ui.expandpath(source)
2249 source = ui.expandpath(source)
2240
2250
2241 if opts['ssh']:
2251 if opts['ssh']:
2242 ui.setconfig("ui", "ssh", opts['ssh'])
2252 ui.setconfig("ui", "ssh", opts['ssh'])
2243 if opts['remotecmd']:
2253 if opts['remotecmd']:
2244 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2254 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2245
2255
2246 other = hg.repository(ui, source)
2256 other = hg.repository(ui, source)
2247 ui.status(_('pulling from %s\n') % (source))
2257 ui.status(_('pulling from %s\n') % (source))
2248 revs = None
2258 revs = None
2249 if opts['rev'] and not other.local():
2259 if opts['rev'] and not other.local():
2250 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2260 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2251 elif opts['rev']:
2261 elif opts['rev']:
2252 revs = [other.lookup(rev) for rev in opts['rev']]
2262 revs = [other.lookup(rev) for rev in opts['rev']]
2253 modheads = repo.pull(other, heads=revs, force=opts['force'])
2263 modheads = repo.pull(other, heads=revs, force=opts['force'])
2254 return postincoming(ui, repo, modheads, opts['update'])
2264 return postincoming(ui, repo, modheads, opts['update'])
2255
2265
2256 def push(ui, repo, dest=None, **opts):
2266 def push(ui, repo, dest=None, **opts):
2257 """push changes to the specified destination
2267 """push changes to the specified destination
2258
2268
2259 Push changes from the local repository to the given destination.
2269 Push changes from the local repository to the given destination.
2260
2270
2261 This is the symmetrical operation for pull. It helps to move
2271 This is the symmetrical operation for pull. It helps to move
2262 changes from the current repository to a different one. If the
2272 changes from the current repository to a different one. If the
2263 destination is local this is identical to a pull in that directory
2273 destination is local this is identical to a pull in that directory
2264 from the current one.
2274 from the current one.
2265
2275
2266 By default, push will refuse to run if it detects the result would
2276 By default, push will refuse to run if it detects the result would
2267 increase the number of remote heads. This generally indicates the
2277 increase the number of remote heads. This generally indicates the
2268 the client has forgotten to sync and merge before pushing.
2278 the client has forgotten to sync and merge before pushing.
2269
2279
2270 Valid URLs are of the form:
2280 Valid URLs are of the form:
2271
2281
2272 local/filesystem/path
2282 local/filesystem/path
2273 ssh://[user@]host[:port][/path]
2283 ssh://[user@]host[:port][/path]
2274
2284
2275 Look at the help text for the pull command for important details
2285 Look at the help text for the pull command for important details
2276 about ssh:// URLs.
2286 about ssh:// URLs.
2277 """
2287 """
2278 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2288 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2279
2289
2280 if opts['ssh']:
2290 if opts['ssh']:
2281 ui.setconfig("ui", "ssh", opts['ssh'])
2291 ui.setconfig("ui", "ssh", opts['ssh'])
2282 if opts['remotecmd']:
2292 if opts['remotecmd']:
2283 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2293 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2284
2294
2285 other = hg.repository(ui, dest)
2295 other = hg.repository(ui, dest)
2286 ui.status('pushing to %s\n' % (dest))
2296 ui.status('pushing to %s\n' % (dest))
2287 revs = None
2297 revs = None
2288 if opts['rev']:
2298 if opts['rev']:
2289 revs = [repo.lookup(rev) for rev in opts['rev']]
2299 revs = [repo.lookup(rev) for rev in opts['rev']]
2290 r = repo.push(other, opts['force'], revs=revs)
2300 r = repo.push(other, opts['force'], revs=revs)
2291 return r == 0
2301 return r == 0
2292
2302
2293 def rawcommit(ui, repo, *flist, **rc):
2303 def rawcommit(ui, repo, *flist, **rc):
2294 """raw commit interface (DEPRECATED)
2304 """raw commit interface (DEPRECATED)
2295
2305
2296 (DEPRECATED)
2306 (DEPRECATED)
2297 Lowlevel commit, for use in helper scripts.
2307 Lowlevel commit, for use in helper scripts.
2298
2308
2299 This command is not intended to be used by normal users, as it is
2309 This command is not intended to be used by normal users, as it is
2300 primarily useful for importing from other SCMs.
2310 primarily useful for importing from other SCMs.
2301
2311
2302 This command is now deprecated and will be removed in a future
2312 This command is now deprecated and will be removed in a future
2303 release, please use debugsetparents and commit instead.
2313 release, please use debugsetparents and commit instead.
2304 """
2314 """
2305
2315
2306 ui.warn(_("(the rawcommit command is deprecated)\n"))
2316 ui.warn(_("(the rawcommit command is deprecated)\n"))
2307
2317
2308 message = rc['message']
2318 message = rc['message']
2309 if not message and rc['logfile']:
2319 if not message and rc['logfile']:
2310 try:
2320 try:
2311 message = open(rc['logfile']).read()
2321 message = open(rc['logfile']).read()
2312 except IOError:
2322 except IOError:
2313 pass
2323 pass
2314 if not message and not rc['logfile']:
2324 if not message and not rc['logfile']:
2315 raise util.Abort(_("missing commit message"))
2325 raise util.Abort(_("missing commit message"))
2316
2326
2317 files = relpath(repo, list(flist))
2327 files = relpath(repo, list(flist))
2318 if rc['files']:
2328 if rc['files']:
2319 files += open(rc['files']).read().splitlines()
2329 files += open(rc['files']).read().splitlines()
2320
2330
2321 rc['parent'] = map(repo.lookup, rc['parent'])
2331 rc['parent'] = map(repo.lookup, rc['parent'])
2322
2332
2323 try:
2333 try:
2324 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2334 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2325 except ValueError, inst:
2335 except ValueError, inst:
2326 raise util.Abort(str(inst))
2336 raise util.Abort(str(inst))
2327
2337
2328 def recover(ui, repo):
2338 def recover(ui, repo):
2329 """roll back an interrupted transaction
2339 """roll back an interrupted transaction
2330
2340
2331 Recover from an interrupted commit or pull.
2341 Recover from an interrupted commit or pull.
2332
2342
2333 This command tries to fix the repository status after an interrupted
2343 This command tries to fix the repository status after an interrupted
2334 operation. It should only be necessary when Mercurial suggests it.
2344 operation. It should only be necessary when Mercurial suggests it.
2335 """
2345 """
2336 if repo.recover():
2346 if repo.recover():
2337 return repo.verify()
2347 return repo.verify()
2338 return 1
2348 return 1
2339
2349
2340 def remove(ui, repo, *pats, **opts):
2350 def remove(ui, repo, *pats, **opts):
2341 """remove the specified files on the next commit
2351 """remove the specified files on the next commit
2342
2352
2343 Schedule the indicated files for removal from the repository.
2353 Schedule the indicated files for removal from the repository.
2344
2354
2345 This command schedules the files to be removed at the next commit.
2355 This command schedules the files to be removed at the next commit.
2346 This only removes files from the current branch, not from the
2356 This only removes files from the current branch, not from the
2347 entire project history. If the files still exist in the working
2357 entire project history. If the files still exist in the working
2348 directory, they will be deleted from it. If invoked with --after,
2358 directory, they will be deleted from it. If invoked with --after,
2349 files that have been manually deleted are marked as removed.
2359 files that have been manually deleted are marked as removed.
2350
2360
2351 Modified files and added files are not removed by default. To
2361 Modified files and added files are not removed by default. To
2352 remove them, use the -f/--force option.
2362 remove them, use the -f/--force option.
2353 """
2363 """
2354 names = []
2364 names = []
2355 if not opts['after'] and not pats:
2365 if not opts['after'] and not pats:
2356 raise util.Abort(_('no files specified'))
2366 raise util.Abort(_('no files specified'))
2357 files, matchfn, anypats = matchpats(repo, pats, opts)
2367 files, matchfn, anypats = matchpats(repo, pats, opts)
2358 exact = dict.fromkeys(files)
2368 exact = dict.fromkeys(files)
2359 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn))
2369 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn))
2360 modified, added, removed, deleted, unknown = mardu
2370 modified, added, removed, deleted, unknown = mardu
2361 remove, forget = [], []
2371 remove, forget = [], []
2362 for src, abs, rel, exact in walk(repo, pats, opts):
2372 for src, abs, rel, exact in walk(repo, pats, opts):
2363 reason = None
2373 reason = None
2364 if abs not in deleted and opts['after']:
2374 if abs not in deleted and opts['after']:
2365 reason = _('is still present')
2375 reason = _('is still present')
2366 elif abs in modified and not opts['force']:
2376 elif abs in modified and not opts['force']:
2367 reason = _('is modified (use -f to force removal)')
2377 reason = _('is modified (use -f to force removal)')
2368 elif abs in added:
2378 elif abs in added:
2369 if opts['force']:
2379 if opts['force']:
2370 forget.append(abs)
2380 forget.append(abs)
2371 continue
2381 continue
2372 reason = _('has been marked for add (use -f to force removal)')
2382 reason = _('has been marked for add (use -f to force removal)')
2373 elif abs in unknown:
2383 elif abs in unknown:
2374 reason = _('is not managed')
2384 reason = _('is not managed')
2375 elif abs in removed:
2385 elif abs in removed:
2376 continue
2386 continue
2377 if reason:
2387 if reason:
2378 if exact:
2388 if exact:
2379 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2389 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2380 else:
2390 else:
2381 if ui.verbose or not exact:
2391 if ui.verbose or not exact:
2382 ui.status(_('removing %s\n') % rel)
2392 ui.status(_('removing %s\n') % rel)
2383 remove.append(abs)
2393 remove.append(abs)
2384 repo.forget(forget)
2394 repo.forget(forget)
2385 repo.remove(remove, unlink=not opts['after'])
2395 repo.remove(remove, unlink=not opts['after'])
2386
2396
2387 def rename(ui, repo, *pats, **opts):
2397 def rename(ui, repo, *pats, **opts):
2388 """rename files; equivalent of copy + remove
2398 """rename files; equivalent of copy + remove
2389
2399
2390 Mark dest as copies of sources; mark sources for deletion. If
2400 Mark dest as copies of sources; mark sources for deletion. If
2391 dest is a directory, copies are put in that directory. If dest is
2401 dest is a directory, copies are put in that directory. If dest is
2392 a file, there can only be one source.
2402 a file, there can only be one source.
2393
2403
2394 By default, this command copies the contents of files as they
2404 By default, this command copies the contents of files as they
2395 stand in the working directory. If invoked with --after, the
2405 stand in the working directory. If invoked with --after, the
2396 operation is recorded, but no copying is performed.
2406 operation is recorded, but no copying is performed.
2397
2407
2398 This command takes effect in the next commit.
2408 This command takes effect in the next commit.
2399
2409
2400 NOTE: This command should be treated as experimental. While it
2410 NOTE: This command should be treated as experimental. While it
2401 should properly record rename files, this information is not yet
2411 should properly record rename files, this information is not yet
2402 fully used by merge, nor fully reported by log.
2412 fully used by merge, nor fully reported by log.
2403 """
2413 """
2404 wlock = repo.wlock(0)
2414 wlock = repo.wlock(0)
2405 errs, copied = docopy(ui, repo, pats, opts, wlock)
2415 errs, copied = docopy(ui, repo, pats, opts, wlock)
2406 names = []
2416 names = []
2407 for abs, rel, exact in copied:
2417 for abs, rel, exact in copied:
2408 if ui.verbose or not exact:
2418 if ui.verbose or not exact:
2409 ui.status(_('removing %s\n') % rel)
2419 ui.status(_('removing %s\n') % rel)
2410 names.append(abs)
2420 names.append(abs)
2411 if not opts.get('dry_run'):
2421 if not opts.get('dry_run'):
2412 repo.remove(names, True, wlock)
2422 repo.remove(names, True, wlock)
2413 return errs
2423 return errs
2414
2424
2415 def revert(ui, repo, *pats, **opts):
2425 def revert(ui, repo, *pats, **opts):
2416 """revert files or dirs to their states as of some revision
2426 """revert files or dirs to their states as of some revision
2417
2427
2418 With no revision specified, revert the named files or directories
2428 With no revision specified, revert the named files or directories
2419 to the contents they had in the parent of the working directory.
2429 to the contents they had in the parent of the working directory.
2420 This restores the contents of the affected files to an unmodified
2430 This restores the contents of the affected files to an unmodified
2421 state. If the working directory has two parents, you must
2431 state. If the working directory has two parents, you must
2422 explicitly specify the revision to revert to.
2432 explicitly specify the revision to revert to.
2423
2433
2424 Modified files are saved with a .orig suffix before reverting.
2434 Modified files are saved with a .orig suffix before reverting.
2425 To disable these backups, use --no-backup.
2435 To disable these backups, use --no-backup.
2426
2436
2427 Using the -r option, revert the given files or directories to
2437 Using the -r option, revert the given files or directories to
2428 their contents as of a specific revision. This can be helpful to"roll
2438 their contents as of a specific revision. This can be helpful to"roll
2429 back" some or all of a change that should not have been committed.
2439 back" some or all of a change that should not have been committed.
2430
2440
2431 Revert modifies the working directory. It does not commit any
2441 Revert modifies the working directory. It does not commit any
2432 changes, or change the parent of the working directory. If you
2442 changes, or change the parent of the working directory. If you
2433 revert to a revision other than the parent of the working
2443 revert to a revision other than the parent of the working
2434 directory, the reverted files will thus appear modified
2444 directory, the reverted files will thus appear modified
2435 afterwards.
2445 afterwards.
2436
2446
2437 If a file has been deleted, it is recreated. If the executable
2447 If a file has been deleted, it is recreated. If the executable
2438 mode of a file was changed, it is reset.
2448 mode of a file was changed, it is reset.
2439
2449
2440 If names are given, all files matching the names are reverted.
2450 If names are given, all files matching the names are reverted.
2441
2451
2442 If no arguments are given, all files in the repository are reverted.
2452 If no arguments are given, all files in the repository are reverted.
2443 """
2453 """
2444 parent, p2 = repo.dirstate.parents()
2454 parent, p2 = repo.dirstate.parents()
2445 if opts['rev']:
2455 if opts['rev']:
2446 node = repo.lookup(opts['rev'])
2456 node = repo.lookup(opts['rev'])
2447 elif p2 != nullid:
2457 elif p2 != nullid:
2448 raise util.Abort(_('working dir has two parents; '
2458 raise util.Abort(_('working dir has two parents; '
2449 'you must specify the revision to revert to'))
2459 'you must specify the revision to revert to'))
2450 else:
2460 else:
2451 node = parent
2461 node = parent
2452 mf = repo.manifest.read(repo.changelog.read(node)[0])
2462 mf = repo.manifest.read(repo.changelog.read(node)[0])
2453 if node == parent:
2463 if node == parent:
2454 pmf = mf
2464 pmf = mf
2455 else:
2465 else:
2456 pmf = None
2466 pmf = None
2457
2467
2458 wlock = repo.wlock()
2468 wlock = repo.wlock()
2459
2469
2460 # need all matching names in dirstate and manifest of target rev,
2470 # need all matching names in dirstate and manifest of target rev,
2461 # so have to walk both. do not print errors if files exist in one
2471 # so have to walk both. do not print errors if files exist in one
2462 # but not other.
2472 # but not other.
2463
2473
2464 names = {}
2474 names = {}
2465 target_only = {}
2475 target_only = {}
2466
2476
2467 # walk dirstate.
2477 # walk dirstate.
2468
2478
2469 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2479 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2470 names[abs] = (rel, exact)
2480 names[abs] = (rel, exact)
2471 if src == 'b':
2481 if src == 'b':
2472 target_only[abs] = True
2482 target_only[abs] = True
2473
2483
2474 # walk target manifest.
2484 # walk target manifest.
2475
2485
2476 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2486 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2477 badmatch=names.has_key):
2487 badmatch=names.has_key):
2478 if abs in names: continue
2488 if abs in names: continue
2479 names[abs] = (rel, exact)
2489 names[abs] = (rel, exact)
2480 target_only[abs] = True
2490 target_only[abs] = True
2481
2491
2482 changes = repo.changes(match=names.has_key, wlock=wlock)
2492 changes = repo.changes(match=names.has_key, wlock=wlock)
2483 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2493 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2484
2494
2485 revert = ([], _('reverting %s\n'))
2495 revert = ([], _('reverting %s\n'))
2486 add = ([], _('adding %s\n'))
2496 add = ([], _('adding %s\n'))
2487 remove = ([], _('removing %s\n'))
2497 remove = ([], _('removing %s\n'))
2488 forget = ([], _('forgetting %s\n'))
2498 forget = ([], _('forgetting %s\n'))
2489 undelete = ([], _('undeleting %s\n'))
2499 undelete = ([], _('undeleting %s\n'))
2490 update = {}
2500 update = {}
2491
2501
2492 disptable = (
2502 disptable = (
2493 # dispatch table:
2503 # dispatch table:
2494 # file state
2504 # file state
2495 # action if in target manifest
2505 # action if in target manifest
2496 # action if not in target manifest
2506 # action if not in target manifest
2497 # make backup if in target manifest
2507 # make backup if in target manifest
2498 # make backup if not in target manifest
2508 # make backup if not in target manifest
2499 (modified, revert, remove, True, True),
2509 (modified, revert, remove, True, True),
2500 (added, revert, forget, True, False),
2510 (added, revert, forget, True, False),
2501 (removed, undelete, None, False, False),
2511 (removed, undelete, None, False, False),
2502 (deleted, revert, remove, False, False),
2512 (deleted, revert, remove, False, False),
2503 (unknown, add, None, True, False),
2513 (unknown, add, None, True, False),
2504 (target_only, add, None, False, False),
2514 (target_only, add, None, False, False),
2505 )
2515 )
2506
2516
2507 entries = names.items()
2517 entries = names.items()
2508 entries.sort()
2518 entries.sort()
2509
2519
2510 for abs, (rel, exact) in entries:
2520 for abs, (rel, exact) in entries:
2511 mfentry = mf.get(abs)
2521 mfentry = mf.get(abs)
2512 def handle(xlist, dobackup):
2522 def handle(xlist, dobackup):
2513 xlist[0].append(abs)
2523 xlist[0].append(abs)
2514 update[abs] = 1
2524 update[abs] = 1
2515 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2525 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2516 bakname = "%s.orig" % rel
2526 bakname = "%s.orig" % rel
2517 ui.note(_('saving current version of %s as %s\n') %
2527 ui.note(_('saving current version of %s as %s\n') %
2518 (rel, bakname))
2528 (rel, bakname))
2519 if not opts.get('dry_run'):
2529 if not opts.get('dry_run'):
2520 shutil.copyfile(rel, bakname)
2530 shutil.copyfile(rel, bakname)
2521 shutil.copymode(rel, bakname)
2531 shutil.copymode(rel, bakname)
2522 if ui.verbose or not exact:
2532 if ui.verbose or not exact:
2523 ui.status(xlist[1] % rel)
2533 ui.status(xlist[1] % rel)
2524 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2534 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2525 if abs not in table: continue
2535 if abs not in table: continue
2526 # file has changed in dirstate
2536 # file has changed in dirstate
2527 if mfentry:
2537 if mfentry:
2528 handle(hitlist, backuphit)
2538 handle(hitlist, backuphit)
2529 elif misslist is not None:
2539 elif misslist is not None:
2530 handle(misslist, backupmiss)
2540 handle(misslist, backupmiss)
2531 else:
2541 else:
2532 if exact: ui.warn(_('file not managed: %s\n' % rel))
2542 if exact: ui.warn(_('file not managed: %s\n' % rel))
2533 break
2543 break
2534 else:
2544 else:
2535 # file has not changed in dirstate
2545 # file has not changed in dirstate
2536 if node == parent:
2546 if node == parent:
2537 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2547 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2538 continue
2548 continue
2539 if pmf is None:
2549 if pmf is None:
2540 # only need parent manifest in this unlikely case,
2550 # only need parent manifest in this unlikely case,
2541 # so do not read by default
2551 # so do not read by default
2542 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2552 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2543 if abs in pmf:
2553 if abs in pmf:
2544 if mfentry:
2554 if mfentry:
2545 # if version of file is same in parent and target
2555 # if version of file is same in parent and target
2546 # manifests, do nothing
2556 # manifests, do nothing
2547 if pmf[abs] != mfentry:
2557 if pmf[abs] != mfentry:
2548 handle(revert, False)
2558 handle(revert, False)
2549 else:
2559 else:
2550 handle(remove, False)
2560 handle(remove, False)
2551
2561
2552 if not opts.get('dry_run'):
2562 if not opts.get('dry_run'):
2553 repo.dirstate.forget(forget[0])
2563 repo.dirstate.forget(forget[0])
2554 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2564 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2555 show_stats=False)
2565 show_stats=False)
2556 repo.dirstate.update(add[0], 'a')
2566 repo.dirstate.update(add[0], 'a')
2557 repo.dirstate.update(undelete[0], 'n')
2567 repo.dirstate.update(undelete[0], 'n')
2558 repo.dirstate.update(remove[0], 'r')
2568 repo.dirstate.update(remove[0], 'r')
2559 return r
2569 return r
2560
2570
2561 def rollback(ui, repo):
2571 def rollback(ui, repo):
2562 """roll back the last transaction in this repository
2572 """roll back the last transaction in this repository
2563
2573
2564 Roll back the last transaction in this repository, restoring the
2574 Roll back the last transaction in this repository, restoring the
2565 project to its state prior to the transaction.
2575 project to its state prior to the transaction.
2566
2576
2567 Transactions are used to encapsulate the effects of all commands
2577 Transactions are used to encapsulate the effects of all commands
2568 that create new changesets or propagate existing changesets into a
2578 that create new changesets or propagate existing changesets into a
2569 repository. For example, the following commands are transactional,
2579 repository. For example, the following commands are transactional,
2570 and their effects can be rolled back:
2580 and their effects can be rolled back:
2571
2581
2572 commit
2582 commit
2573 import
2583 import
2574 pull
2584 pull
2575 push (with this repository as destination)
2585 push (with this repository as destination)
2576 unbundle
2586 unbundle
2577
2587
2578 This command should be used with care. There is only one level of
2588 This command should be used with care. There is only one level of
2579 rollback, and there is no way to undo a rollback.
2589 rollback, and there is no way to undo a rollback.
2580
2590
2581 This command is not intended for use on public repositories. Once
2591 This command is not intended for use on public repositories. Once
2582 changes are visible for pull by other users, rolling a transaction
2592 changes are visible for pull by other users, rolling a transaction
2583 back locally is ineffective (someone else may already have pulled
2593 back locally is ineffective (someone else may already have pulled
2584 the changes). Furthermore, a race is possible with readers of the
2594 the changes). Furthermore, a race is possible with readers of the
2585 repository; for example an in-progress pull from the repository
2595 repository; for example an in-progress pull from the repository
2586 may fail if a rollback is performed.
2596 may fail if a rollback is performed.
2587 """
2597 """
2588 repo.rollback()
2598 repo.rollback()
2589
2599
2590 def root(ui, repo):
2600 def root(ui, repo):
2591 """print the root (top) of the current working dir
2601 """print the root (top) of the current working dir
2592
2602
2593 Print the root directory of the current repository.
2603 Print the root directory of the current repository.
2594 """
2604 """
2595 ui.write(repo.root + "\n")
2605 ui.write(repo.root + "\n")
2596
2606
2597 def serve(ui, repo, **opts):
2607 def serve(ui, repo, **opts):
2598 """export the repository via HTTP
2608 """export the repository via HTTP
2599
2609
2600 Start a local HTTP repository browser and pull server.
2610 Start a local HTTP repository browser and pull server.
2601
2611
2602 By default, the server logs accesses to stdout and errors to
2612 By default, the server logs accesses to stdout and errors to
2603 stderr. Use the "-A" and "-E" options to log to files.
2613 stderr. Use the "-A" and "-E" options to log to files.
2604 """
2614 """
2605
2615
2606 if opts["stdio"]:
2616 if opts["stdio"]:
2607 if repo is None:
2617 if repo is None:
2608 raise hg.RepoError(_('no repo found'))
2618 raise hg.RepoError(_('no repo found'))
2609 s = sshserver.sshserver(ui, repo)
2619 s = sshserver.sshserver(ui, repo)
2610 s.serve_forever()
2620 s.serve_forever()
2611
2621
2612 optlist = ("name templates style address port ipv6"
2622 optlist = ("name templates style address port ipv6"
2613 " accesslog errorlog webdir_conf")
2623 " accesslog errorlog webdir_conf")
2614 for o in optlist.split():
2624 for o in optlist.split():
2615 if opts[o]:
2625 if opts[o]:
2616 ui.setconfig("web", o, opts[o])
2626 ui.setconfig("web", o, opts[o])
2617
2627
2618 if repo is None and not ui.config("web", "webdir_conf"):
2628 if repo is None and not ui.config("web", "webdir_conf"):
2619 raise hg.RepoError(_('no repo found'))
2629 raise hg.RepoError(_('no repo found'))
2620
2630
2621 if opts['daemon'] and not opts['daemon_pipefds']:
2631 if opts['daemon'] and not opts['daemon_pipefds']:
2622 rfd, wfd = os.pipe()
2632 rfd, wfd = os.pipe()
2623 args = sys.argv[:]
2633 args = sys.argv[:]
2624 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2634 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2625 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2635 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2626 args[0], args)
2636 args[0], args)
2627 os.close(wfd)
2637 os.close(wfd)
2628 os.read(rfd, 1)
2638 os.read(rfd, 1)
2629 os._exit(0)
2639 os._exit(0)
2630
2640
2631 try:
2641 try:
2632 httpd = hgweb.server.create_server(ui, repo)
2642 httpd = hgweb.server.create_server(ui, repo)
2633 except socket.error, inst:
2643 except socket.error, inst:
2634 raise util.Abort(_('cannot start server: ') + inst.args[1])
2644 raise util.Abort(_('cannot start server: ') + inst.args[1])
2635
2645
2636 if ui.verbose:
2646 if ui.verbose:
2637 addr, port = httpd.socket.getsockname()
2647 addr, port = httpd.socket.getsockname()
2638 if addr == '0.0.0.0':
2648 if addr == '0.0.0.0':
2639 addr = socket.gethostname()
2649 addr = socket.gethostname()
2640 else:
2650 else:
2641 try:
2651 try:
2642 addr = socket.gethostbyaddr(addr)[0]
2652 addr = socket.gethostbyaddr(addr)[0]
2643 except socket.error:
2653 except socket.error:
2644 pass
2654 pass
2645 if port != 80:
2655 if port != 80:
2646 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2656 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2647 else:
2657 else:
2648 ui.status(_('listening at http://%s/\n') % addr)
2658 ui.status(_('listening at http://%s/\n') % addr)
2649
2659
2650 if opts['pid_file']:
2660 if opts['pid_file']:
2651 fp = open(opts['pid_file'], 'w')
2661 fp = open(opts['pid_file'], 'w')
2652 fp.write(str(os.getpid()) + '\n')
2662 fp.write(str(os.getpid()) + '\n')
2653 fp.close()
2663 fp.close()
2654
2664
2655 if opts['daemon_pipefds']:
2665 if opts['daemon_pipefds']:
2656 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2666 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2657 os.close(rfd)
2667 os.close(rfd)
2658 os.write(wfd, 'y')
2668 os.write(wfd, 'y')
2659 os.close(wfd)
2669 os.close(wfd)
2660 sys.stdout.flush()
2670 sys.stdout.flush()
2661 sys.stderr.flush()
2671 sys.stderr.flush()
2662 fd = os.open(util.nulldev, os.O_RDWR)
2672 fd = os.open(util.nulldev, os.O_RDWR)
2663 if fd != 0: os.dup2(fd, 0)
2673 if fd != 0: os.dup2(fd, 0)
2664 if fd != 1: os.dup2(fd, 1)
2674 if fd != 1: os.dup2(fd, 1)
2665 if fd != 2: os.dup2(fd, 2)
2675 if fd != 2: os.dup2(fd, 2)
2666 if fd not in (0, 1, 2): os.close(fd)
2676 if fd not in (0, 1, 2): os.close(fd)
2667
2677
2668 httpd.serve_forever()
2678 httpd.serve_forever()
2669
2679
2670 def status(ui, repo, *pats, **opts):
2680 def status(ui, repo, *pats, **opts):
2671 """show changed files in the working directory
2681 """show changed files in the working directory
2672
2682
2673 Show changed files in the repository. If names are
2683 Show changed files in the repository. If names are
2674 given, only files that match are shown.
2684 given, only files that match are shown.
2675
2685
2676 The codes used to show the status of files are:
2686 The codes used to show the status of files are:
2677 M = modified
2687 M = modified
2678 A = added
2688 A = added
2679 R = removed
2689 R = removed
2680 ! = deleted, but still tracked
2690 ! = deleted, but still tracked
2681 ? = not tracked
2691 ? = not tracked
2682 I = ignored (not shown by default)
2692 I = ignored (not shown by default)
2683 """
2693 """
2684
2694
2685 show_ignored = opts['ignored'] and True or False
2695 show_ignored = opts['ignored'] and True or False
2686 files, matchfn, anypats = matchpats(repo, pats, opts)
2696 files, matchfn, anypats = matchpats(repo, pats, opts)
2687 cwd = (pats and repo.getcwd()) or ''
2697 cwd = (pats and repo.getcwd()) or ''
2688 modified, added, removed, deleted, unknown, ignored = [
2698 modified, added, removed, deleted, unknown, ignored = [
2689 [util.pathto(cwd, x) for x in n]
2699 [util.pathto(cwd, x) for x in n]
2690 for n in repo.changes(files=files, match=matchfn,
2700 for n in repo.changes(files=files, match=matchfn,
2691 show_ignored=show_ignored)]
2701 show_ignored=show_ignored)]
2692
2702
2693 changetypes = [('modified', 'M', modified),
2703 changetypes = [('modified', 'M', modified),
2694 ('added', 'A', added),
2704 ('added', 'A', added),
2695 ('removed', 'R', removed),
2705 ('removed', 'R', removed),
2696 ('deleted', '!', deleted),
2706 ('deleted', '!', deleted),
2697 ('unknown', '?', unknown),
2707 ('unknown', '?', unknown),
2698 ('ignored', 'I', ignored)]
2708 ('ignored', 'I', ignored)]
2699
2709
2700 end = opts['print0'] and '\0' or '\n'
2710 end = opts['print0'] and '\0' or '\n'
2701
2711
2702 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2712 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2703 or changetypes):
2713 or changetypes):
2704 if opts['no_status']:
2714 if opts['no_status']:
2705 format = "%%s%s" % end
2715 format = "%%s%s" % end
2706 else:
2716 else:
2707 format = "%s %%s%s" % (char, end)
2717 format = "%s %%s%s" % (char, end)
2708
2718
2709 for f in changes:
2719 for f in changes:
2710 ui.write(format % f)
2720 ui.write(format % f)
2711
2721
2712 def tag(ui, repo, name, rev_=None, **opts):
2722 def tag(ui, repo, name, rev_=None, **opts):
2713 """add a tag for the current tip or a given revision
2723 """add a tag for the current tip or a given revision
2714
2724
2715 Name a particular revision using <name>.
2725 Name a particular revision using <name>.
2716
2726
2717 Tags are used to name particular revisions of the repository and are
2727 Tags are used to name particular revisions of the repository and are
2718 very useful to compare different revision, to go back to significant
2728 very useful to compare different revision, to go back to significant
2719 earlier versions or to mark branch points as releases, etc.
2729 earlier versions or to mark branch points as releases, etc.
2720
2730
2721 If no revision is given, the tip is used.
2731 If no revision is given, the tip is used.
2722
2732
2723 To facilitate version control, distribution, and merging of tags,
2733 To facilitate version control, distribution, and merging of tags,
2724 they are stored as a file named ".hgtags" which is managed
2734 they are stored as a file named ".hgtags" which is managed
2725 similarly to other project files and can be hand-edited if
2735 similarly to other project files and can be hand-edited if
2726 necessary. The file '.hg/localtags' is used for local tags (not
2736 necessary. The file '.hg/localtags' is used for local tags (not
2727 shared among repositories).
2737 shared among repositories).
2728 """
2738 """
2729 if name == "tip":
2739 if name == "tip":
2730 raise util.Abort(_("the name 'tip' is reserved"))
2740 raise util.Abort(_("the name 'tip' is reserved"))
2731 if rev_ is not None:
2741 if rev_ is not None:
2732 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2742 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2733 "please use 'hg tag [-r REV] NAME' instead\n"))
2743 "please use 'hg tag [-r REV] NAME' instead\n"))
2734 if opts['rev']:
2744 if opts['rev']:
2735 raise util.Abort(_("use only one form to specify the revision"))
2745 raise util.Abort(_("use only one form to specify the revision"))
2736 if opts['rev']:
2746 if opts['rev']:
2737 rev_ = opts['rev']
2747 rev_ = opts['rev']
2738 if rev_:
2748 if rev_:
2739 r = hex(repo.lookup(rev_))
2749 r = hex(repo.lookup(rev_))
2740 else:
2750 else:
2741 r = hex(repo.changelog.tip())
2751 r = hex(repo.changelog.tip())
2742
2752
2743 disallowed = (revrangesep, '\r', '\n')
2753 disallowed = (revrangesep, '\r', '\n')
2744 for c in disallowed:
2754 for c in disallowed:
2745 if c in name:
2755 if c in name:
2746 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2756 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2747
2757
2748 repo.hook('pretag', throw=True, node=r, tag=name,
2758 repo.hook('pretag', throw=True, node=r, tag=name,
2749 local=int(not not opts['local']))
2759 local=int(not not opts['local']))
2750
2760
2751 if opts['local']:
2761 if opts['local']:
2752 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2762 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2753 repo.hook('tag', node=r, tag=name, local=1)
2763 repo.hook('tag', node=r, tag=name, local=1)
2754 return
2764 return
2755
2765
2756 for x in repo.changes():
2766 for x in repo.changes():
2757 if ".hgtags" in x:
2767 if ".hgtags" in x:
2758 raise util.Abort(_("working copy of .hgtags is changed "
2768 raise util.Abort(_("working copy of .hgtags is changed "
2759 "(please commit .hgtags manually)"))
2769 "(please commit .hgtags manually)"))
2760
2770
2761 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2771 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2762 if repo.dirstate.state(".hgtags") == '?':
2772 if repo.dirstate.state(".hgtags") == '?':
2763 repo.add([".hgtags"])
2773 repo.add([".hgtags"])
2764
2774
2765 message = (opts['message'] or
2775 message = (opts['message'] or
2766 _("Added tag %s for changeset %s") % (name, r))
2776 _("Added tag %s for changeset %s") % (name, r))
2767 try:
2777 try:
2768 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2778 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2769 repo.hook('tag', node=r, tag=name, local=0)
2779 repo.hook('tag', node=r, tag=name, local=0)
2770 except ValueError, inst:
2780 except ValueError, inst:
2771 raise util.Abort(str(inst))
2781 raise util.Abort(str(inst))
2772
2782
2773 def tags(ui, repo):
2783 def tags(ui, repo):
2774 """list repository tags
2784 """list repository tags
2775
2785
2776 List the repository tags.
2786 List the repository tags.
2777
2787
2778 This lists both regular and local tags.
2788 This lists both regular and local tags.
2779 """
2789 """
2780
2790
2781 l = repo.tagslist()
2791 l = repo.tagslist()
2782 l.reverse()
2792 l.reverse()
2783 for t, n in l:
2793 for t, n in l:
2784 try:
2794 try:
2785 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2795 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2786 except KeyError:
2796 except KeyError:
2787 r = " ?:?"
2797 r = " ?:?"
2788 if ui.quiet:
2798 if ui.quiet:
2789 ui.write("%s\n" % t)
2799 ui.write("%s\n" % t)
2790 else:
2800 else:
2791 ui.write("%-30s %s\n" % (t, r))
2801 ui.write("%-30s %s\n" % (t, r))
2792
2802
2793 def tip(ui, repo, **opts):
2803 def tip(ui, repo, **opts):
2794 """show the tip revision
2804 """show the tip revision
2795
2805
2796 Show the tip revision.
2806 Show the tip revision.
2797 """
2807 """
2798 n = repo.changelog.tip()
2808 n = repo.changelog.tip()
2799 br = None
2809 br = None
2800 if opts['branches']:
2810 if opts['branches']:
2801 br = repo.branchlookup([n])
2811 br = repo.branchlookup([n])
2802 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2812 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2803 if opts['patch']:
2813 if opts['patch']:
2804 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2814 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2805
2815
2806 def unbundle(ui, repo, fname, **opts):
2816 def unbundle(ui, repo, fname, **opts):
2807 """apply a changegroup file
2817 """apply a changegroup file
2808
2818
2809 Apply a compressed changegroup file generated by the bundle
2819 Apply a compressed changegroup file generated by the bundle
2810 command.
2820 command.
2811 """
2821 """
2812 f = urllib.urlopen(fname)
2822 f = urllib.urlopen(fname)
2813
2823
2814 header = f.read(6)
2824 header = f.read(6)
2815 if not header.startswith("HG"):
2825 if not header.startswith("HG"):
2816 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2826 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2817 elif not header.startswith("HG10"):
2827 elif not header.startswith("HG10"):
2818 raise util.Abort(_("%s: unknown bundle version") % fname)
2828 raise util.Abort(_("%s: unknown bundle version") % fname)
2819 elif header == "HG10BZ":
2829 elif header == "HG10BZ":
2820 def generator(f):
2830 def generator(f):
2821 zd = bz2.BZ2Decompressor()
2831 zd = bz2.BZ2Decompressor()
2822 zd.decompress("BZ")
2832 zd.decompress("BZ")
2823 for chunk in f:
2833 for chunk in f:
2824 yield zd.decompress(chunk)
2834 yield zd.decompress(chunk)
2825 elif header == "HG10UN":
2835 elif header == "HG10UN":
2826 def generator(f):
2836 def generator(f):
2827 for chunk in f:
2837 for chunk in f:
2828 yield chunk
2838 yield chunk
2829 else:
2839 else:
2830 raise util.Abort(_("%s: unknown bundle compression type")
2840 raise util.Abort(_("%s: unknown bundle compression type")
2831 % fname)
2841 % fname)
2832 gen = generator(util.filechunkiter(f, 4096))
2842 gen = generator(util.filechunkiter(f, 4096))
2833 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle')
2843 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle')
2834 return postincoming(ui, repo, modheads, opts['update'])
2844 return postincoming(ui, repo, modheads, opts['update'])
2835
2845
2836 def undo(ui, repo):
2846 def undo(ui, repo):
2837 """undo the last commit or pull (DEPRECATED)
2847 """undo the last commit or pull (DEPRECATED)
2838
2848
2839 (DEPRECATED)
2849 (DEPRECATED)
2840 This command is now deprecated and will be removed in a future
2850 This command is now deprecated and will be removed in a future
2841 release. Please use the rollback command instead. For usage
2851 release. Please use the rollback command instead. For usage
2842 instructions, see the rollback command.
2852 instructions, see the rollback command.
2843 """
2853 """
2844 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2854 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2845 repo.rollback()
2855 repo.rollback()
2846
2856
2847 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2857 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2848 branch=None, **opts):
2858 branch=None, **opts):
2849 """update or merge working directory
2859 """update or merge working directory
2850
2860
2851 Update the working directory to the specified revision.
2861 Update the working directory to the specified revision.
2852
2862
2853 If there are no outstanding changes in the working directory and
2863 If there are no outstanding changes in the working directory and
2854 there is a linear relationship between the current version and the
2864 there is a linear relationship between the current version and the
2855 requested version, the result is the requested version.
2865 requested version, the result is the requested version.
2856
2866
2857 To merge the working directory with another revision, use the
2867 To merge the working directory with another revision, use the
2858 merge command.
2868 merge command.
2859
2869
2860 By default, update will refuse to run if doing so would require
2870 By default, update will refuse to run if doing so would require
2861 merging or discarding local changes.
2871 merging or discarding local changes.
2862 """
2872 """
2863 if merge:
2873 if merge:
2864 ui.warn(_('(the -m/--merge option is deprecated; '
2874 ui.warn(_('(the -m/--merge option is deprecated; '
2865 'use the merge command instead)\n'))
2875 'use the merge command instead)\n'))
2866 return doupdate(ui, repo, node, merge, clean, force, branch, **opts)
2876 return doupdate(ui, repo, node, merge, clean, force, branch, **opts)
2867
2877
2868 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None,
2878 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None,
2869 branch=None, **opts):
2879 branch=None, **opts):
2870 if branch:
2880 if branch:
2871 br = repo.branchlookup(branch=branch)
2881 br = repo.branchlookup(branch=branch)
2872 found = []
2882 found = []
2873 for x in br:
2883 for x in br:
2874 if branch in br[x]:
2884 if branch in br[x]:
2875 found.append(x)
2885 found.append(x)
2876 if len(found) > 1:
2886 if len(found) > 1:
2877 ui.warn(_("Found multiple heads for %s\n") % branch)
2887 ui.warn(_("Found multiple heads for %s\n") % branch)
2878 for x in found:
2888 for x in found:
2879 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2889 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2880 return 1
2890 return 1
2881 if len(found) == 1:
2891 if len(found) == 1:
2882 node = found[0]
2892 node = found[0]
2883 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2893 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2884 else:
2894 else:
2885 ui.warn(_("branch %s not found\n") % (branch))
2895 ui.warn(_("branch %s not found\n") % (branch))
2886 return 1
2896 return 1
2887 else:
2897 else:
2888 node = node and repo.lookup(node) or repo.changelog.tip()
2898 node = node and repo.lookup(node) or repo.changelog.tip()
2889 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2899 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2890
2900
2891 def verify(ui, repo):
2901 def verify(ui, repo):
2892 """verify the integrity of the repository
2902 """verify the integrity of the repository
2893
2903
2894 Verify the integrity of the current repository.
2904 Verify the integrity of the current repository.
2895
2905
2896 This will perform an extensive check of the repository's
2906 This will perform an extensive check of the repository's
2897 integrity, validating the hashes and checksums of each entry in
2907 integrity, validating the hashes and checksums of each entry in
2898 the changelog, manifest, and tracked files, as well as the
2908 the changelog, manifest, and tracked files, as well as the
2899 integrity of their crosslinks and indices.
2909 integrity of their crosslinks and indices.
2900 """
2910 """
2901 return repo.verify()
2911 return repo.verify()
2902
2912
2903 # Command options and aliases are listed here, alphabetically
2913 # Command options and aliases are listed here, alphabetically
2904
2914
2905 table = {
2915 table = {
2906 "^add":
2916 "^add":
2907 (add,
2917 (add,
2908 [('I', 'include', [], _('include names matching the given patterns')),
2918 [('I', 'include', [], _('include names matching the given patterns')),
2909 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2919 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2910 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2920 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2911 _('hg add [OPTION]... [FILE]...')),
2921 _('hg add [OPTION]... [FILE]...')),
2912 "debugaddremove|addremove":
2922 "debugaddremove|addremove":
2913 (addremove,
2923 (addremove,
2914 [('I', 'include', [], _('include names matching the given patterns')),
2924 [('I', 'include', [], _('include names matching the given patterns')),
2915 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2925 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2916 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2926 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2917 _('hg addremove [OPTION]... [FILE]...')),
2927 _('hg addremove [OPTION]... [FILE]...')),
2918 "^annotate":
2928 "^annotate":
2919 (annotate,
2929 (annotate,
2920 [('r', 'rev', '', _('annotate the specified revision')),
2930 [('r', 'rev', '', _('annotate the specified revision')),
2921 ('a', 'text', None, _('treat all files as text')),
2931 ('a', 'text', None, _('treat all files as text')),
2922 ('u', 'user', None, _('list the author')),
2932 ('u', 'user', None, _('list the author')),
2923 ('d', 'date', None, _('list the date')),
2933 ('d', 'date', None, _('list the date')),
2924 ('n', 'number', None, _('list the revision number (default)')),
2934 ('n', 'number', None, _('list the revision number (default)')),
2925 ('c', 'changeset', None, _('list the changeset')),
2935 ('c', 'changeset', None, _('list the changeset')),
2926 ('I', 'include', [], _('include names matching the given patterns')),
2936 ('I', 'include', [], _('include names matching the given patterns')),
2927 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2937 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2928 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2938 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2929 "archive":
2939 "archive":
2930 (archive,
2940 (archive,
2931 [('', 'no-decode', None, _('do not pass files through decoders')),
2941 [('', 'no-decode', None, _('do not pass files through decoders')),
2932 ('p', 'prefix', '', _('directory prefix for files in archive')),
2942 ('p', 'prefix', '', _('directory prefix for files in archive')),
2933 ('r', 'rev', '', _('revision to distribute')),
2943 ('r', 'rev', '', _('revision to distribute')),
2934 ('t', 'type', '', _('type of distribution to create')),
2944 ('t', 'type', '', _('type of distribution to create')),
2935 ('I', 'include', [], _('include names matching the given patterns')),
2945 ('I', 'include', [], _('include names matching the given patterns')),
2936 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2946 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2937 _('hg archive [OPTION]... DEST')),
2947 _('hg archive [OPTION]... DEST')),
2938 "backout":
2948 "backout":
2939 (backout,
2949 (backout,
2940 [('', 'merge', None,
2950 [('', 'merge', None,
2941 _('merge with old dirstate parent after backout')),
2951 _('merge with old dirstate parent after backout')),
2942 ('m', 'message', '', _('use <text> as commit message')),
2952 ('m', 'message', '', _('use <text> as commit message')),
2943 ('l', 'logfile', '', _('read commit message from <file>')),
2953 ('l', 'logfile', '', _('read commit message from <file>')),
2944 ('d', 'date', '', _('record datecode as commit date')),
2954 ('d', 'date', '', _('record datecode as commit date')),
2945 ('u', 'user', '', _('record user as committer')),
2955 ('u', 'user', '', _('record user as committer')),
2946 ('I', 'include', [], _('include names matching the given patterns')),
2956 ('I', 'include', [], _('include names matching the given patterns')),
2947 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2957 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2948 _('hg backout [OPTION]... REV')),
2958 _('hg backout [OPTION]... REV')),
2949 "bundle":
2959 "bundle":
2950 (bundle,
2960 (bundle,
2951 [('f', 'force', None,
2961 [('f', 'force', None,
2952 _('run even when remote repository is unrelated'))],
2962 _('run even when remote repository is unrelated'))],
2953 _('hg bundle FILE DEST')),
2963 _('hg bundle FILE DEST')),
2954 "cat":
2964 "cat":
2955 (cat,
2965 (cat,
2956 [('o', 'output', '', _('print output to file with formatted name')),
2966 [('o', 'output', '', _('print output to file with formatted name')),
2957 ('r', 'rev', '', _('print the given revision')),
2967 ('r', 'rev', '', _('print the given revision')),
2958 ('I', 'include', [], _('include names matching the given patterns')),
2968 ('I', 'include', [], _('include names matching the given patterns')),
2959 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2969 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2960 _('hg cat [OPTION]... FILE...')),
2970 _('hg cat [OPTION]... FILE...')),
2961 "^clone":
2971 "^clone":
2962 (clone,
2972 (clone,
2963 [('U', 'noupdate', None, _('do not update the new working directory')),
2973 [('U', 'noupdate', None, _('do not update the new working directory')),
2964 ('r', 'rev', [],
2974 ('r', 'rev', [],
2965 _('a changeset you would like to have after cloning')),
2975 _('a changeset you would like to have after cloning')),
2966 ('', 'pull', None, _('use pull protocol to copy metadata')),
2976 ('', 'pull', None, _('use pull protocol to copy metadata')),
2967 ('e', 'ssh', '', _('specify ssh command to use')),
2977 ('e', 'ssh', '', _('specify ssh command to use')),
2968 ('', 'remotecmd', '',
2978 ('', 'remotecmd', '',
2969 _('specify hg command to run on the remote side'))],
2979 _('specify hg command to run on the remote side'))],
2970 _('hg clone [OPTION]... SOURCE [DEST]')),
2980 _('hg clone [OPTION]... SOURCE [DEST]')),
2971 "^commit|ci":
2981 "^commit|ci":
2972 (commit,
2982 (commit,
2973 [('A', 'addremove', None,
2983 [('A', 'addremove', None,
2974 _('mark new/missing files as added/removed before committing')),
2984 _('mark new/missing files as added/removed before committing')),
2975 ('m', 'message', '', _('use <text> as commit message')),
2985 ('m', 'message', '', _('use <text> as commit message')),
2976 ('l', 'logfile', '', _('read the commit message from <file>')),
2986 ('l', 'logfile', '', _('read the commit message from <file>')),
2977 ('d', 'date', '', _('record datecode as commit date')),
2987 ('d', 'date', '', _('record datecode as commit date')),
2978 ('u', 'user', '', _('record user as commiter')),
2988 ('u', 'user', '', _('record user as commiter')),
2979 ('I', 'include', [], _('include names matching the given patterns')),
2989 ('I', 'include', [], _('include names matching the given patterns')),
2980 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2990 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2981 _('hg commit [OPTION]... [FILE]...')),
2991 _('hg commit [OPTION]... [FILE]...')),
2982 "copy|cp":
2992 "copy|cp":
2983 (copy,
2993 (copy,
2984 [('A', 'after', None, _('record a copy that has already occurred')),
2994 [('A', 'after', None, _('record a copy that has already occurred')),
2985 ('f', 'force', None,
2995 ('f', 'force', None,
2986 _('forcibly copy over an existing managed file')),
2996 _('forcibly copy over an existing managed file')),
2987 ('I', 'include', [], _('include names matching the given patterns')),
2997 ('I', 'include', [], _('include names matching the given patterns')),
2988 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2998 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2989 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2999 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2990 _('hg copy [OPTION]... [SOURCE]... DEST')),
3000 _('hg copy [OPTION]... [SOURCE]... DEST')),
2991 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
3001 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2992 "debugcomplete":
3002 "debugcomplete":
2993 (debugcomplete,
3003 (debugcomplete,
2994 [('o', 'options', None, _('show the command options'))],
3004 [('o', 'options', None, _('show the command options'))],
2995 _('debugcomplete [-o] CMD')),
3005 _('debugcomplete [-o] CMD')),
2996 "debugrebuildstate":
3006 "debugrebuildstate":
2997 (debugrebuildstate,
3007 (debugrebuildstate,
2998 [('r', 'rev', '', _('revision to rebuild to'))],
3008 [('r', 'rev', '', _('revision to rebuild to'))],
2999 _('debugrebuildstate [-r REV] [REV]')),
3009 _('debugrebuildstate [-r REV] [REV]')),
3000 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
3010 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
3001 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
3011 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
3002 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
3012 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
3003 "debugstate": (debugstate, [], _('debugstate')),
3013 "debugstate": (debugstate, [], _('debugstate')),
3004 "debugdata": (debugdata, [], _('debugdata FILE REV')),
3014 "debugdata": (debugdata, [], _('debugdata FILE REV')),
3005 "debugindex": (debugindex, [], _('debugindex FILE')),
3015 "debugindex": (debugindex, [], _('debugindex FILE')),
3006 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
3016 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
3007 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
3017 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
3008 "debugwalk":
3018 "debugwalk":
3009 (debugwalk,
3019 (debugwalk,
3010 [('I', 'include', [], _('include names matching the given patterns')),
3020 [('I', 'include', [], _('include names matching the given patterns')),
3011 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3021 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3012 _('debugwalk [OPTION]... [FILE]...')),
3022 _('debugwalk [OPTION]... [FILE]...')),
3013 "^diff":
3023 "^diff":
3014 (diff,
3024 (diff,
3015 [('r', 'rev', [], _('revision')),
3025 [('r', 'rev', [], _('revision')),
3016 ('a', 'text', None, _('treat all files as text')),
3026 ('a', 'text', None, _('treat all files as text')),
3017 ('p', 'show-function', None,
3027 ('p', 'show-function', None,
3018 _('show which function each change is in')),
3028 _('show which function each change is in')),
3019 ('w', 'ignore-all-space', None,
3029 ('w', 'ignore-all-space', None,
3020 _('ignore white space when comparing lines')),
3030 _('ignore white space when comparing lines')),
3031 ('b', 'ignore-space-change', None,
3032 _('ignore changes in the amount of white space')),
3033 ('B', 'ignore-blank-lines', None,
3034 _('ignore changes whose lines are all blank')),
3021 ('I', 'include', [], _('include names matching the given patterns')),
3035 ('I', 'include', [], _('include names matching the given patterns')),
3022 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3036 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3023 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
3037 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
3024 "^export":
3038 "^export":
3025 (export,
3039 (export,
3026 [('o', 'output', '', _('print output to file with formatted name')),
3040 [('o', 'output', '', _('print output to file with formatted name')),
3027 ('a', 'text', None, _('treat all files as text')),
3041 ('a', 'text', None, _('treat all files as text')),
3028 ('', 'switch-parent', None, _('diff against the second parent'))],
3042 ('', 'switch-parent', None, _('diff against the second parent'))],
3029 _('hg export [-a] [-o OUTFILESPEC] REV...')),
3043 _('hg export [-a] [-o OUTFILESPEC] REV...')),
3030 "debugforget|forget":
3044 "debugforget|forget":
3031 (forget,
3045 (forget,
3032 [('I', 'include', [], _('include names matching the given patterns')),
3046 [('I', 'include', [], _('include names matching the given patterns')),
3033 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3047 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3034 _('hg forget [OPTION]... FILE...')),
3048 _('hg forget [OPTION]... FILE...')),
3035 "grep":
3049 "grep":
3036 (grep,
3050 (grep,
3037 [('0', 'print0', None, _('end fields with NUL')),
3051 [('0', 'print0', None, _('end fields with NUL')),
3038 ('', 'all', None, _('print all revisions that match')),
3052 ('', 'all', None, _('print all revisions that match')),
3039 ('i', 'ignore-case', None, _('ignore case when matching')),
3053 ('i', 'ignore-case', None, _('ignore case when matching')),
3040 ('l', 'files-with-matches', None,
3054 ('l', 'files-with-matches', None,
3041 _('print only filenames and revs that match')),
3055 _('print only filenames and revs that match')),
3042 ('n', 'line-number', None, _('print matching line numbers')),
3056 ('n', 'line-number', None, _('print matching line numbers')),
3043 ('r', 'rev', [], _('search in given revision range')),
3057 ('r', 'rev', [], _('search in given revision range')),
3044 ('u', 'user', None, _('print user who committed change')),
3058 ('u', 'user', None, _('print user who committed change')),
3045 ('I', 'include', [], _('include names matching the given patterns')),
3059 ('I', 'include', [], _('include names matching the given patterns')),
3046 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3060 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3047 _('hg grep [OPTION]... PATTERN [FILE]...')),
3061 _('hg grep [OPTION]... PATTERN [FILE]...')),
3048 "heads":
3062 "heads":
3049 (heads,
3063 (heads,
3050 [('b', 'branches', None, _('show branches')),
3064 [('b', 'branches', None, _('show branches')),
3051 ('', 'style', '', _('display using template map file')),
3065 ('', 'style', '', _('display using template map file')),
3052 ('r', 'rev', '', _('show only heads which are descendants of rev')),
3066 ('r', 'rev', '', _('show only heads which are descendants of rev')),
3053 ('', 'template', '', _('display with template'))],
3067 ('', 'template', '', _('display with template'))],
3054 _('hg heads [-b] [-r <rev>]')),
3068 _('hg heads [-b] [-r <rev>]')),
3055 "help": (help_, [], _('hg help [COMMAND]')),
3069 "help": (help_, [], _('hg help [COMMAND]')),
3056 "identify|id": (identify, [], _('hg identify')),
3070 "identify|id": (identify, [], _('hg identify')),
3057 "import|patch":
3071 "import|patch":
3058 (import_,
3072 (import_,
3059 [('p', 'strip', 1,
3073 [('p', 'strip', 1,
3060 _('directory strip option for patch. This has the same\n'
3074 _('directory strip option for patch. This has the same\n'
3061 'meaning as the corresponding patch option')),
3075 'meaning as the corresponding patch option')),
3062 ('m', 'message', '', _('use <text> as commit message')),
3076 ('m', 'message', '', _('use <text> as commit message')),
3063 ('b', 'base', '', _('base path')),
3077 ('b', 'base', '', _('base path')),
3064 ('f', 'force', None,
3078 ('f', 'force', None,
3065 _('skip check for outstanding uncommitted changes'))],
3079 _('skip check for outstanding uncommitted changes'))],
3066 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
3080 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
3067 "incoming|in": (incoming,
3081 "incoming|in": (incoming,
3068 [('M', 'no-merges', None, _('do not show merges')),
3082 [('M', 'no-merges', None, _('do not show merges')),
3069 ('f', 'force', None,
3083 ('f', 'force', None,
3070 _('run even when remote repository is unrelated')),
3084 _('run even when remote repository is unrelated')),
3071 ('', 'style', '', _('display using template map file')),
3085 ('', 'style', '', _('display using template map file')),
3072 ('n', 'newest-first', None, _('show newest record first')),
3086 ('n', 'newest-first', None, _('show newest record first')),
3073 ('', 'bundle', '', _('file to store the bundles into')),
3087 ('', 'bundle', '', _('file to store the bundles into')),
3074 ('p', 'patch', None, _('show patch')),
3088 ('p', 'patch', None, _('show patch')),
3075 ('r', 'rev', [], _('a specific revision you would like to pull')),
3089 ('r', 'rev', [], _('a specific revision you would like to pull')),
3076 ('', 'template', '', _('display with template')),
3090 ('', 'template', '', _('display with template')),
3077 ('e', 'ssh', '', _('specify ssh command to use')),
3091 ('e', 'ssh', '', _('specify ssh command to use')),
3078 ('', 'remotecmd', '',
3092 ('', 'remotecmd', '',
3079 _('specify hg command to run on the remote side'))],
3093 _('specify hg command to run on the remote side'))],
3080 _('hg incoming [-p] [-n] [-M] [-r REV]...'
3094 _('hg incoming [-p] [-n] [-M] [-r REV]...'
3081 ' [--bundle FILENAME] [SOURCE]')),
3095 ' [--bundle FILENAME] [SOURCE]')),
3082 "^init": (init, [], _('hg init [DEST]')),
3096 "^init": (init, [], _('hg init [DEST]')),
3083 "locate":
3097 "locate":
3084 (locate,
3098 (locate,
3085 [('r', 'rev', '', _('search the repository as it stood at rev')),
3099 [('r', 'rev', '', _('search the repository as it stood at rev')),
3086 ('0', 'print0', None,
3100 ('0', 'print0', None,
3087 _('end filenames with NUL, for use with xargs')),
3101 _('end filenames with NUL, for use with xargs')),
3088 ('f', 'fullpath', None,
3102 ('f', 'fullpath', None,
3089 _('print complete paths from the filesystem root')),
3103 _('print complete paths from the filesystem root')),
3090 ('I', 'include', [], _('include names matching the given patterns')),
3104 ('I', 'include', [], _('include names matching the given patterns')),
3091 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3105 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3092 _('hg locate [OPTION]... [PATTERN]...')),
3106 _('hg locate [OPTION]... [PATTERN]...')),
3093 "^log|history":
3107 "^log|history":
3094 (log,
3108 (log,
3095 [('b', 'branches', None, _('show branches')),
3109 [('b', 'branches', None, _('show branches')),
3096 ('k', 'keyword', [], _('search for a keyword')),
3110 ('k', 'keyword', [], _('search for a keyword')),
3097 ('l', 'limit', '', _('limit number of changes displayed')),
3111 ('l', 'limit', '', _('limit number of changes displayed')),
3098 ('r', 'rev', [], _('show the specified revision or range')),
3112 ('r', 'rev', [], _('show the specified revision or range')),
3099 ('M', 'no-merges', None, _('do not show merges')),
3113 ('M', 'no-merges', None, _('do not show merges')),
3100 ('', 'style', '', _('display using template map file')),
3114 ('', 'style', '', _('display using template map file')),
3101 ('m', 'only-merges', None, _('show only merges')),
3115 ('m', 'only-merges', None, _('show only merges')),
3102 ('p', 'patch', None, _('show patch')),
3116 ('p', 'patch', None, _('show patch')),
3103 ('', 'template', '', _('display with template')),
3117 ('', 'template', '', _('display with template')),
3104 ('I', 'include', [], _('include names matching the given patterns')),
3118 ('I', 'include', [], _('include names matching the given patterns')),
3105 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3119 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3106 _('hg log [OPTION]... [FILE]')),
3120 _('hg log [OPTION]... [FILE]')),
3107 "manifest": (manifest, [], _('hg manifest [REV]')),
3121 "manifest": (manifest, [], _('hg manifest [REV]')),
3108 "merge":
3122 "merge":
3109 (merge,
3123 (merge,
3110 [('b', 'branch', '', _('merge with head of a specific branch')),
3124 [('b', 'branch', '', _('merge with head of a specific branch')),
3111 ('f', 'force', None, _('force a merge with outstanding changes'))],
3125 ('f', 'force', None, _('force a merge with outstanding changes'))],
3112 _('hg merge [-b TAG] [-f] [REV]')),
3126 _('hg merge [-b TAG] [-f] [REV]')),
3113 "outgoing|out": (outgoing,
3127 "outgoing|out": (outgoing,
3114 [('M', 'no-merges', None, _('do not show merges')),
3128 [('M', 'no-merges', None, _('do not show merges')),
3115 ('f', 'force', None,
3129 ('f', 'force', None,
3116 _('run even when remote repository is unrelated')),
3130 _('run even when remote repository is unrelated')),
3117 ('p', 'patch', None, _('show patch')),
3131 ('p', 'patch', None, _('show patch')),
3118 ('', 'style', '', _('display using template map file')),
3132 ('', 'style', '', _('display using template map file')),
3119 ('r', 'rev', [], _('a specific revision you would like to push')),
3133 ('r', 'rev', [], _('a specific revision you would like to push')),
3120 ('n', 'newest-first', None, _('show newest record first')),
3134 ('n', 'newest-first', None, _('show newest record first')),
3121 ('', 'template', '', _('display with template')),
3135 ('', 'template', '', _('display with template')),
3122 ('e', 'ssh', '', _('specify ssh command to use')),
3136 ('e', 'ssh', '', _('specify ssh command to use')),
3123 ('', 'remotecmd', '',
3137 ('', 'remotecmd', '',
3124 _('specify hg command to run on the remote side'))],
3138 _('specify hg command to run on the remote side'))],
3125 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3139 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3126 "^parents":
3140 "^parents":
3127 (parents,
3141 (parents,
3128 [('b', 'branches', None, _('show branches')),
3142 [('b', 'branches', None, _('show branches')),
3129 ('', 'style', '', _('display using template map file')),
3143 ('', 'style', '', _('display using template map file')),
3130 ('', 'template', '', _('display with template'))],
3144 ('', 'template', '', _('display with template'))],
3131 _('hg parents [-b] [REV]')),
3145 _('hg parents [-b] [REV]')),
3132 "paths": (paths, [], _('hg paths [NAME]')),
3146 "paths": (paths, [], _('hg paths [NAME]')),
3133 "^pull":
3147 "^pull":
3134 (pull,
3148 (pull,
3135 [('u', 'update', None,
3149 [('u', 'update', None,
3136 _('update the working directory to tip after pull')),
3150 _('update the working directory to tip after pull')),
3137 ('e', 'ssh', '', _('specify ssh command to use')),
3151 ('e', 'ssh', '', _('specify ssh command to use')),
3138 ('f', 'force', None,
3152 ('f', 'force', None,
3139 _('run even when remote repository is unrelated')),
3153 _('run even when remote repository is unrelated')),
3140 ('r', 'rev', [], _('a specific revision you would like to pull')),
3154 ('r', 'rev', [], _('a specific revision you would like to pull')),
3141 ('', 'remotecmd', '',
3155 ('', 'remotecmd', '',
3142 _('specify hg command to run on the remote side'))],
3156 _('specify hg command to run on the remote side'))],
3143 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
3157 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
3144 "^push":
3158 "^push":
3145 (push,
3159 (push,
3146 [('f', 'force', None, _('force push')),
3160 [('f', 'force', None, _('force push')),
3147 ('e', 'ssh', '', _('specify ssh command to use')),
3161 ('e', 'ssh', '', _('specify ssh command to use')),
3148 ('r', 'rev', [], _('a specific revision you would like to push')),
3162 ('r', 'rev', [], _('a specific revision you would like to push')),
3149 ('', 'remotecmd', '',
3163 ('', 'remotecmd', '',
3150 _('specify hg command to run on the remote side'))],
3164 _('specify hg command to run on the remote side'))],
3151 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
3165 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
3152 "debugrawcommit|rawcommit":
3166 "debugrawcommit|rawcommit":
3153 (rawcommit,
3167 (rawcommit,
3154 [('p', 'parent', [], _('parent')),
3168 [('p', 'parent', [], _('parent')),
3155 ('d', 'date', '', _('date code')),
3169 ('d', 'date', '', _('date code')),
3156 ('u', 'user', '', _('user')),
3170 ('u', 'user', '', _('user')),
3157 ('F', 'files', '', _('file list')),
3171 ('F', 'files', '', _('file list')),
3158 ('m', 'message', '', _('commit message')),
3172 ('m', 'message', '', _('commit message')),
3159 ('l', 'logfile', '', _('commit message file'))],
3173 ('l', 'logfile', '', _('commit message file'))],
3160 _('hg debugrawcommit [OPTION]... [FILE]...')),
3174 _('hg debugrawcommit [OPTION]... [FILE]...')),
3161 "recover": (recover, [], _('hg recover')),
3175 "recover": (recover, [], _('hg recover')),
3162 "^remove|rm":
3176 "^remove|rm":
3163 (remove,
3177 (remove,
3164 [('A', 'after', None, _('record remove that has already occurred')),
3178 [('A', 'after', None, _('record remove that has already occurred')),
3165 ('f', 'force', None, _('remove file even if modified')),
3179 ('f', 'force', None, _('remove file even if modified')),
3166 ('I', 'include', [], _('include names matching the given patterns')),
3180 ('I', 'include', [], _('include names matching the given patterns')),
3167 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3181 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3168 _('hg remove [OPTION]... FILE...')),
3182 _('hg remove [OPTION]... FILE...')),
3169 "rename|mv":
3183 "rename|mv":
3170 (rename,
3184 (rename,
3171 [('A', 'after', None, _('record a rename that has already occurred')),
3185 [('A', 'after', None, _('record a rename that has already occurred')),
3172 ('f', 'force', None,
3186 ('f', 'force', None,
3173 _('forcibly copy over an existing managed file')),
3187 _('forcibly copy over an existing managed file')),
3174 ('I', 'include', [], _('include names matching the given patterns')),
3188 ('I', 'include', [], _('include names matching the given patterns')),
3175 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3189 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3176 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3190 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3177 _('hg rename [OPTION]... SOURCE... DEST')),
3191 _('hg rename [OPTION]... SOURCE... DEST')),
3178 "^revert":
3192 "^revert":
3179 (revert,
3193 (revert,
3180 [('r', 'rev', '', _('revision to revert to')),
3194 [('r', 'rev', '', _('revision to revert to')),
3181 ('', 'no-backup', None, _('do not save backup copies of files')),
3195 ('', 'no-backup', None, _('do not save backup copies of files')),
3182 ('I', 'include', [], _('include names matching given patterns')),
3196 ('I', 'include', [], _('include names matching given patterns')),
3183 ('X', 'exclude', [], _('exclude names matching given patterns')),
3197 ('X', 'exclude', [], _('exclude names matching given patterns')),
3184 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3198 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3185 _('hg revert [-r REV] [NAME]...')),
3199 _('hg revert [-r REV] [NAME]...')),
3186 "rollback": (rollback, [], _('hg rollback')),
3200 "rollback": (rollback, [], _('hg rollback')),
3187 "root": (root, [], _('hg root')),
3201 "root": (root, [], _('hg root')),
3188 "^serve":
3202 "^serve":
3189 (serve,
3203 (serve,
3190 [('A', 'accesslog', '', _('name of access log file to write to')),
3204 [('A', 'accesslog', '', _('name of access log file to write to')),
3191 ('d', 'daemon', None, _('run server in background')),
3205 ('d', 'daemon', None, _('run server in background')),
3192 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3206 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3193 ('E', 'errorlog', '', _('name of error log file to write to')),
3207 ('E', 'errorlog', '', _('name of error log file to write to')),
3194 ('p', 'port', 0, _('port to use (default: 8000)')),
3208 ('p', 'port', 0, _('port to use (default: 8000)')),
3195 ('a', 'address', '', _('address to use')),
3209 ('a', 'address', '', _('address to use')),
3196 ('n', 'name', '',
3210 ('n', 'name', '',
3197 _('name to show in web pages (default: working dir)')),
3211 _('name to show in web pages (default: working dir)')),
3198 ('', 'webdir-conf', '', _('name of the webdir config file'
3212 ('', 'webdir-conf', '', _('name of the webdir config file'
3199 ' (serve more than one repo)')),
3213 ' (serve more than one repo)')),
3200 ('', 'pid-file', '', _('name of file to write process ID to')),
3214 ('', 'pid-file', '', _('name of file to write process ID to')),
3201 ('', 'stdio', None, _('for remote clients')),
3215 ('', 'stdio', None, _('for remote clients')),
3202 ('t', 'templates', '', _('web templates to use')),
3216 ('t', 'templates', '', _('web templates to use')),
3203 ('', 'style', '', _('template style to use')),
3217 ('', 'style', '', _('template style to use')),
3204 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3218 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3205 _('hg serve [OPTION]...')),
3219 _('hg serve [OPTION]...')),
3206 "^status|st":
3220 "^status|st":
3207 (status,
3221 (status,
3208 [('m', 'modified', None, _('show only modified files')),
3222 [('m', 'modified', None, _('show only modified files')),
3209 ('a', 'added', None, _('show only added files')),
3223 ('a', 'added', None, _('show only added files')),
3210 ('r', 'removed', None, _('show only removed files')),
3224 ('r', 'removed', None, _('show only removed files')),
3211 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3225 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3212 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3226 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3213 ('i', 'ignored', None, _('show ignored files')),
3227 ('i', 'ignored', None, _('show ignored files')),
3214 ('n', 'no-status', None, _('hide status prefix')),
3228 ('n', 'no-status', None, _('hide status prefix')),
3215 ('0', 'print0', None,
3229 ('0', 'print0', None,
3216 _('end filenames with NUL, for use with xargs')),
3230 _('end filenames with NUL, for use with xargs')),
3217 ('I', 'include', [], _('include names matching the given patterns')),
3231 ('I', 'include', [], _('include names matching the given patterns')),
3218 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3232 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3219 _('hg status [OPTION]... [FILE]...')),
3233 _('hg status [OPTION]... [FILE]...')),
3220 "tag":
3234 "tag":
3221 (tag,
3235 (tag,
3222 [('l', 'local', None, _('make the tag local')),
3236 [('l', 'local', None, _('make the tag local')),
3223 ('m', 'message', '', _('message for tag commit log entry')),
3237 ('m', 'message', '', _('message for tag commit log entry')),
3224 ('d', 'date', '', _('record datecode as commit date')),
3238 ('d', 'date', '', _('record datecode as commit date')),
3225 ('u', 'user', '', _('record user as commiter')),
3239 ('u', 'user', '', _('record user as commiter')),
3226 ('r', 'rev', '', _('revision to tag'))],
3240 ('r', 'rev', '', _('revision to tag'))],
3227 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3241 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3228 "tags": (tags, [], _('hg tags')),
3242 "tags": (tags, [], _('hg tags')),
3229 "tip":
3243 "tip":
3230 (tip,
3244 (tip,
3231 [('b', 'branches', None, _('show branches')),
3245 [('b', 'branches', None, _('show branches')),
3232 ('', 'style', '', _('display using template map file')),
3246 ('', 'style', '', _('display using template map file')),
3233 ('p', 'patch', None, _('show patch')),
3247 ('p', 'patch', None, _('show patch')),
3234 ('', 'template', '', _('display with template'))],
3248 ('', 'template', '', _('display with template'))],
3235 _('hg tip [-b] [-p]')),
3249 _('hg tip [-b] [-p]')),
3236 "unbundle":
3250 "unbundle":
3237 (unbundle,
3251 (unbundle,
3238 [('u', 'update', None,
3252 [('u', 'update', None,
3239 _('update the working directory to tip after unbundle'))],
3253 _('update the working directory to tip after unbundle'))],
3240 _('hg unbundle [-u] FILE')),
3254 _('hg unbundle [-u] FILE')),
3241 "debugundo|undo": (undo, [], _('hg undo')),
3255 "debugundo|undo": (undo, [], _('hg undo')),
3242 "^update|up|checkout|co":
3256 "^update|up|checkout|co":
3243 (update,
3257 (update,
3244 [('b', 'branch', '', _('checkout the head of a specific branch')),
3258 [('b', 'branch', '', _('checkout the head of a specific branch')),
3245 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3259 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3246 ('C', 'clean', None, _('overwrite locally modified files')),
3260 ('C', 'clean', None, _('overwrite locally modified files')),
3247 ('f', 'force', None, _('force a merge with outstanding changes'))],
3261 ('f', 'force', None, _('force a merge with outstanding changes'))],
3248 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3262 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3249 "verify": (verify, [], _('hg verify')),
3263 "verify": (verify, [], _('hg verify')),
3250 "version": (show_version, [], _('hg version')),
3264 "version": (show_version, [], _('hg version')),
3251 }
3265 }
3252
3266
3253 globalopts = [
3267 globalopts = [
3254 ('R', 'repository', '',
3268 ('R', 'repository', '',
3255 _('repository root directory or symbolic path name')),
3269 _('repository root directory or symbolic path name')),
3256 ('', 'cwd', '', _('change working directory')),
3270 ('', 'cwd', '', _('change working directory')),
3257 ('y', 'noninteractive', None,
3271 ('y', 'noninteractive', None,
3258 _('do not prompt, assume \'yes\' for any required answers')),
3272 _('do not prompt, assume \'yes\' for any required answers')),
3259 ('q', 'quiet', None, _('suppress output')),
3273 ('q', 'quiet', None, _('suppress output')),
3260 ('v', 'verbose', None, _('enable additional output')),
3274 ('v', 'verbose', None, _('enable additional output')),
3261 ('', 'config', [], _('set/override config option')),
3275 ('', 'config', [], _('set/override config option')),
3262 ('', 'debug', None, _('enable debugging output')),
3276 ('', 'debug', None, _('enable debugging output')),
3263 ('', 'debugger', None, _('start debugger')),
3277 ('', 'debugger', None, _('start debugger')),
3264 ('', 'lsprof', None, _('print improved command execution profile')),
3278 ('', 'lsprof', None, _('print improved command execution profile')),
3265 ('', 'traceback', None, _('print traceback on exception')),
3279 ('', 'traceback', None, _('print traceback on exception')),
3266 ('', 'time', None, _('time how long the command takes')),
3280 ('', 'time', None, _('time how long the command takes')),
3267 ('', 'profile', None, _('print command execution profile')),
3281 ('', 'profile', None, _('print command execution profile')),
3268 ('', 'version', None, _('output version information and exit')),
3282 ('', 'version', None, _('output version information and exit')),
3269 ('h', 'help', None, _('display help and exit')),
3283 ('h', 'help', None, _('display help and exit')),
3270 ]
3284 ]
3271
3285
3272 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3286 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3273 " debugindex debugindexdot")
3287 " debugindex debugindexdot")
3274 optionalrepo = ("paths serve debugconfig")
3288 optionalrepo = ("paths serve debugconfig")
3275
3289
3276 def findpossible(cmd):
3290 def findpossible(cmd):
3277 """
3291 """
3278 Return cmd -> (aliases, command table entry)
3292 Return cmd -> (aliases, command table entry)
3279 for each matching command.
3293 for each matching command.
3280 Return debug commands (or their aliases) only if no normal command matches.
3294 Return debug commands (or their aliases) only if no normal command matches.
3281 """
3295 """
3282 choice = {}
3296 choice = {}
3283 debugchoice = {}
3297 debugchoice = {}
3284 for e in table.keys():
3298 for e in table.keys():
3285 aliases = e.lstrip("^").split("|")
3299 aliases = e.lstrip("^").split("|")
3286 found = None
3300 found = None
3287 if cmd in aliases:
3301 if cmd in aliases:
3288 found = cmd
3302 found = cmd
3289 else:
3303 else:
3290 for a in aliases:
3304 for a in aliases:
3291 if a.startswith(cmd):
3305 if a.startswith(cmd):
3292 found = a
3306 found = a
3293 break
3307 break
3294 if found is not None:
3308 if found is not None:
3295 if aliases[0].startswith("debug"):
3309 if aliases[0].startswith("debug"):
3296 debugchoice[found] = (aliases, table[e])
3310 debugchoice[found] = (aliases, table[e])
3297 else:
3311 else:
3298 choice[found] = (aliases, table[e])
3312 choice[found] = (aliases, table[e])
3299
3313
3300 if not choice and debugchoice:
3314 if not choice and debugchoice:
3301 choice = debugchoice
3315 choice = debugchoice
3302
3316
3303 return choice
3317 return choice
3304
3318
3305 def findcmd(cmd):
3319 def findcmd(cmd):
3306 """Return (aliases, command table entry) for command string."""
3320 """Return (aliases, command table entry) for command string."""
3307 choice = findpossible(cmd)
3321 choice = findpossible(cmd)
3308
3322
3309 if choice.has_key(cmd):
3323 if choice.has_key(cmd):
3310 return choice[cmd]
3324 return choice[cmd]
3311
3325
3312 if len(choice) > 1:
3326 if len(choice) > 1:
3313 clist = choice.keys()
3327 clist = choice.keys()
3314 clist.sort()
3328 clist.sort()
3315 raise AmbiguousCommand(cmd, clist)
3329 raise AmbiguousCommand(cmd, clist)
3316
3330
3317 if choice:
3331 if choice:
3318 return choice.values()[0]
3332 return choice.values()[0]
3319
3333
3320 raise UnknownCommand(cmd)
3334 raise UnknownCommand(cmd)
3321
3335
3322 def catchterm(*args):
3336 def catchterm(*args):
3323 raise util.SignalInterrupt
3337 raise util.SignalInterrupt
3324
3338
3325 def run():
3339 def run():
3326 sys.exit(dispatch(sys.argv[1:]))
3340 sys.exit(dispatch(sys.argv[1:]))
3327
3341
3328 class ParseError(Exception):
3342 class ParseError(Exception):
3329 """Exception raised on errors in parsing the command line."""
3343 """Exception raised on errors in parsing the command line."""
3330
3344
3331 def parse(ui, args):
3345 def parse(ui, args):
3332 options = {}
3346 options = {}
3333 cmdoptions = {}
3347 cmdoptions = {}
3334
3348
3335 try:
3349 try:
3336 args = fancyopts.fancyopts(args, globalopts, options)
3350 args = fancyopts.fancyopts(args, globalopts, options)
3337 except fancyopts.getopt.GetoptError, inst:
3351 except fancyopts.getopt.GetoptError, inst:
3338 raise ParseError(None, inst)
3352 raise ParseError(None, inst)
3339
3353
3340 if args:
3354 if args:
3341 cmd, args = args[0], args[1:]
3355 cmd, args = args[0], args[1:]
3342 aliases, i = findcmd(cmd)
3356 aliases, i = findcmd(cmd)
3343 cmd = aliases[0]
3357 cmd = aliases[0]
3344 defaults = ui.config("defaults", cmd)
3358 defaults = ui.config("defaults", cmd)
3345 if defaults:
3359 if defaults:
3346 args = defaults.split() + args
3360 args = defaults.split() + args
3347 c = list(i[1])
3361 c = list(i[1])
3348 else:
3362 else:
3349 cmd = None
3363 cmd = None
3350 c = []
3364 c = []
3351
3365
3352 # combine global options into local
3366 # combine global options into local
3353 for o in globalopts:
3367 for o in globalopts:
3354 c.append((o[0], o[1], options[o[1]], o[3]))
3368 c.append((o[0], o[1], options[o[1]], o[3]))
3355
3369
3356 try:
3370 try:
3357 args = fancyopts.fancyopts(args, c, cmdoptions)
3371 args = fancyopts.fancyopts(args, c, cmdoptions)
3358 except fancyopts.getopt.GetoptError, inst:
3372 except fancyopts.getopt.GetoptError, inst:
3359 raise ParseError(cmd, inst)
3373 raise ParseError(cmd, inst)
3360
3374
3361 # separate global options back out
3375 # separate global options back out
3362 for o in globalopts:
3376 for o in globalopts:
3363 n = o[1]
3377 n = o[1]
3364 options[n] = cmdoptions[n]
3378 options[n] = cmdoptions[n]
3365 del cmdoptions[n]
3379 del cmdoptions[n]
3366
3380
3367 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3381 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3368
3382
3369 external = {}
3383 external = {}
3370
3384
3371 def findext(name):
3385 def findext(name):
3372 '''return module with given extension name'''
3386 '''return module with given extension name'''
3373 try:
3387 try:
3374 return sys.modules[external[name]]
3388 return sys.modules[external[name]]
3375 except KeyError:
3389 except KeyError:
3376 dotname = '.' + name
3390 dotname = '.' + name
3377 for k, v in external.iteritems():
3391 for k, v in external.iteritems():
3378 if k.endswith('.' + name) or v == name:
3392 if k.endswith('.' + name) or v == name:
3379 return sys.modules[v]
3393 return sys.modules[v]
3380 raise KeyError(name)
3394 raise KeyError(name)
3381
3395
3382 def dispatch(args):
3396 def dispatch(args):
3383 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3397 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3384 num = getattr(signal, name, None)
3398 num = getattr(signal, name, None)
3385 if num: signal.signal(num, catchterm)
3399 if num: signal.signal(num, catchterm)
3386
3400
3387 try:
3401 try:
3388 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3402 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3389 except util.Abort, inst:
3403 except util.Abort, inst:
3390 sys.stderr.write(_("abort: %s\n") % inst)
3404 sys.stderr.write(_("abort: %s\n") % inst)
3391 return -1
3405 return -1
3392
3406
3393 for ext_name, load_from_name in u.extensions():
3407 for ext_name, load_from_name in u.extensions():
3394 try:
3408 try:
3395 if load_from_name:
3409 if load_from_name:
3396 # the module will be loaded in sys.modules
3410 # the module will be loaded in sys.modules
3397 # choose an unique name so that it doesn't
3411 # choose an unique name so that it doesn't
3398 # conflicts with other modules
3412 # conflicts with other modules
3399 module_name = "hgext_%s" % ext_name.replace('.', '_')
3413 module_name = "hgext_%s" % ext_name.replace('.', '_')
3400 mod = imp.load_source(module_name, load_from_name)
3414 mod = imp.load_source(module_name, load_from_name)
3401 else:
3415 else:
3402 def importh(name):
3416 def importh(name):
3403 mod = __import__(name)
3417 mod = __import__(name)
3404 components = name.split('.')
3418 components = name.split('.')
3405 for comp in components[1:]:
3419 for comp in components[1:]:
3406 mod = getattr(mod, comp)
3420 mod = getattr(mod, comp)
3407 return mod
3421 return mod
3408 try:
3422 try:
3409 mod = importh("hgext.%s" % ext_name)
3423 mod = importh("hgext.%s" % ext_name)
3410 except ImportError:
3424 except ImportError:
3411 mod = importh(ext_name)
3425 mod = importh(ext_name)
3412 external[ext_name] = mod.__name__
3426 external[ext_name] = mod.__name__
3413 except (util.SignalInterrupt, KeyboardInterrupt):
3427 except (util.SignalInterrupt, KeyboardInterrupt):
3414 raise
3428 raise
3415 except Exception, inst:
3429 except Exception, inst:
3416 u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst))
3430 u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst))
3417 if u.print_exc():
3431 if u.print_exc():
3418 return 1
3432 return 1
3419
3433
3420 for name in external.itervalues():
3434 for name in external.itervalues():
3421 mod = sys.modules[name]
3435 mod = sys.modules[name]
3422 uisetup = getattr(mod, 'uisetup', None)
3436 uisetup = getattr(mod, 'uisetup', None)
3423 if uisetup:
3437 if uisetup:
3424 uisetup(u)
3438 uisetup(u)
3425 cmdtable = getattr(mod, 'cmdtable', {})
3439 cmdtable = getattr(mod, 'cmdtable', {})
3426 for t in cmdtable:
3440 for t in cmdtable:
3427 if t in table:
3441 if t in table:
3428 u.warn(_("module %s overrides %s\n") % (name, t))
3442 u.warn(_("module %s overrides %s\n") % (name, t))
3429 table.update(cmdtable)
3443 table.update(cmdtable)
3430
3444
3431 try:
3445 try:
3432 cmd, func, args, options, cmdoptions = parse(u, args)
3446 cmd, func, args, options, cmdoptions = parse(u, args)
3433 if options["time"]:
3447 if options["time"]:
3434 def get_times():
3448 def get_times():
3435 t = os.times()
3449 t = os.times()
3436 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3450 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3437 t = (t[0], t[1], t[2], t[3], time.clock())
3451 t = (t[0], t[1], t[2], t[3], time.clock())
3438 return t
3452 return t
3439 s = get_times()
3453 s = get_times()
3440 def print_time():
3454 def print_time():
3441 t = get_times()
3455 t = get_times()
3442 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3456 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3443 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3457 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3444 atexit.register(print_time)
3458 atexit.register(print_time)
3445
3459
3446 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3460 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3447 not options["noninteractive"], options["traceback"],
3461 not options["noninteractive"], options["traceback"],
3448 options["config"])
3462 options["config"])
3449
3463
3450 # enter the debugger before command execution
3464 # enter the debugger before command execution
3451 if options['debugger']:
3465 if options['debugger']:
3452 pdb.set_trace()
3466 pdb.set_trace()
3453
3467
3454 try:
3468 try:
3455 if options['cwd']:
3469 if options['cwd']:
3456 try:
3470 try:
3457 os.chdir(options['cwd'])
3471 os.chdir(options['cwd'])
3458 except OSError, inst:
3472 except OSError, inst:
3459 raise util.Abort('%s: %s' %
3473 raise util.Abort('%s: %s' %
3460 (options['cwd'], inst.strerror))
3474 (options['cwd'], inst.strerror))
3461
3475
3462 path = u.expandpath(options["repository"]) or ""
3476 path = u.expandpath(options["repository"]) or ""
3463 repo = path and hg.repository(u, path=path) or None
3477 repo = path and hg.repository(u, path=path) or None
3464
3478
3465 if options['help']:
3479 if options['help']:
3466 return help_(u, cmd, options['version'])
3480 return help_(u, cmd, options['version'])
3467 elif options['version']:
3481 elif options['version']:
3468 return show_version(u)
3482 return show_version(u)
3469 elif not cmd:
3483 elif not cmd:
3470 return help_(u, 'shortlist')
3484 return help_(u, 'shortlist')
3471
3485
3472 if cmd not in norepo.split():
3486 if cmd not in norepo.split():
3473 try:
3487 try:
3474 if not repo:
3488 if not repo:
3475 repo = hg.repository(u, path=path)
3489 repo = hg.repository(u, path=path)
3476 u = repo.ui
3490 u = repo.ui
3477 for name in external.itervalues():
3491 for name in external.itervalues():
3478 mod = sys.modules[name]
3492 mod = sys.modules[name]
3479 if hasattr(mod, 'reposetup'):
3493 if hasattr(mod, 'reposetup'):
3480 mod.reposetup(u, repo)
3494 mod.reposetup(u, repo)
3481 except hg.RepoError:
3495 except hg.RepoError:
3482 if cmd not in optionalrepo.split():
3496 if cmd not in optionalrepo.split():
3483 raise
3497 raise
3484 d = lambda: func(u, repo, *args, **cmdoptions)
3498 d = lambda: func(u, repo, *args, **cmdoptions)
3485 else:
3499 else:
3486 d = lambda: func(u, *args, **cmdoptions)
3500 d = lambda: func(u, *args, **cmdoptions)
3487
3501
3488 try:
3502 try:
3489 if options['profile']:
3503 if options['profile']:
3490 import hotshot, hotshot.stats
3504 import hotshot, hotshot.stats
3491 prof = hotshot.Profile("hg.prof")
3505 prof = hotshot.Profile("hg.prof")
3492 try:
3506 try:
3493 try:
3507 try:
3494 return prof.runcall(d)
3508 return prof.runcall(d)
3495 except:
3509 except:
3496 try:
3510 try:
3497 u.warn(_('exception raised - generating '
3511 u.warn(_('exception raised - generating '
3498 'profile anyway\n'))
3512 'profile anyway\n'))
3499 except:
3513 except:
3500 pass
3514 pass
3501 raise
3515 raise
3502 finally:
3516 finally:
3503 prof.close()
3517 prof.close()
3504 stats = hotshot.stats.load("hg.prof")
3518 stats = hotshot.stats.load("hg.prof")
3505 stats.strip_dirs()
3519 stats.strip_dirs()
3506 stats.sort_stats('time', 'calls')
3520 stats.sort_stats('time', 'calls')
3507 stats.print_stats(40)
3521 stats.print_stats(40)
3508 elif options['lsprof']:
3522 elif options['lsprof']:
3509 try:
3523 try:
3510 from mercurial import lsprof
3524 from mercurial import lsprof
3511 except ImportError:
3525 except ImportError:
3512 raise util.Abort(_(
3526 raise util.Abort(_(
3513 'lsprof not available - install from '
3527 'lsprof not available - install from '
3514 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3528 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3515 p = lsprof.Profiler()
3529 p = lsprof.Profiler()
3516 p.enable(subcalls=True)
3530 p.enable(subcalls=True)
3517 try:
3531 try:
3518 return d()
3532 return d()
3519 finally:
3533 finally:
3520 p.disable()
3534 p.disable()
3521 stats = lsprof.Stats(p.getstats())
3535 stats = lsprof.Stats(p.getstats())
3522 stats.sort()
3536 stats.sort()
3523 stats.pprint(top=10, file=sys.stderr, climit=5)
3537 stats.pprint(top=10, file=sys.stderr, climit=5)
3524 else:
3538 else:
3525 return d()
3539 return d()
3526 finally:
3540 finally:
3527 u.flush()
3541 u.flush()
3528 except:
3542 except:
3529 # enter the debugger when we hit an exception
3543 # enter the debugger when we hit an exception
3530 if options['debugger']:
3544 if options['debugger']:
3531 pdb.post_mortem(sys.exc_info()[2])
3545 pdb.post_mortem(sys.exc_info()[2])
3532 u.print_exc()
3546 u.print_exc()
3533 raise
3547 raise
3534 except ParseError, inst:
3548 except ParseError, inst:
3535 if inst.args[0]:
3549 if inst.args[0]:
3536 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3550 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3537 help_(u, inst.args[0])
3551 help_(u, inst.args[0])
3538 else:
3552 else:
3539 u.warn(_("hg: %s\n") % inst.args[1])
3553 u.warn(_("hg: %s\n") % inst.args[1])
3540 help_(u, 'shortlist')
3554 help_(u, 'shortlist')
3541 except AmbiguousCommand, inst:
3555 except AmbiguousCommand, inst:
3542 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3556 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3543 (inst.args[0], " ".join(inst.args[1])))
3557 (inst.args[0], " ".join(inst.args[1])))
3544 except UnknownCommand, inst:
3558 except UnknownCommand, inst:
3545 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3559 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3546 help_(u, 'shortlist')
3560 help_(u, 'shortlist')
3547 except hg.RepoError, inst:
3561 except hg.RepoError, inst:
3548 u.warn(_("abort: %s!\n") % inst)
3562 u.warn(_("abort: %s!\n") % inst)
3549 except lock.LockHeld, inst:
3563 except lock.LockHeld, inst:
3550 if inst.errno == errno.ETIMEDOUT:
3564 if inst.errno == errno.ETIMEDOUT:
3551 reason = _('timed out waiting for lock held by %s') % inst.locker
3565 reason = _('timed out waiting for lock held by %s') % inst.locker
3552 else:
3566 else:
3553 reason = _('lock held by %s') % inst.locker
3567 reason = _('lock held by %s') % inst.locker
3554 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3568 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3555 except lock.LockUnavailable, inst:
3569 except lock.LockUnavailable, inst:
3556 u.warn(_("abort: could not lock %s: %s\n") %
3570 u.warn(_("abort: could not lock %s: %s\n") %
3557 (inst.desc or inst.filename, inst.strerror))
3571 (inst.desc or inst.filename, inst.strerror))
3558 except revlog.RevlogError, inst:
3572 except revlog.RevlogError, inst:
3559 u.warn(_("abort: "), inst, "!\n")
3573 u.warn(_("abort: "), inst, "!\n")
3560 except util.SignalInterrupt:
3574 except util.SignalInterrupt:
3561 u.warn(_("killed!\n"))
3575 u.warn(_("killed!\n"))
3562 except KeyboardInterrupt:
3576 except KeyboardInterrupt:
3563 try:
3577 try:
3564 u.warn(_("interrupted!\n"))
3578 u.warn(_("interrupted!\n"))
3565 except IOError, inst:
3579 except IOError, inst:
3566 if inst.errno == errno.EPIPE:
3580 if inst.errno == errno.EPIPE:
3567 if u.debugflag:
3581 if u.debugflag:
3568 u.warn(_("\nbroken pipe\n"))
3582 u.warn(_("\nbroken pipe\n"))
3569 else:
3583 else:
3570 raise
3584 raise
3571 except IOError, inst:
3585 except IOError, inst:
3572 if hasattr(inst, "code"):
3586 if hasattr(inst, "code"):
3573 u.warn(_("abort: %s\n") % inst)
3587 u.warn(_("abort: %s\n") % inst)
3574 elif hasattr(inst, "reason"):
3588 elif hasattr(inst, "reason"):
3575 u.warn(_("abort: error: %s\n") % inst.reason[1])
3589 u.warn(_("abort: error: %s\n") % inst.reason[1])
3576 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3590 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3577 if u.debugflag:
3591 if u.debugflag:
3578 u.warn(_("broken pipe\n"))
3592 u.warn(_("broken pipe\n"))
3579 elif getattr(inst, "strerror", None):
3593 elif getattr(inst, "strerror", None):
3580 if getattr(inst, "filename", None):
3594 if getattr(inst, "filename", None):
3581 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3595 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3582 else:
3596 else:
3583 u.warn(_("abort: %s\n") % inst.strerror)
3597 u.warn(_("abort: %s\n") % inst.strerror)
3584 else:
3598 else:
3585 raise
3599 raise
3586 except OSError, inst:
3600 except OSError, inst:
3587 if hasattr(inst, "filename"):
3601 if hasattr(inst, "filename"):
3588 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3602 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3589 else:
3603 else:
3590 u.warn(_("abort: %s\n") % inst.strerror)
3604 u.warn(_("abort: %s\n") % inst.strerror)
3591 except util.Abort, inst:
3605 except util.Abort, inst:
3592 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3606 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3593 except TypeError, inst:
3607 except TypeError, inst:
3594 # was this an argument error?
3608 # was this an argument error?
3595 tb = traceback.extract_tb(sys.exc_info()[2])
3609 tb = traceback.extract_tb(sys.exc_info()[2])
3596 if len(tb) > 2: # no
3610 if len(tb) > 2: # no
3597 raise
3611 raise
3598 u.debug(inst, "\n")
3612 u.debug(inst, "\n")
3599 u.warn(_("%s: invalid arguments\n") % cmd)
3613 u.warn(_("%s: invalid arguments\n") % cmd)
3600 help_(u, cmd)
3614 help_(u, cmd)
3601 except SystemExit, inst:
3615 except SystemExit, inst:
3602 # Commands shouldn't sys.exit directly, but give a return code.
3616 # Commands shouldn't sys.exit directly, but give a return code.
3603 # Just in case catch this and and pass exit code to caller.
3617 # Just in case catch this and and pass exit code to caller.
3604 return inst.code
3618 return inst.code
3605 except:
3619 except:
3606 u.warn(_("** unknown exception encountered, details follow\n"))
3620 u.warn(_("** unknown exception encountered, details follow\n"))
3607 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3621 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3608 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3622 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3609 % version.get_version())
3623 % version.get_version())
3610 raise
3624 raise
3611
3625
3612 return -1
3626 return -1
@@ -1,944 +1,952 b''
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os
9 import os
10 import os.path
10 import os.path
11 import mimetypes
11 import mimetypes
12 from mercurial.demandload import demandload
12 from mercurial.demandload import demandload
13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
15 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
15 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
16 from mercurial.node import *
16 from mercurial.node import *
17 from mercurial.i18n import gettext as _
17 from mercurial.i18n import gettext as _
18
18
19 def _up(p):
19 def _up(p):
20 if p[0] != "/":
20 if p[0] != "/":
21 p = "/" + p
21 p = "/" + p
22 if p[-1] == "/":
22 if p[-1] == "/":
23 p = p[:-1]
23 p = p[:-1]
24 up = os.path.dirname(p)
24 up = os.path.dirname(p)
25 if up == "/":
25 if up == "/":
26 return "/"
26 return "/"
27 return up + "/"
27 return up + "/"
28
28
29 class hgweb(object):
29 class hgweb(object):
30 def __init__(self, repo, name=None):
30 def __init__(self, repo, name=None):
31 if type(repo) == type(""):
31 if type(repo) == type(""):
32 self.repo = hg.repository(ui.ui(), repo)
32 self.repo = hg.repository(ui.ui(), repo)
33 else:
33 else:
34 self.repo = repo
34 self.repo = repo
35
35
36 self.mtime = -1
36 self.mtime = -1
37 self.reponame = name
37 self.reponame = name
38 self.archives = 'zip', 'gz', 'bz2'
38 self.archives = 'zip', 'gz', 'bz2'
39 self.templatepath = self.repo.ui.config("web", "templates",
39 self.templatepath = self.repo.ui.config("web", "templates",
40 templater.templatepath())
40 templater.templatepath())
41
41
42 def refresh(self):
42 def refresh(self):
43 mtime = get_mtime(self.repo.root)
43 mtime = get_mtime(self.repo.root)
44 if mtime != self.mtime:
44 if mtime != self.mtime:
45 self.mtime = mtime
45 self.mtime = mtime
46 self.repo = hg.repository(self.repo.ui, self.repo.root)
46 self.repo = hg.repository(self.repo.ui, self.repo.root)
47 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
47 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
48 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
48 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
49 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
49 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
50
50
51 def archivelist(self, nodeid):
51 def archivelist(self, nodeid):
52 allowed = self.repo.ui.configlist("web", "allow_archive")
52 allowed = self.repo.ui.configlist("web", "allow_archive")
53 for i in self.archives:
53 for i in self.archives:
54 if i in allowed or self.repo.ui.configbool("web", "allow" + i):
54 if i in allowed or self.repo.ui.configbool("web", "allow" + i):
55 yield {"type" : i, "node" : nodeid, "url": ""}
55 yield {"type" : i, "node" : nodeid, "url": ""}
56
56
57 def listfiles(self, files, mf):
57 def listfiles(self, files, mf):
58 for f in files[:self.maxfiles]:
58 for f in files[:self.maxfiles]:
59 yield self.t("filenodelink", node=hex(mf[f]), file=f)
59 yield self.t("filenodelink", node=hex(mf[f]), file=f)
60 if len(files) > self.maxfiles:
60 if len(files) > self.maxfiles:
61 yield self.t("fileellipses")
61 yield self.t("fileellipses")
62
62
63 def listfilediffs(self, files, changeset):
63 def listfilediffs(self, files, changeset):
64 for f in files[:self.maxfiles]:
64 for f in files[:self.maxfiles]:
65 yield self.t("filedifflink", node=hex(changeset), file=f)
65 yield self.t("filedifflink", node=hex(changeset), file=f)
66 if len(files) > self.maxfiles:
66 if len(files) > self.maxfiles:
67 yield self.t("fileellipses")
67 yield self.t("fileellipses")
68
68
69 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
69 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
70 if not rev:
70 if not rev:
71 rev = lambda x: ""
71 rev = lambda x: ""
72 siblings = [s for s in siblings if s != nullid]
72 siblings = [s for s in siblings if s != nullid]
73 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
73 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
74 return
74 return
75 for s in siblings:
75 for s in siblings:
76 yield dict(node=hex(s), rev=rev(s), **args)
76 yield dict(node=hex(s), rev=rev(s), **args)
77
77
78 def renamelink(self, fl, node):
78 def renamelink(self, fl, node):
79 r = fl.renamed(node)
79 r = fl.renamed(node)
80 if r:
80 if r:
81 return [dict(file=r[0], node=hex(r[1]))]
81 return [dict(file=r[0], node=hex(r[1]))]
82 return []
82 return []
83
83
84 def showtag(self, t1, node=nullid, **args):
84 def showtag(self, t1, node=nullid, **args):
85 for t in self.repo.nodetags(node):
85 for t in self.repo.nodetags(node):
86 yield self.t(t1, tag=t, **args)
86 yield self.t(t1, tag=t, **args)
87
87
88 def diff(self, node1, node2, files):
88 def diff(self, node1, node2, files):
89 def filterfiles(filters, files):
89 def filterfiles(filters, files):
90 l = [x for x in files if x in filters]
90 l = [x for x in files if x in filters]
91
91
92 for t in filters:
92 for t in filters:
93 if t and t[-1] != os.sep:
93 if t and t[-1] != os.sep:
94 t += os.sep
94 t += os.sep
95 l += [x for x in files if x.startswith(t)]
95 l += [x for x in files if x.startswith(t)]
96 return l
96 return l
97
97
98 parity = [0]
98 parity = [0]
99 def diffblock(diff, f, fn):
99 def diffblock(diff, f, fn):
100 yield self.t("diffblock",
100 yield self.t("diffblock",
101 lines=prettyprintlines(diff),
101 lines=prettyprintlines(diff),
102 parity=parity[0],
102 parity=parity[0],
103 file=f,
103 file=f,
104 filenode=hex(fn or nullid))
104 filenode=hex(fn or nullid))
105 parity[0] = 1 - parity[0]
105 parity[0] = 1 - parity[0]
106
106
107 def prettyprintlines(diff):
107 def prettyprintlines(diff):
108 for l in diff.splitlines(1):
108 for l in diff.splitlines(1):
109 if l.startswith('+'):
109 if l.startswith('+'):
110 yield self.t("difflineplus", line=l)
110 yield self.t("difflineplus", line=l)
111 elif l.startswith('-'):
111 elif l.startswith('-'):
112 yield self.t("difflineminus", line=l)
112 yield self.t("difflineminus", line=l)
113 elif l.startswith('@'):
113 elif l.startswith('@'):
114 yield self.t("difflineat", line=l)
114 yield self.t("difflineat", line=l)
115 else:
115 else:
116 yield self.t("diffline", line=l)
116 yield self.t("diffline", line=l)
117
117
118 r = self.repo
118 r = self.repo
119 cl = r.changelog
119 cl = r.changelog
120 mf = r.manifest
120 mf = r.manifest
121 change1 = cl.read(node1)
121 change1 = cl.read(node1)
122 change2 = cl.read(node2)
122 change2 = cl.read(node2)
123 mmap1 = mf.read(change1[0])
123 mmap1 = mf.read(change1[0])
124 mmap2 = mf.read(change2[0])
124 mmap2 = mf.read(change2[0])
125 date1 = util.datestr(change1[2])
125 date1 = util.datestr(change1[2])
126 date2 = util.datestr(change2[2])
126 date2 = util.datestr(change2[2])
127
127
128 modified, added, removed, deleted, unknown = r.changes(node1, node2)
128 modified, added, removed, deleted, unknown = r.changes(node1, node2)
129 if files:
129 if files:
130 modified, added, removed = map(lambda x: filterfiles(files, x),
130 modified, added, removed = map(lambda x: filterfiles(files, x),
131 (modified, added, removed))
131 (modified, added, removed))
132
132
133 diffopts = self.repo.ui.diffopts()
133 diffopts = self.repo.ui.diffopts()
134 showfunc = diffopts['showfunc']
134 showfunc = diffopts['showfunc']
135 ignorews = diffopts['ignorews']
135 ignorews = diffopts['ignorews']
136 ignorewsamount = diffopts['ignorewsamount']
137 ignoreblanklines = diffopts['ignoreblanklines']
136 for f in modified:
138 for f in modified:
137 to = r.file(f).read(mmap1[f])
139 to = r.file(f).read(mmap1[f])
138 tn = r.file(f).read(mmap2[f])
140 tn = r.file(f).read(mmap2[f])
139 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
141 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
140 showfunc=showfunc, ignorews=ignorews), f, tn)
142 showfunc=showfunc, ignorews=ignorews,
143 ignorewsamount=ignorewsamount,
144 ignoreblanklines=ignoreblanklines), f, tn)
141 for f in added:
145 for f in added:
142 to = None
146 to = None
143 tn = r.file(f).read(mmap2[f])
147 tn = r.file(f).read(mmap2[f])
144 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
148 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
145 showfunc=showfunc, ignorews=ignorews), f, tn)
149 showfunc=showfunc, ignorews=ignorews,
150 ignorewsamount=ignorewsamount,
151 ignoreblanklines=ignoreblanklines), f, tn)
146 for f in removed:
152 for f in removed:
147 to = r.file(f).read(mmap1[f])
153 to = r.file(f).read(mmap1[f])
148 tn = None
154 tn = None
149 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
155 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
150 showfunc=showfunc, ignorews=ignorews), f, tn)
156 showfunc=showfunc, ignorews=ignorews,
157 ignorewsamount=ignorewsamount,
158 ignoreblanklines=ignoreblanklines), f, tn)
151
159
152 def changelog(self, pos):
160 def changelog(self, pos):
153 def changenav(**map):
161 def changenav(**map):
154 def seq(factor, maxchanges=None):
162 def seq(factor, maxchanges=None):
155 if maxchanges:
163 if maxchanges:
156 yield maxchanges
164 yield maxchanges
157 if maxchanges >= 20 and maxchanges <= 40:
165 if maxchanges >= 20 and maxchanges <= 40:
158 yield 50
166 yield 50
159 else:
167 else:
160 yield 1 * factor
168 yield 1 * factor
161 yield 3 * factor
169 yield 3 * factor
162 for f in seq(factor * 10):
170 for f in seq(factor * 10):
163 yield f
171 yield f
164
172
165 l = []
173 l = []
166 last = 0
174 last = 0
167 for f in seq(1, self.maxchanges):
175 for f in seq(1, self.maxchanges):
168 if f < self.maxchanges or f <= last:
176 if f < self.maxchanges or f <= last:
169 continue
177 continue
170 if f > count:
178 if f > count:
171 break
179 break
172 last = f
180 last = f
173 r = "%d" % f
181 r = "%d" % f
174 if pos + f < count:
182 if pos + f < count:
175 l.append(("+" + r, pos + f))
183 l.append(("+" + r, pos + f))
176 if pos - f >= 0:
184 if pos - f >= 0:
177 l.insert(0, ("-" + r, pos - f))
185 l.insert(0, ("-" + r, pos - f))
178
186
179 yield {"rev": 0, "label": "(0)"}
187 yield {"rev": 0, "label": "(0)"}
180
188
181 for label, rev in l:
189 for label, rev in l:
182 yield {"label": label, "rev": rev}
190 yield {"label": label, "rev": rev}
183
191
184 yield {"label": "tip", "rev": "tip"}
192 yield {"label": "tip", "rev": "tip"}
185
193
186 def changelist(**map):
194 def changelist(**map):
187 parity = (start - end) & 1
195 parity = (start - end) & 1
188 cl = self.repo.changelog
196 cl = self.repo.changelog
189 l = [] # build a list in forward order for efficiency
197 l = [] # build a list in forward order for efficiency
190 for i in range(start, end):
198 for i in range(start, end):
191 n = cl.node(i)
199 n = cl.node(i)
192 changes = cl.read(n)
200 changes = cl.read(n)
193 hn = hex(n)
201 hn = hex(n)
194
202
195 l.insert(0, {"parity": parity,
203 l.insert(0, {"parity": parity,
196 "author": changes[1],
204 "author": changes[1],
197 "parent": self.siblings(cl.parents(n), cl.rev,
205 "parent": self.siblings(cl.parents(n), cl.rev,
198 cl.rev(n) - 1),
206 cl.rev(n) - 1),
199 "child": self.siblings(cl.children(n), cl.rev,
207 "child": self.siblings(cl.children(n), cl.rev,
200 cl.rev(n) + 1),
208 cl.rev(n) + 1),
201 "changelogtag": self.showtag("changelogtag",n),
209 "changelogtag": self.showtag("changelogtag",n),
202 "manifest": hex(changes[0]),
210 "manifest": hex(changes[0]),
203 "desc": changes[4],
211 "desc": changes[4],
204 "date": changes[2],
212 "date": changes[2],
205 "files": self.listfilediffs(changes[3], n),
213 "files": self.listfilediffs(changes[3], n),
206 "rev": i,
214 "rev": i,
207 "node": hn})
215 "node": hn})
208 parity = 1 - parity
216 parity = 1 - parity
209
217
210 for e in l:
218 for e in l:
211 yield e
219 yield e
212
220
213 cl = self.repo.changelog
221 cl = self.repo.changelog
214 mf = cl.read(cl.tip())[0]
222 mf = cl.read(cl.tip())[0]
215 count = cl.count()
223 count = cl.count()
216 start = max(0, pos - self.maxchanges + 1)
224 start = max(0, pos - self.maxchanges + 1)
217 end = min(count, start + self.maxchanges)
225 end = min(count, start + self.maxchanges)
218 pos = end - 1
226 pos = end - 1
219
227
220 yield self.t('changelog',
228 yield self.t('changelog',
221 changenav=changenav,
229 changenav=changenav,
222 manifest=hex(mf),
230 manifest=hex(mf),
223 rev=pos, changesets=count, entries=changelist,
231 rev=pos, changesets=count, entries=changelist,
224 archives=self.archivelist("tip"))
232 archives=self.archivelist("tip"))
225
233
226 def search(self, query):
234 def search(self, query):
227
235
228 def changelist(**map):
236 def changelist(**map):
229 cl = self.repo.changelog
237 cl = self.repo.changelog
230 count = 0
238 count = 0
231 qw = query.lower().split()
239 qw = query.lower().split()
232
240
233 def revgen():
241 def revgen():
234 for i in range(cl.count() - 1, 0, -100):
242 for i in range(cl.count() - 1, 0, -100):
235 l = []
243 l = []
236 for j in range(max(0, i - 100), i):
244 for j in range(max(0, i - 100), i):
237 n = cl.node(j)
245 n = cl.node(j)
238 changes = cl.read(n)
246 changes = cl.read(n)
239 l.append((n, j, changes))
247 l.append((n, j, changes))
240 l.reverse()
248 l.reverse()
241 for e in l:
249 for e in l:
242 yield e
250 yield e
243
251
244 for n, i, changes in revgen():
252 for n, i, changes in revgen():
245 miss = 0
253 miss = 0
246 for q in qw:
254 for q in qw:
247 if not (q in changes[1].lower() or
255 if not (q in changes[1].lower() or
248 q in changes[4].lower() or
256 q in changes[4].lower() or
249 q in " ".join(changes[3][:20]).lower()):
257 q in " ".join(changes[3][:20]).lower()):
250 miss = 1
258 miss = 1
251 break
259 break
252 if miss:
260 if miss:
253 continue
261 continue
254
262
255 count += 1
263 count += 1
256 hn = hex(n)
264 hn = hex(n)
257
265
258 yield self.t('searchentry',
266 yield self.t('searchentry',
259 parity=count & 1,
267 parity=count & 1,
260 author=changes[1],
268 author=changes[1],
261 parent=self.siblings(cl.parents(n), cl.rev),
269 parent=self.siblings(cl.parents(n), cl.rev),
262 child=self.siblings(cl.children(n), cl.rev),
270 child=self.siblings(cl.children(n), cl.rev),
263 changelogtag=self.showtag("changelogtag",n),
271 changelogtag=self.showtag("changelogtag",n),
264 manifest=hex(changes[0]),
272 manifest=hex(changes[0]),
265 desc=changes[4],
273 desc=changes[4],
266 date=changes[2],
274 date=changes[2],
267 files=self.listfilediffs(changes[3], n),
275 files=self.listfilediffs(changes[3], n),
268 rev=i,
276 rev=i,
269 node=hn)
277 node=hn)
270
278
271 if count >= self.maxchanges:
279 if count >= self.maxchanges:
272 break
280 break
273
281
274 cl = self.repo.changelog
282 cl = self.repo.changelog
275 mf = cl.read(cl.tip())[0]
283 mf = cl.read(cl.tip())[0]
276
284
277 yield self.t('search',
285 yield self.t('search',
278 query=query,
286 query=query,
279 manifest=hex(mf),
287 manifest=hex(mf),
280 entries=changelist)
288 entries=changelist)
281
289
282 def changeset(self, nodeid):
290 def changeset(self, nodeid):
283 cl = self.repo.changelog
291 cl = self.repo.changelog
284 n = self.repo.lookup(nodeid)
292 n = self.repo.lookup(nodeid)
285 nodeid = hex(n)
293 nodeid = hex(n)
286 changes = cl.read(n)
294 changes = cl.read(n)
287 p1 = cl.parents(n)[0]
295 p1 = cl.parents(n)[0]
288
296
289 files = []
297 files = []
290 mf = self.repo.manifest.read(changes[0])
298 mf = self.repo.manifest.read(changes[0])
291 for f in changes[3]:
299 for f in changes[3]:
292 files.append(self.t("filenodelink",
300 files.append(self.t("filenodelink",
293 filenode=hex(mf.get(f, nullid)), file=f))
301 filenode=hex(mf.get(f, nullid)), file=f))
294
302
295 def diff(**map):
303 def diff(**map):
296 yield self.diff(p1, n, None)
304 yield self.diff(p1, n, None)
297
305
298 yield self.t('changeset',
306 yield self.t('changeset',
299 diff=diff,
307 diff=diff,
300 rev=cl.rev(n),
308 rev=cl.rev(n),
301 node=nodeid,
309 node=nodeid,
302 parent=self.siblings(cl.parents(n), cl.rev),
310 parent=self.siblings(cl.parents(n), cl.rev),
303 child=self.siblings(cl.children(n), cl.rev),
311 child=self.siblings(cl.children(n), cl.rev),
304 changesettag=self.showtag("changesettag",n),
312 changesettag=self.showtag("changesettag",n),
305 manifest=hex(changes[0]),
313 manifest=hex(changes[0]),
306 author=changes[1],
314 author=changes[1],
307 desc=changes[4],
315 desc=changes[4],
308 date=changes[2],
316 date=changes[2],
309 files=files,
317 files=files,
310 archives=self.archivelist(nodeid))
318 archives=self.archivelist(nodeid))
311
319
312 def filelog(self, f, filenode):
320 def filelog(self, f, filenode):
313 cl = self.repo.changelog
321 cl = self.repo.changelog
314 fl = self.repo.file(f)
322 fl = self.repo.file(f)
315 filenode = hex(fl.lookup(filenode))
323 filenode = hex(fl.lookup(filenode))
316 count = fl.count()
324 count = fl.count()
317
325
318 def entries(**map):
326 def entries(**map):
319 l = []
327 l = []
320 parity = (count - 1) & 1
328 parity = (count - 1) & 1
321
329
322 for i in range(count):
330 for i in range(count):
323 n = fl.node(i)
331 n = fl.node(i)
324 lr = fl.linkrev(n)
332 lr = fl.linkrev(n)
325 cn = cl.node(lr)
333 cn = cl.node(lr)
326 cs = cl.read(cl.node(lr))
334 cs = cl.read(cl.node(lr))
327
335
328 l.insert(0, {"parity": parity,
336 l.insert(0, {"parity": parity,
329 "filenode": hex(n),
337 "filenode": hex(n),
330 "filerev": i,
338 "filerev": i,
331 "file": f,
339 "file": f,
332 "node": hex(cn),
340 "node": hex(cn),
333 "author": cs[1],
341 "author": cs[1],
334 "date": cs[2],
342 "date": cs[2],
335 "rename": self.renamelink(fl, n),
343 "rename": self.renamelink(fl, n),
336 "parent": self.siblings(fl.parents(n),
344 "parent": self.siblings(fl.parents(n),
337 fl.rev, file=f),
345 fl.rev, file=f),
338 "child": self.siblings(fl.children(n),
346 "child": self.siblings(fl.children(n),
339 fl.rev, file=f),
347 fl.rev, file=f),
340 "desc": cs[4]})
348 "desc": cs[4]})
341 parity = 1 - parity
349 parity = 1 - parity
342
350
343 for e in l:
351 for e in l:
344 yield e
352 yield e
345
353
346 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
354 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
347
355
348 def filerevision(self, f, node):
356 def filerevision(self, f, node):
349 fl = self.repo.file(f)
357 fl = self.repo.file(f)
350 n = fl.lookup(node)
358 n = fl.lookup(node)
351 node = hex(n)
359 node = hex(n)
352 text = fl.read(n)
360 text = fl.read(n)
353 changerev = fl.linkrev(n)
361 changerev = fl.linkrev(n)
354 cl = self.repo.changelog
362 cl = self.repo.changelog
355 cn = cl.node(changerev)
363 cn = cl.node(changerev)
356 cs = cl.read(cn)
364 cs = cl.read(cn)
357 mfn = cs[0]
365 mfn = cs[0]
358
366
359 mt = mimetypes.guess_type(f)[0]
367 mt = mimetypes.guess_type(f)[0]
360 rawtext = text
368 rawtext = text
361 if util.binary(text):
369 if util.binary(text):
362 mt = mt or 'application/octet-stream'
370 mt = mt or 'application/octet-stream'
363 text = "(binary:%s)" % mt
371 text = "(binary:%s)" % mt
364 mt = mt or 'text/plain'
372 mt = mt or 'text/plain'
365
373
366 def lines():
374 def lines():
367 for l, t in enumerate(text.splitlines(1)):
375 for l, t in enumerate(text.splitlines(1)):
368 yield {"line": t,
376 yield {"line": t,
369 "linenumber": "% 6d" % (l + 1),
377 "linenumber": "% 6d" % (l + 1),
370 "parity": l & 1}
378 "parity": l & 1}
371
379
372 yield self.t("filerevision",
380 yield self.t("filerevision",
373 file=f,
381 file=f,
374 filenode=node,
382 filenode=node,
375 path=_up(f),
383 path=_up(f),
376 text=lines(),
384 text=lines(),
377 raw=rawtext,
385 raw=rawtext,
378 mimetype=mt,
386 mimetype=mt,
379 rev=changerev,
387 rev=changerev,
380 node=hex(cn),
388 node=hex(cn),
381 manifest=hex(mfn),
389 manifest=hex(mfn),
382 author=cs[1],
390 author=cs[1],
383 date=cs[2],
391 date=cs[2],
384 parent=self.siblings(fl.parents(n), fl.rev, file=f),
392 parent=self.siblings(fl.parents(n), fl.rev, file=f),
385 child=self.siblings(fl.children(n), fl.rev, file=f),
393 child=self.siblings(fl.children(n), fl.rev, file=f),
386 rename=self.renamelink(fl, n),
394 rename=self.renamelink(fl, n),
387 permissions=self.repo.manifest.readflags(mfn)[f])
395 permissions=self.repo.manifest.readflags(mfn)[f])
388
396
389 def fileannotate(self, f, node):
397 def fileannotate(self, f, node):
390 bcache = {}
398 bcache = {}
391 ncache = {}
399 ncache = {}
392 fl = self.repo.file(f)
400 fl = self.repo.file(f)
393 n = fl.lookup(node)
401 n = fl.lookup(node)
394 node = hex(n)
402 node = hex(n)
395 changerev = fl.linkrev(n)
403 changerev = fl.linkrev(n)
396
404
397 cl = self.repo.changelog
405 cl = self.repo.changelog
398 cn = cl.node(changerev)
406 cn = cl.node(changerev)
399 cs = cl.read(cn)
407 cs = cl.read(cn)
400 mfn = cs[0]
408 mfn = cs[0]
401
409
402 def annotate(**map):
410 def annotate(**map):
403 parity = 1
411 parity = 1
404 last = None
412 last = None
405 for r, l in fl.annotate(n):
413 for r, l in fl.annotate(n):
406 try:
414 try:
407 cnode = ncache[r]
415 cnode = ncache[r]
408 except KeyError:
416 except KeyError:
409 cnode = ncache[r] = self.repo.changelog.node(r)
417 cnode = ncache[r] = self.repo.changelog.node(r)
410
418
411 try:
419 try:
412 name = bcache[r]
420 name = bcache[r]
413 except KeyError:
421 except KeyError:
414 cl = self.repo.changelog.read(cnode)
422 cl = self.repo.changelog.read(cnode)
415 bcache[r] = name = self.repo.ui.shortuser(cl[1])
423 bcache[r] = name = self.repo.ui.shortuser(cl[1])
416
424
417 if last != cnode:
425 if last != cnode:
418 parity = 1 - parity
426 parity = 1 - parity
419 last = cnode
427 last = cnode
420
428
421 yield {"parity": parity,
429 yield {"parity": parity,
422 "node": hex(cnode),
430 "node": hex(cnode),
423 "rev": r,
431 "rev": r,
424 "author": name,
432 "author": name,
425 "file": f,
433 "file": f,
426 "line": l}
434 "line": l}
427
435
428 yield self.t("fileannotate",
436 yield self.t("fileannotate",
429 file=f,
437 file=f,
430 filenode=node,
438 filenode=node,
431 annotate=annotate,
439 annotate=annotate,
432 path=_up(f),
440 path=_up(f),
433 rev=changerev,
441 rev=changerev,
434 node=hex(cn),
442 node=hex(cn),
435 manifest=hex(mfn),
443 manifest=hex(mfn),
436 author=cs[1],
444 author=cs[1],
437 date=cs[2],
445 date=cs[2],
438 rename=self.renamelink(fl, n),
446 rename=self.renamelink(fl, n),
439 parent=self.siblings(fl.parents(n), fl.rev, file=f),
447 parent=self.siblings(fl.parents(n), fl.rev, file=f),
440 child=self.siblings(fl.children(n), fl.rev, file=f),
448 child=self.siblings(fl.children(n), fl.rev, file=f),
441 permissions=self.repo.manifest.readflags(mfn)[f])
449 permissions=self.repo.manifest.readflags(mfn)[f])
442
450
443 def manifest(self, mnode, path):
451 def manifest(self, mnode, path):
444 man = self.repo.manifest
452 man = self.repo.manifest
445 mn = man.lookup(mnode)
453 mn = man.lookup(mnode)
446 mnode = hex(mn)
454 mnode = hex(mn)
447 mf = man.read(mn)
455 mf = man.read(mn)
448 rev = man.rev(mn)
456 rev = man.rev(mn)
449 changerev = man.linkrev(mn)
457 changerev = man.linkrev(mn)
450 node = self.repo.changelog.node(changerev)
458 node = self.repo.changelog.node(changerev)
451 mff = man.readflags(mn)
459 mff = man.readflags(mn)
452
460
453 files = {}
461 files = {}
454
462
455 p = path[1:]
463 p = path[1:]
456 if p and p[-1] != "/":
464 if p and p[-1] != "/":
457 p += "/"
465 p += "/"
458 l = len(p)
466 l = len(p)
459
467
460 for f,n in mf.items():
468 for f,n in mf.items():
461 if f[:l] != p:
469 if f[:l] != p:
462 continue
470 continue
463 remain = f[l:]
471 remain = f[l:]
464 if "/" in remain:
472 if "/" in remain:
465 short = remain[:remain.index("/") + 1] # bleah
473 short = remain[:remain.index("/") + 1] # bleah
466 files[short] = (f, None)
474 files[short] = (f, None)
467 else:
475 else:
468 short = os.path.basename(remain)
476 short = os.path.basename(remain)
469 files[short] = (f, n)
477 files[short] = (f, n)
470
478
471 def filelist(**map):
479 def filelist(**map):
472 parity = 0
480 parity = 0
473 fl = files.keys()
481 fl = files.keys()
474 fl.sort()
482 fl.sort()
475 for f in fl:
483 for f in fl:
476 full, fnode = files[f]
484 full, fnode = files[f]
477 if not fnode:
485 if not fnode:
478 continue
486 continue
479
487
480 yield {"file": full,
488 yield {"file": full,
481 "manifest": mnode,
489 "manifest": mnode,
482 "filenode": hex(fnode),
490 "filenode": hex(fnode),
483 "parity": parity,
491 "parity": parity,
484 "basename": f,
492 "basename": f,
485 "permissions": mff[full]}
493 "permissions": mff[full]}
486 parity = 1 - parity
494 parity = 1 - parity
487
495
488 def dirlist(**map):
496 def dirlist(**map):
489 parity = 0
497 parity = 0
490 fl = files.keys()
498 fl = files.keys()
491 fl.sort()
499 fl.sort()
492 for f in fl:
500 for f in fl:
493 full, fnode = files[f]
501 full, fnode = files[f]
494 if fnode:
502 if fnode:
495 continue
503 continue
496
504
497 yield {"parity": parity,
505 yield {"parity": parity,
498 "path": os.path.join(path, f),
506 "path": os.path.join(path, f),
499 "manifest": mnode,
507 "manifest": mnode,
500 "basename": f[:-1]}
508 "basename": f[:-1]}
501 parity = 1 - parity
509 parity = 1 - parity
502
510
503 yield self.t("manifest",
511 yield self.t("manifest",
504 manifest=mnode,
512 manifest=mnode,
505 rev=rev,
513 rev=rev,
506 node=hex(node),
514 node=hex(node),
507 path=path,
515 path=path,
508 up=_up(path),
516 up=_up(path),
509 fentries=filelist,
517 fentries=filelist,
510 dentries=dirlist,
518 dentries=dirlist,
511 archives=self.archivelist(hex(node)))
519 archives=self.archivelist(hex(node)))
512
520
513 def tags(self):
521 def tags(self):
514 cl = self.repo.changelog
522 cl = self.repo.changelog
515 mf = cl.read(cl.tip())[0]
523 mf = cl.read(cl.tip())[0]
516
524
517 i = self.repo.tagslist()
525 i = self.repo.tagslist()
518 i.reverse()
526 i.reverse()
519
527
520 def entries(notip=False, **map):
528 def entries(notip=False, **map):
521 parity = 0
529 parity = 0
522 for k,n in i:
530 for k,n in i:
523 if notip and k == "tip": continue
531 if notip and k == "tip": continue
524 yield {"parity": parity,
532 yield {"parity": parity,
525 "tag": k,
533 "tag": k,
526 "tagmanifest": hex(cl.read(n)[0]),
534 "tagmanifest": hex(cl.read(n)[0]),
527 "date": cl.read(n)[2],
535 "date": cl.read(n)[2],
528 "node": hex(n)}
536 "node": hex(n)}
529 parity = 1 - parity
537 parity = 1 - parity
530
538
531 yield self.t("tags",
539 yield self.t("tags",
532 manifest=hex(mf),
540 manifest=hex(mf),
533 entries=lambda **x: entries(False, **x),
541 entries=lambda **x: entries(False, **x),
534 entriesnotip=lambda **x: entries(True, **x))
542 entriesnotip=lambda **x: entries(True, **x))
535
543
536 def summary(self):
544 def summary(self):
537 cl = self.repo.changelog
545 cl = self.repo.changelog
538 mf = cl.read(cl.tip())[0]
546 mf = cl.read(cl.tip())[0]
539
547
540 i = self.repo.tagslist()
548 i = self.repo.tagslist()
541 i.reverse()
549 i.reverse()
542
550
543 def tagentries(**map):
551 def tagentries(**map):
544 parity = 0
552 parity = 0
545 count = 0
553 count = 0
546 for k,n in i:
554 for k,n in i:
547 if k == "tip": # skip tip
555 if k == "tip": # skip tip
548 continue;
556 continue;
549
557
550 count += 1
558 count += 1
551 if count > 10: # limit to 10 tags
559 if count > 10: # limit to 10 tags
552 break;
560 break;
553
561
554 c = cl.read(n)
562 c = cl.read(n)
555 m = c[0]
563 m = c[0]
556 t = c[2]
564 t = c[2]
557
565
558 yield self.t("tagentry",
566 yield self.t("tagentry",
559 parity = parity,
567 parity = parity,
560 tag = k,
568 tag = k,
561 node = hex(n),
569 node = hex(n),
562 date = t,
570 date = t,
563 tagmanifest = hex(m))
571 tagmanifest = hex(m))
564 parity = 1 - parity
572 parity = 1 - parity
565
573
566 def changelist(**map):
574 def changelist(**map):
567 parity = 0
575 parity = 0
568 cl = self.repo.changelog
576 cl = self.repo.changelog
569 l = [] # build a list in forward order for efficiency
577 l = [] # build a list in forward order for efficiency
570 for i in range(start, end):
578 for i in range(start, end):
571 n = cl.node(i)
579 n = cl.node(i)
572 changes = cl.read(n)
580 changes = cl.read(n)
573 hn = hex(n)
581 hn = hex(n)
574 t = changes[2]
582 t = changes[2]
575
583
576 l.insert(0, self.t(
584 l.insert(0, self.t(
577 'shortlogentry',
585 'shortlogentry',
578 parity = parity,
586 parity = parity,
579 author = changes[1],
587 author = changes[1],
580 manifest = hex(changes[0]),
588 manifest = hex(changes[0]),
581 desc = changes[4],
589 desc = changes[4],
582 date = t,
590 date = t,
583 rev = i,
591 rev = i,
584 node = hn))
592 node = hn))
585 parity = 1 - parity
593 parity = 1 - parity
586
594
587 yield l
595 yield l
588
596
589 cl = self.repo.changelog
597 cl = self.repo.changelog
590 mf = cl.read(cl.tip())[0]
598 mf = cl.read(cl.tip())[0]
591 count = cl.count()
599 count = cl.count()
592 start = max(0, count - self.maxchanges)
600 start = max(0, count - self.maxchanges)
593 end = min(count, start + self.maxchanges)
601 end = min(count, start + self.maxchanges)
594
602
595 yield self.t("summary",
603 yield self.t("summary",
596 desc = self.repo.ui.config("web", "description", "unknown"),
604 desc = self.repo.ui.config("web", "description", "unknown"),
597 owner = (self.repo.ui.config("ui", "username") or # preferred
605 owner = (self.repo.ui.config("ui", "username") or # preferred
598 self.repo.ui.config("web", "contact") or # deprecated
606 self.repo.ui.config("web", "contact") or # deprecated
599 self.repo.ui.config("web", "author", "unknown")), # also
607 self.repo.ui.config("web", "author", "unknown")), # also
600 lastchange = (0, 0), # FIXME
608 lastchange = (0, 0), # FIXME
601 manifest = hex(mf),
609 manifest = hex(mf),
602 tags = tagentries,
610 tags = tagentries,
603 shortlog = changelist)
611 shortlog = changelist)
604
612
605 def filediff(self, file, changeset):
613 def filediff(self, file, changeset):
606 cl = self.repo.changelog
614 cl = self.repo.changelog
607 n = self.repo.lookup(changeset)
615 n = self.repo.lookup(changeset)
608 changeset = hex(n)
616 changeset = hex(n)
609 p1 = cl.parents(n)[0]
617 p1 = cl.parents(n)[0]
610 cs = cl.read(n)
618 cs = cl.read(n)
611 mf = self.repo.manifest.read(cs[0])
619 mf = self.repo.manifest.read(cs[0])
612
620
613 def diff(**map):
621 def diff(**map):
614 yield self.diff(p1, n, [file])
622 yield self.diff(p1, n, [file])
615
623
616 yield self.t("filediff",
624 yield self.t("filediff",
617 file=file,
625 file=file,
618 filenode=hex(mf.get(file, nullid)),
626 filenode=hex(mf.get(file, nullid)),
619 node=changeset,
627 node=changeset,
620 rev=self.repo.changelog.rev(n),
628 rev=self.repo.changelog.rev(n),
621 parent=self.siblings(cl.parents(n), cl.rev),
629 parent=self.siblings(cl.parents(n), cl.rev),
622 child=self.siblings(cl.children(n), cl.rev),
630 child=self.siblings(cl.children(n), cl.rev),
623 diff=diff)
631 diff=diff)
624
632
625 archive_specs = {
633 archive_specs = {
626 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
634 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
627 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
635 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
628 'zip': ('application/zip', 'zip', '.zip', None),
636 'zip': ('application/zip', 'zip', '.zip', None),
629 }
637 }
630
638
631 def archive(self, req, cnode, type_):
639 def archive(self, req, cnode, type_):
632 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
640 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
633 name = "%s-%s" % (reponame, short(cnode))
641 name = "%s-%s" % (reponame, short(cnode))
634 mimetype, artype, extension, encoding = self.archive_specs[type_]
642 mimetype, artype, extension, encoding = self.archive_specs[type_]
635 headers = [('Content-type', mimetype),
643 headers = [('Content-type', mimetype),
636 ('Content-disposition', 'attachment; filename=%s%s' %
644 ('Content-disposition', 'attachment; filename=%s%s' %
637 (name, extension))]
645 (name, extension))]
638 if encoding:
646 if encoding:
639 headers.append(('Content-encoding', encoding))
647 headers.append(('Content-encoding', encoding))
640 req.header(headers)
648 req.header(headers)
641 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
649 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
642
650
643 # add tags to things
651 # add tags to things
644 # tags -> list of changesets corresponding to tags
652 # tags -> list of changesets corresponding to tags
645 # find tag, changeset, file
653 # find tag, changeset, file
646
654
647 def cleanpath(self, path):
655 def cleanpath(self, path):
648 p = util.normpath(path)
656 p = util.normpath(path)
649 if p[:2] == "..":
657 if p[:2] == "..":
650 raise Exception("suspicious path")
658 raise Exception("suspicious path")
651 return p
659 return p
652
660
653 def run(self):
661 def run(self):
654 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
662 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
655 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
663 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
656 import mercurial.hgweb.wsgicgi as wsgicgi
664 import mercurial.hgweb.wsgicgi as wsgicgi
657 from request import wsgiapplication
665 from request import wsgiapplication
658 def make_web_app():
666 def make_web_app():
659 return self
667 return self
660 wsgicgi.launch(wsgiapplication(make_web_app))
668 wsgicgi.launch(wsgiapplication(make_web_app))
661
669
662 def run_wsgi(self, req):
670 def run_wsgi(self, req):
663 def header(**map):
671 def header(**map):
664 header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
672 header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
665 msg = mimetools.Message(header_file, 0)
673 msg = mimetools.Message(header_file, 0)
666 req.header(msg.items())
674 req.header(msg.items())
667 yield header_file.read()
675 yield header_file.read()
668
676
669 def rawfileheader(**map):
677 def rawfileheader(**map):
670 req.header([('Content-type', map['mimetype']),
678 req.header([('Content-type', map['mimetype']),
671 ('Content-disposition', 'filename=%s' % map['file']),
679 ('Content-disposition', 'filename=%s' % map['file']),
672 ('Content-length', str(len(map['raw'])))])
680 ('Content-length', str(len(map['raw'])))])
673 yield ''
681 yield ''
674
682
675 def footer(**map):
683 def footer(**map):
676 yield self.t("footer",
684 yield self.t("footer",
677 motd=self.repo.ui.config("web", "motd", ""),
685 motd=self.repo.ui.config("web", "motd", ""),
678 **map)
686 **map)
679
687
680 def expand_form(form):
688 def expand_form(form):
681 shortcuts = {
689 shortcuts = {
682 'cl': [('cmd', ['changelog']), ('rev', None)],
690 'cl': [('cmd', ['changelog']), ('rev', None)],
683 'cs': [('cmd', ['changeset']), ('node', None)],
691 'cs': [('cmd', ['changeset']), ('node', None)],
684 'f': [('cmd', ['file']), ('filenode', None)],
692 'f': [('cmd', ['file']), ('filenode', None)],
685 'fl': [('cmd', ['filelog']), ('filenode', None)],
693 'fl': [('cmd', ['filelog']), ('filenode', None)],
686 'fd': [('cmd', ['filediff']), ('node', None)],
694 'fd': [('cmd', ['filediff']), ('node', None)],
687 'fa': [('cmd', ['annotate']), ('filenode', None)],
695 'fa': [('cmd', ['annotate']), ('filenode', None)],
688 'mf': [('cmd', ['manifest']), ('manifest', None)],
696 'mf': [('cmd', ['manifest']), ('manifest', None)],
689 'ca': [('cmd', ['archive']), ('node', None)],
697 'ca': [('cmd', ['archive']), ('node', None)],
690 'tags': [('cmd', ['tags'])],
698 'tags': [('cmd', ['tags'])],
691 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
699 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
692 'static': [('cmd', ['static']), ('file', None)]
700 'static': [('cmd', ['static']), ('file', None)]
693 }
701 }
694
702
695 for k in shortcuts.iterkeys():
703 for k in shortcuts.iterkeys():
696 if form.has_key(k):
704 if form.has_key(k):
697 for name, value in shortcuts[k]:
705 for name, value in shortcuts[k]:
698 if value is None:
706 if value is None:
699 value = form[k]
707 value = form[k]
700 form[name] = value
708 form[name] = value
701 del form[k]
709 del form[k]
702
710
703 self.refresh()
711 self.refresh()
704
712
705 expand_form(req.form)
713 expand_form(req.form)
706
714
707 m = os.path.join(self.templatepath, "map")
715 m = os.path.join(self.templatepath, "map")
708 style = self.repo.ui.config("web", "style", "")
716 style = self.repo.ui.config("web", "style", "")
709 if req.form.has_key('style'):
717 if req.form.has_key('style'):
710 style = req.form['style'][0]
718 style = req.form['style'][0]
711 if style:
719 if style:
712 b = os.path.basename("map-" + style)
720 b = os.path.basename("map-" + style)
713 p = os.path.join(self.templatepath, b)
721 p = os.path.join(self.templatepath, b)
714 if os.path.isfile(p):
722 if os.path.isfile(p):
715 m = p
723 m = p
716
724
717 port = req.env["SERVER_PORT"]
725 port = req.env["SERVER_PORT"]
718 port = port != "80" and (":" + port) or ""
726 port = port != "80" and (":" + port) or ""
719 uri = req.env["REQUEST_URI"]
727 uri = req.env["REQUEST_URI"]
720 if "?" in uri:
728 if "?" in uri:
721 uri = uri.split("?")[0]
729 uri = uri.split("?")[0]
722 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
730 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
723 if not self.reponame:
731 if not self.reponame:
724 self.reponame = (self.repo.ui.config("web", "name")
732 self.reponame = (self.repo.ui.config("web", "name")
725 or uri.strip('/') or self.repo.root)
733 or uri.strip('/') or self.repo.root)
726
734
727 self.t = templater.templater(m, templater.common_filters,
735 self.t = templater.templater(m, templater.common_filters,
728 defaults={"url": url,
736 defaults={"url": url,
729 "repo": self.reponame,
737 "repo": self.reponame,
730 "header": header,
738 "header": header,
731 "footer": footer,
739 "footer": footer,
732 "rawfileheader": rawfileheader,
740 "rawfileheader": rawfileheader,
733 })
741 })
734
742
735 if not req.form.has_key('cmd'):
743 if not req.form.has_key('cmd'):
736 req.form['cmd'] = [self.t.cache['default'],]
744 req.form['cmd'] = [self.t.cache['default'],]
737
745
738 cmd = req.form['cmd'][0]
746 cmd = req.form['cmd'][0]
739
747
740 method = getattr(self, 'do_' + cmd, None)
748 method = getattr(self, 'do_' + cmd, None)
741 if method:
749 if method:
742 method(req)
750 method(req)
743 else:
751 else:
744 req.write(self.t("error"))
752 req.write(self.t("error"))
745
753
746 def do_changelog(self, req):
754 def do_changelog(self, req):
747 hi = self.repo.changelog.count() - 1
755 hi = self.repo.changelog.count() - 1
748 if req.form.has_key('rev'):
756 if req.form.has_key('rev'):
749 hi = req.form['rev'][0]
757 hi = req.form['rev'][0]
750 try:
758 try:
751 hi = self.repo.changelog.rev(self.repo.lookup(hi))
759 hi = self.repo.changelog.rev(self.repo.lookup(hi))
752 except hg.RepoError:
760 except hg.RepoError:
753 req.write(self.search(hi)) # XXX redirect to 404 page?
761 req.write(self.search(hi)) # XXX redirect to 404 page?
754 return
762 return
755
763
756 req.write(self.changelog(hi))
764 req.write(self.changelog(hi))
757
765
758 def do_changeset(self, req):
766 def do_changeset(self, req):
759 req.write(self.changeset(req.form['node'][0]))
767 req.write(self.changeset(req.form['node'][0]))
760
768
761 def do_manifest(self, req):
769 def do_manifest(self, req):
762 req.write(self.manifest(req.form['manifest'][0],
770 req.write(self.manifest(req.form['manifest'][0],
763 self.cleanpath(req.form['path'][0])))
771 self.cleanpath(req.form['path'][0])))
764
772
765 def do_tags(self, req):
773 def do_tags(self, req):
766 req.write(self.tags())
774 req.write(self.tags())
767
775
768 def do_summary(self, req):
776 def do_summary(self, req):
769 req.write(self.summary())
777 req.write(self.summary())
770
778
771 def do_filediff(self, req):
779 def do_filediff(self, req):
772 req.write(self.filediff(self.cleanpath(req.form['file'][0]),
780 req.write(self.filediff(self.cleanpath(req.form['file'][0]),
773 req.form['node'][0]))
781 req.form['node'][0]))
774
782
775 def do_file(self, req):
783 def do_file(self, req):
776 req.write(self.filerevision(self.cleanpath(req.form['file'][0]),
784 req.write(self.filerevision(self.cleanpath(req.form['file'][0]),
777 req.form['filenode'][0]))
785 req.form['filenode'][0]))
778
786
779 def do_annotate(self, req):
787 def do_annotate(self, req):
780 req.write(self.fileannotate(self.cleanpath(req.form['file'][0]),
788 req.write(self.fileannotate(self.cleanpath(req.form['file'][0]),
781 req.form['filenode'][0]))
789 req.form['filenode'][0]))
782
790
783 def do_filelog(self, req):
791 def do_filelog(self, req):
784 req.write(self.filelog(self.cleanpath(req.form['file'][0]),
792 req.write(self.filelog(self.cleanpath(req.form['file'][0]),
785 req.form['filenode'][0]))
793 req.form['filenode'][0]))
786
794
787 def do_heads(self, req):
795 def do_heads(self, req):
788 resp = " ".join(map(hex, self.repo.heads())) + "\n"
796 resp = " ".join(map(hex, self.repo.heads())) + "\n"
789 req.httphdr("application/mercurial-0.1", length=len(resp))
797 req.httphdr("application/mercurial-0.1", length=len(resp))
790 req.write(resp)
798 req.write(resp)
791
799
792 def do_branches(self, req):
800 def do_branches(self, req):
793 nodes = []
801 nodes = []
794 if req.form.has_key('nodes'):
802 if req.form.has_key('nodes'):
795 nodes = map(bin, req.form['nodes'][0].split(" "))
803 nodes = map(bin, req.form['nodes'][0].split(" "))
796 resp = cStringIO.StringIO()
804 resp = cStringIO.StringIO()
797 for b in self.repo.branches(nodes):
805 for b in self.repo.branches(nodes):
798 resp.write(" ".join(map(hex, b)) + "\n")
806 resp.write(" ".join(map(hex, b)) + "\n")
799 resp = resp.getvalue()
807 resp = resp.getvalue()
800 req.httphdr("application/mercurial-0.1", length=len(resp))
808 req.httphdr("application/mercurial-0.1", length=len(resp))
801 req.write(resp)
809 req.write(resp)
802
810
803 def do_between(self, req):
811 def do_between(self, req):
804 nodes = []
812 nodes = []
805 if req.form.has_key('pairs'):
813 if req.form.has_key('pairs'):
806 pairs = [map(bin, p.split("-"))
814 pairs = [map(bin, p.split("-"))
807 for p in req.form['pairs'][0].split(" ")]
815 for p in req.form['pairs'][0].split(" ")]
808 resp = cStringIO.StringIO()
816 resp = cStringIO.StringIO()
809 for b in self.repo.between(pairs):
817 for b in self.repo.between(pairs):
810 resp.write(" ".join(map(hex, b)) + "\n")
818 resp.write(" ".join(map(hex, b)) + "\n")
811 resp = resp.getvalue()
819 resp = resp.getvalue()
812 req.httphdr("application/mercurial-0.1", length=len(resp))
820 req.httphdr("application/mercurial-0.1", length=len(resp))
813 req.write(resp)
821 req.write(resp)
814
822
815 def do_changegroup(self, req):
823 def do_changegroup(self, req):
816 req.httphdr("application/mercurial-0.1")
824 req.httphdr("application/mercurial-0.1")
817 nodes = []
825 nodes = []
818 if not self.allowpull:
826 if not self.allowpull:
819 return
827 return
820
828
821 if req.form.has_key('roots'):
829 if req.form.has_key('roots'):
822 nodes = map(bin, req.form['roots'][0].split(" "))
830 nodes = map(bin, req.form['roots'][0].split(" "))
823
831
824 z = zlib.compressobj()
832 z = zlib.compressobj()
825 f = self.repo.changegroup(nodes, 'serve')
833 f = self.repo.changegroup(nodes, 'serve')
826 while 1:
834 while 1:
827 chunk = f.read(4096)
835 chunk = f.read(4096)
828 if not chunk:
836 if not chunk:
829 break
837 break
830 req.write(z.compress(chunk))
838 req.write(z.compress(chunk))
831
839
832 req.write(z.flush())
840 req.write(z.flush())
833
841
834 def do_archive(self, req):
842 def do_archive(self, req):
835 changeset = self.repo.lookup(req.form['node'][0])
843 changeset = self.repo.lookup(req.form['node'][0])
836 type_ = req.form['type'][0]
844 type_ = req.form['type'][0]
837 allowed = self.repo.ui.configlist("web", "allow_archive")
845 allowed = self.repo.ui.configlist("web", "allow_archive")
838 if (type_ in self.archives and (type_ in allowed or
846 if (type_ in self.archives and (type_ in allowed or
839 self.repo.ui.configbool("web", "allow" + type_, False))):
847 self.repo.ui.configbool("web", "allow" + type_, False))):
840 self.archive(req, changeset, type_)
848 self.archive(req, changeset, type_)
841 return
849 return
842
850
843 req.write(self.t("error"))
851 req.write(self.t("error"))
844
852
845 def do_static(self, req):
853 def do_static(self, req):
846 fname = req.form['file'][0]
854 fname = req.form['file'][0]
847 static = self.repo.ui.config("web", "static",
855 static = self.repo.ui.config("web", "static",
848 os.path.join(self.templatepath,
856 os.path.join(self.templatepath,
849 "static"))
857 "static"))
850 req.write(staticfile(static, fname, req)
858 req.write(staticfile(static, fname, req)
851 or self.t("error", error="%r not found" % fname))
859 or self.t("error", error="%r not found" % fname))
852
860
853 def do_capabilities(self, req):
861 def do_capabilities(self, req):
854 resp = 'unbundle'
862 resp = 'unbundle'
855 req.httphdr("application/mercurial-0.1", length=len(resp))
863 req.httphdr("application/mercurial-0.1", length=len(resp))
856 req.write(resp)
864 req.write(resp)
857
865
858 def check_perm(self, req, op, default):
866 def check_perm(self, req, op, default):
859 '''check permission for operation based on user auth.
867 '''check permission for operation based on user auth.
860 return true if op allowed, else false.
868 return true if op allowed, else false.
861 default is policy to use if no config given.'''
869 default is policy to use if no config given.'''
862
870
863 user = req.env.get('REMOTE_USER')
871 user = req.env.get('REMOTE_USER')
864
872
865 deny = self.repo.ui.configlist('web', 'deny_' + op)
873 deny = self.repo.ui.configlist('web', 'deny_' + op)
866 if deny and (not user or deny == ['*'] or user in deny):
874 if deny and (not user or deny == ['*'] or user in deny):
867 return False
875 return False
868
876
869 allow = self.repo.ui.configlist('web', 'allow_' + op)
877 allow = self.repo.ui.configlist('web', 'allow_' + op)
870 return (allow and (allow == ['*'] or user in allow)) or default
878 return (allow and (allow == ['*'] or user in allow)) or default
871
879
872 def do_unbundle(self, req):
880 def do_unbundle(self, req):
873 def bail(response, headers={}):
881 def bail(response, headers={}):
874 length = int(req.env['CONTENT_LENGTH'])
882 length = int(req.env['CONTENT_LENGTH'])
875 for s in util.filechunkiter(req, limit=length):
883 for s in util.filechunkiter(req, limit=length):
876 # drain incoming bundle, else client will not see
884 # drain incoming bundle, else client will not see
877 # response when run outside cgi script
885 # response when run outside cgi script
878 pass
886 pass
879 req.httphdr("application/mercurial-0.1", headers=headers)
887 req.httphdr("application/mercurial-0.1", headers=headers)
880 req.write('0\n')
888 req.write('0\n')
881 req.write(response)
889 req.write(response)
882
890
883 # require ssl by default, auth info cannot be sniffed and
891 # require ssl by default, auth info cannot be sniffed and
884 # replayed
892 # replayed
885 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
893 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
886 if ssl_req and not req.env.get('HTTPS'):
894 if ssl_req and not req.env.get('HTTPS'):
887 bail(_('ssl required\n'))
895 bail(_('ssl required\n'))
888 return
896 return
889
897
890 # do not allow push unless explicitly allowed
898 # do not allow push unless explicitly allowed
891 if not self.check_perm(req, 'push', False):
899 if not self.check_perm(req, 'push', False):
892 bail(_('push not authorized\n'),
900 bail(_('push not authorized\n'),
893 headers={'status': '401 Unauthorized'})
901 headers={'status': '401 Unauthorized'})
894 return
902 return
895
903
896 req.httphdr("application/mercurial-0.1")
904 req.httphdr("application/mercurial-0.1")
897
905
898 their_heads = req.form['heads'][0].split(' ')
906 their_heads = req.form['heads'][0].split(' ')
899
907
900 def check_heads():
908 def check_heads():
901 heads = map(hex, self.repo.heads())
909 heads = map(hex, self.repo.heads())
902 return their_heads == [hex('force')] or their_heads == heads
910 return their_heads == [hex('force')] or their_heads == heads
903
911
904 # fail early if possible
912 # fail early if possible
905 if not check_heads():
913 if not check_heads():
906 bail(_('unsynced changes\n'))
914 bail(_('unsynced changes\n'))
907 return
915 return
908
916
909 # do not lock repo until all changegroup data is
917 # do not lock repo until all changegroup data is
910 # streamed. save to temporary file.
918 # streamed. save to temporary file.
911
919
912 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
920 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
913 fp = os.fdopen(fd, 'wb+')
921 fp = os.fdopen(fd, 'wb+')
914 try:
922 try:
915 length = int(req.env['CONTENT_LENGTH'])
923 length = int(req.env['CONTENT_LENGTH'])
916 for s in util.filechunkiter(req, limit=length):
924 for s in util.filechunkiter(req, limit=length):
917 fp.write(s)
925 fp.write(s)
918
926
919 lock = self.repo.lock()
927 lock = self.repo.lock()
920 try:
928 try:
921 if not check_heads():
929 if not check_heads():
922 req.write('0\n')
930 req.write('0\n')
923 req.write(_('unsynced changes\n'))
931 req.write(_('unsynced changes\n'))
924 return
932 return
925
933
926 fp.seek(0)
934 fp.seek(0)
927
935
928 # send addchangegroup output to client
936 # send addchangegroup output to client
929
937
930 old_stdout = sys.stdout
938 old_stdout = sys.stdout
931 sys.stdout = cStringIO.StringIO()
939 sys.stdout = cStringIO.StringIO()
932
940
933 try:
941 try:
934 ret = self.repo.addchangegroup(fp, 'serve')
942 ret = self.repo.addchangegroup(fp, 'serve')
935 finally:
943 finally:
936 val = sys.stdout.getvalue()
944 val = sys.stdout.getvalue()
937 sys.stdout = old_stdout
945 sys.stdout = old_stdout
938 req.write('%d\n' % ret)
946 req.write('%d\n' % ret)
939 req.write(val)
947 req.write(val)
940 finally:
948 finally:
941 lock.release()
949 lock.release()
942 finally:
950 finally:
943 fp.close()
951 fp.close()
944 os.unlink(tempname)
952 os.unlink(tempname)
@@ -1,205 +1,229 b''
1 # mdiff.py - diff and patch routines for mercurial
1 # mdiff.py - diff and patch routines for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 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 demandload import demandload
8 from demandload import demandload
9 import bdiff, mpatch
9 import bdiff, mpatch
10 demandload(globals(), "re struct util")
10 demandload(globals(), "re struct util")
11
11
12 def splitnewlines(text):
12 def splitnewlines(text):
13 '''like str.splitlines, but only split on newlines.'''
13 '''like str.splitlines, but only split on newlines.'''
14 lines = [l + '\n' for l in text.split('\n')]
14 lines = [l + '\n' for l in text.split('\n')]
15 if lines:
15 if lines:
16 if lines[-1] == '\n':
16 if lines[-1] == '\n':
17 lines.pop()
17 lines.pop()
18 else:
18 else:
19 lines[-1] = lines[-1][:-1]
19 lines[-1] = lines[-1][:-1]
20 return lines
20 return lines
21
21
22 def unidiff(a, ad, b, bd, fn, r=None, text=False,
22 def unidiff(a, ad, b, bd, fn, r=None, text=False,
23 showfunc=False, ignorews=False):
23 showfunc=False, ignorews=False, ignorewsamount=False,
24 ignoreblanklines=False):
24
25
25 if not a and not b: return ""
26 if not a and not b: return ""
26 epoch = util.datestr((0, 0))
27 epoch = util.datestr((0, 0))
27
28
28 if not text and (util.binary(a) or util.binary(b)):
29 if not text and (util.binary(a) or util.binary(b)):
29 l = ['Binary file %s has changed\n' % fn]
30 l = ['Binary file %s has changed\n' % fn]
30 elif not a:
31 elif not a:
31 b = splitnewlines(b)
32 b = splitnewlines(b)
32 if a is None:
33 if a is None:
33 l1 = "--- %s\t%s\n" % ("/dev/null", epoch)
34 l1 = "--- %s\t%s\n" % ("/dev/null", epoch)
34 else:
35 else:
35 l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
36 l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
36 l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
37 l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
37 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
38 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
38 l = [l1, l2, l3] + ["+" + e for e in b]
39 l = [l1, l2, l3] + ["+" + e for e in b]
39 elif not b:
40 elif not b:
40 a = splitnewlines(a)
41 a = splitnewlines(a)
41 l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
42 l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
42 if b is None:
43 if b is None:
43 l2 = "+++ %s\t%s\n" % ("/dev/null", epoch)
44 l2 = "+++ %s\t%s\n" % ("/dev/null", epoch)
44 else:
45 else:
45 l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
46 l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
46 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
47 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
47 l = [l1, l2, l3] + ["-" + e for e in a]
48 l = [l1, l2, l3] + ["-" + e for e in a]
48 else:
49 else:
49 al = splitnewlines(a)
50 al = splitnewlines(a)
50 bl = splitnewlines(b)
51 bl = splitnewlines(b)
51 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn,
52 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn,
52 showfunc=showfunc, ignorews=ignorews))
53 showfunc=showfunc, ignorews=ignorews,
54 ignorewsamount=ignorewsamount,
55 ignoreblanklines=ignoreblanklines))
53 if not l: return ""
56 if not l: return ""
54 # difflib uses a space, rather than a tab
57 # difflib uses a space, rather than a tab
55 l[0] = "%s\t%s\n" % (l[0][:-2], ad)
58 l[0] = "%s\t%s\n" % (l[0][:-2], ad)
56 l[1] = "%s\t%s\n" % (l[1][:-2], bd)
59 l[1] = "%s\t%s\n" % (l[1][:-2], bd)
57
60
58 for ln in xrange(len(l)):
61 for ln in xrange(len(l)):
59 if l[ln][-1] != '\n':
62 if l[ln][-1] != '\n':
60 l[ln] += "\n\ No newline at end of file\n"
63 l[ln] += "\n\ No newline at end of file\n"
61
64
62 if r:
65 if r:
63 l.insert(0, "diff %s %s\n" %
66 l.insert(0, "diff %s %s\n" %
64 (' '.join(["-r %s" % rev for rev in r]), fn))
67 (' '.join(["-r %s" % rev for rev in r]), fn))
65
68
66 return "".join(l)
69 return "".join(l)
67
70
68 # somewhat self contained replacement for difflib.unified_diff
71 # somewhat self contained replacement for difflib.unified_diff
69 # t1 and t2 are the text to be diffed
72 # t1 and t2 are the text to be diffed
70 # l1 and l2 are the text broken up into lines
73 # l1 and l2 are the text broken up into lines
71 # header1 and header2 are the filenames for the diff output
74 # header1 and header2 are the filenames for the diff output
72 # context is the number of context lines
75 # context is the number of context lines
73 # showfunc enables diff -p output
76 # showfunc enables diff -p output
74 # ignorews ignores all whitespace changes in the diff
77 # ignorews ignores all whitespace changes in the diff
78 # ignorewsamount ignores changes in the amount of whitespace
79 # ignoreblanklines ignores changes whose lines are all blank
75 def bunidiff(t1, t2, l1, l2, header1, header2, context=3, showfunc=False,
80 def bunidiff(t1, t2, l1, l2, header1, header2, context=3, showfunc=False,
76 ignorews=False):
81 ignorews=False, ignorewsamount=False, ignoreblanklines=False):
77 def contextend(l, len):
82 def contextend(l, len):
78 ret = l + context
83 ret = l + context
79 if ret > len:
84 if ret > len:
80 ret = len
85 ret = len
81 return ret
86 return ret
82
87
83 def contextstart(l):
88 def contextstart(l):
84 ret = l - context
89 ret = l - context
85 if ret < 0:
90 if ret < 0:
86 return 0
91 return 0
87 return ret
92 return ret
88
93
89 def yieldhunk(hunk, header):
94 def yieldhunk(hunk, header):
90 if header:
95 if header:
91 for x in header:
96 for x in header:
92 yield x
97 yield x
93 (astart, a2, bstart, b2, delta) = hunk
98 (astart, a2, bstart, b2, delta) = hunk
94 aend = contextend(a2, len(l1))
99 aend = contextend(a2, len(l1))
95 alen = aend - astart
100 alen = aend - astart
96 blen = b2 - bstart + aend - a2
101 blen = b2 - bstart + aend - a2
97
102
98 func = ""
103 func = ""
99 if showfunc:
104 if showfunc:
100 # walk backwards from the start of the context
105 # walk backwards from the start of the context
101 # to find a line starting with an alphanumeric char.
106 # to find a line starting with an alphanumeric char.
102 for x in xrange(astart, -1, -1):
107 for x in xrange(astart, -1, -1):
103 t = l1[x].rstrip()
108 t = l1[x].rstrip()
104 if funcre.match(t):
109 if funcre.match(t):
105 func = ' ' + t[:40]
110 func = ' ' + t[:40]
106 break
111 break
107
112
108 yield "@@ -%d,%d +%d,%d @@%s\n" % (astart + 1, alen,
113 yield "@@ -%d,%d +%d,%d @@%s\n" % (astart + 1, alen,
109 bstart + 1, blen, func)
114 bstart + 1, blen, func)
110 for x in delta:
115 for x in delta:
111 yield x
116 yield x
112 for x in xrange(a2, aend):
117 for x in xrange(a2, aend):
113 yield ' ' + l1[x]
118 yield ' ' + l1[x]
114
119
115 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ]
120 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ]
116
121
117 if showfunc:
122 if showfunc:
118 funcre = re.compile('\w')
123 funcre = re.compile('\w')
124 if ignorewsamount:
125 wsamountre = re.compile('[ \t]+')
126 wsappendedre = re.compile(' \n')
127 if ignoreblanklines:
128 wsblanklinesre = re.compile('\n')
119 if ignorews:
129 if ignorews:
120 wsre = re.compile('[ \t]')
130 wsre = re.compile('[ \t]')
121
131
122 # bdiff.blocks gives us the matching sequences in the files. The loop
132 # bdiff.blocks gives us the matching sequences in the files. The loop
123 # below finds the spaces between those matching sequences and translates
133 # below finds the spaces between those matching sequences and translates
124 # them into diff output.
134 # them into diff output.
125 #
135 #
126 diff = bdiff.blocks(t1, t2)
136 diff = bdiff.blocks(t1, t2)
127 hunk = None
137 hunk = None
128 for i in xrange(len(diff)):
138 for i in xrange(len(diff)):
129 # The first match is special.
139 # The first match is special.
130 # we've either found a match starting at line 0 or a match later
140 # we've either found a match starting at line 0 or a match later
131 # in the file. If it starts later, old and new below will both be
141 # in the file. If it starts later, old and new below will both be
132 # empty and we'll continue to the next match.
142 # empty and we'll continue to the next match.
133 if i > 0:
143 if i > 0:
134 s = diff[i-1]
144 s = diff[i-1]
135 else:
145 else:
136 s = [0, 0, 0, 0]
146 s = [0, 0, 0, 0]
137 delta = []
147 delta = []
138 s1 = diff[i]
148 s1 = diff[i]
139 a1 = s[1]
149 a1 = s[1]
140 a2 = s1[0]
150 a2 = s1[0]
141 b1 = s[3]
151 b1 = s[3]
142 b2 = s1[2]
152 b2 = s1[2]
143
153
144 old = l1[a1:a2]
154 old = l1[a1:a2]
145 new = l2[b1:b2]
155 new = l2[b1:b2]
146
156
147 # bdiff sometimes gives huge matches past eof, this check eats them,
157 # bdiff sometimes gives huge matches past eof, this check eats them,
148 # and deals with the special first match case described above
158 # and deals with the special first match case described above
149 if not old and not new:
159 if not old and not new:
150 continue
160 continue
151
161
162 if ignoreblanklines:
163 wsold = wsblanklinesre.sub('', "".join(old))
164 wsnew = wsblanklinesre.sub('', "".join(new))
165 if wsold == wsnew:
166 continue
167
168 if ignorewsamount:
169 wsold = wsamountre.sub(' ', "".join(old))
170 wsold = wsappendedre.sub('\n', wsold)
171 wsnew = wsamountre.sub(' ', "".join(new))
172 wsnew = wsappendedre.sub('\n', wsnew)
173 if wsold == wsnew:
174 continue
175
152 if ignorews:
176 if ignorews:
153 wsold = wsre.sub('', "".join(old))
177 wsold = wsre.sub('', "".join(old))
154 wsnew = wsre.sub('', "".join(new))
178 wsnew = wsre.sub('', "".join(new))
155 if wsold == wsnew:
179 if wsold == wsnew:
156 continue
180 continue
157
181
158 astart = contextstart(a1)
182 astart = contextstart(a1)
159 bstart = contextstart(b1)
183 bstart = contextstart(b1)
160 prev = None
184 prev = None
161 if hunk:
185 if hunk:
162 # join with the previous hunk if it falls inside the context
186 # join with the previous hunk if it falls inside the context
163 if astart < hunk[1] + context + 1:
187 if astart < hunk[1] + context + 1:
164 prev = hunk
188 prev = hunk
165 astart = hunk[1]
189 astart = hunk[1]
166 bstart = hunk[3]
190 bstart = hunk[3]
167 else:
191 else:
168 for x in yieldhunk(hunk, header):
192 for x in yieldhunk(hunk, header):
169 yield x
193 yield x
170 # we only want to yield the header if the files differ, and
194 # we only want to yield the header if the files differ, and
171 # we only want to yield it once.
195 # we only want to yield it once.
172 header = None
196 header = None
173 if prev:
197 if prev:
174 # we've joined the previous hunk, record the new ending points.
198 # we've joined the previous hunk, record the new ending points.
175 hunk[1] = a2
199 hunk[1] = a2
176 hunk[3] = b2
200 hunk[3] = b2
177 delta = hunk[4]
201 delta = hunk[4]
178 else:
202 else:
179 # create a new hunk
203 # create a new hunk
180 hunk = [ astart, a2, bstart, b2, delta ]
204 hunk = [ astart, a2, bstart, b2, delta ]
181
205
182 delta[len(delta):] = [ ' ' + x for x in l1[astart:a1] ]
206 delta[len(delta):] = [ ' ' + x for x in l1[astart:a1] ]
183 delta[len(delta):] = [ '-' + x for x in old ]
207 delta[len(delta):] = [ '-' + x for x in old ]
184 delta[len(delta):] = [ '+' + x for x in new ]
208 delta[len(delta):] = [ '+' + x for x in new ]
185
209
186 if hunk:
210 if hunk:
187 for x in yieldhunk(hunk, header):
211 for x in yieldhunk(hunk, header):
188 yield x
212 yield x
189
213
190 def patchtext(bin):
214 def patchtext(bin):
191 pos = 0
215 pos = 0
192 t = []
216 t = []
193 while pos < len(bin):
217 while pos < len(bin):
194 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
218 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
195 pos += 12
219 pos += 12
196 t.append(bin[pos:pos + l])
220 t.append(bin[pos:pos + l])
197 pos += l
221 pos += l
198 return "".join(t)
222 return "".join(t)
199
223
200 def patch(a, bin):
224 def patch(a, bin):
201 return mpatch.patches(a, [bin])
225 return mpatch.patches(a, [bin])
202
226
203 patches = mpatch.patches
227 patches = mpatch.patches
204 patchedsize = mpatch.patchedsize
228 patchedsize = mpatch.patchedsize
205 textdiff = bdiff.bdiff
229 textdiff = bdiff.bdiff
@@ -1,355 +1,356 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 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 i18n import gettext as _
8 from i18n import gettext as _
9 from demandload import *
9 from demandload import *
10 demandload(globals(), "errno getpass os re smtplib socket sys tempfile")
10 demandload(globals(), "errno getpass os re smtplib socket sys tempfile")
11 demandload(globals(), "ConfigParser templater traceback util")
11 demandload(globals(), "ConfigParser templater traceback util")
12
12
13 class ui(object):
13 class ui(object):
14 def __init__(self, verbose=False, debug=False, quiet=False,
14 def __init__(self, verbose=False, debug=False, quiet=False,
15 interactive=True, traceback=False, parentui=None):
15 interactive=True, traceback=False, parentui=None):
16 self.overlay = {}
16 self.overlay = {}
17 if parentui is None:
17 if parentui is None:
18 # this is the parent of all ui children
18 # this is the parent of all ui children
19 self.parentui = None
19 self.parentui = None
20 self.cdata = ConfigParser.SafeConfigParser()
20 self.cdata = ConfigParser.SafeConfigParser()
21 self.readconfig(util.rcpath())
21 self.readconfig(util.rcpath())
22
22
23 self.quiet = self.configbool("ui", "quiet")
23 self.quiet = self.configbool("ui", "quiet")
24 self.verbose = self.configbool("ui", "verbose")
24 self.verbose = self.configbool("ui", "verbose")
25 self.debugflag = self.configbool("ui", "debug")
25 self.debugflag = self.configbool("ui", "debug")
26 self.interactive = self.configbool("ui", "interactive", True)
26 self.interactive = self.configbool("ui", "interactive", True)
27 self.traceback = traceback
27 self.traceback = traceback
28
28
29 self.updateopts(verbose, debug, quiet, interactive)
29 self.updateopts(verbose, debug, quiet, interactive)
30 self.diffcache = None
30 self.diffcache = None
31 self.header = []
31 self.header = []
32 self.prev_header = []
32 self.prev_header = []
33 self.revlogopts = self.configrevlog()
33 self.revlogopts = self.configrevlog()
34 else:
34 else:
35 # parentui may point to an ui object which is already a child
35 # parentui may point to an ui object which is already a child
36 self.parentui = parentui.parentui or parentui
36 self.parentui = parentui.parentui or parentui
37 parent_cdata = self.parentui.cdata
37 parent_cdata = self.parentui.cdata
38 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
38 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
39 # make interpolation work
39 # make interpolation work
40 for section in parent_cdata.sections():
40 for section in parent_cdata.sections():
41 self.cdata.add_section(section)
41 self.cdata.add_section(section)
42 for name, value in parent_cdata.items(section, raw=True):
42 for name, value in parent_cdata.items(section, raw=True):
43 self.cdata.set(section, name, value)
43 self.cdata.set(section, name, value)
44
44
45 def __getattr__(self, key):
45 def __getattr__(self, key):
46 return getattr(self.parentui, key)
46 return getattr(self.parentui, key)
47
47
48 def updateopts(self, verbose=False, debug=False, quiet=False,
48 def updateopts(self, verbose=False, debug=False, quiet=False,
49 interactive=True, traceback=False, config=[]):
49 interactive=True, traceback=False, config=[]):
50 self.quiet = (self.quiet or quiet) and not verbose and not debug
50 self.quiet = (self.quiet or quiet) and not verbose and not debug
51 self.verbose = (self.verbose or verbose) or debug
51 self.verbose = (self.verbose or verbose) or debug
52 self.debugflag = (self.debugflag or debug)
52 self.debugflag = (self.debugflag or debug)
53 self.interactive = (self.interactive and interactive)
53 self.interactive = (self.interactive and interactive)
54 self.traceback = self.traceback or traceback
54 self.traceback = self.traceback or traceback
55 for cfg in config:
55 for cfg in config:
56 try:
56 try:
57 name, value = cfg.split('=', 1)
57 name, value = cfg.split('=', 1)
58 section, name = name.split('.', 1)
58 section, name = name.split('.', 1)
59 if not self.cdata.has_section(section):
59 if not self.cdata.has_section(section):
60 self.cdata.add_section(section)
60 self.cdata.add_section(section)
61 if not section or not name:
61 if not section or not name:
62 raise IndexError
62 raise IndexError
63 self.cdata.set(section, name, value)
63 self.cdata.set(section, name, value)
64 except (IndexError, ValueError):
64 except (IndexError, ValueError):
65 raise util.Abort(_('malformed --config option: %s') % cfg)
65 raise util.Abort(_('malformed --config option: %s') % cfg)
66
66
67 def readconfig(self, fn, root=None):
67 def readconfig(self, fn, root=None):
68 if isinstance(fn, basestring):
68 if isinstance(fn, basestring):
69 fn = [fn]
69 fn = [fn]
70 for f in fn:
70 for f in fn:
71 try:
71 try:
72 self.cdata.read(f)
72 self.cdata.read(f)
73 except ConfigParser.ParsingError, inst:
73 except ConfigParser.ParsingError, inst:
74 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
74 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
75 # translate paths relative to root (or home) into absolute paths
75 # translate paths relative to root (or home) into absolute paths
76 if root is None:
76 if root is None:
77 root = os.path.expanduser('~')
77 root = os.path.expanduser('~')
78 for name, path in self.configitems("paths"):
78 for name, path in self.configitems("paths"):
79 if path and "://" not in path and not os.path.isabs(path):
79 if path and "://" not in path and not os.path.isabs(path):
80 self.cdata.set("paths", name, os.path.join(root, path))
80 self.cdata.set("paths", name, os.path.join(root, path))
81
81
82 def setconfig(self, section, name, val):
82 def setconfig(self, section, name, val):
83 self.overlay[(section, name)] = val
83 self.overlay[(section, name)] = val
84
84
85 def config(self, section, name, default=None):
85 def config(self, section, name, default=None):
86 if self.overlay.has_key((section, name)):
86 if self.overlay.has_key((section, name)):
87 return self.overlay[(section, name)]
87 return self.overlay[(section, name)]
88 if self.cdata.has_option(section, name):
88 if self.cdata.has_option(section, name):
89 try:
89 try:
90 return self.cdata.get(section, name)
90 return self.cdata.get(section, name)
91 except ConfigParser.InterpolationError, inst:
91 except ConfigParser.InterpolationError, inst:
92 raise util.Abort(_("Error in configuration:\n%s") % inst)
92 raise util.Abort(_("Error in configuration:\n%s") % inst)
93 if self.parentui is None:
93 if self.parentui is None:
94 return default
94 return default
95 else:
95 else:
96 return self.parentui.config(section, name, default)
96 return self.parentui.config(section, name, default)
97
97
98 def configlist(self, section, name, default=None):
98 def configlist(self, section, name, default=None):
99 """Return a list of comma/space separated strings"""
99 """Return a list of comma/space separated strings"""
100 result = self.config(section, name)
100 result = self.config(section, name)
101 if result is None:
101 if result is None:
102 result = default or []
102 result = default or []
103 if isinstance(result, basestring):
103 if isinstance(result, basestring):
104 result = result.replace(",", " ").split()
104 result = result.replace(",", " ").split()
105 return result
105 return result
106
106
107 def configbool(self, section, name, default=False):
107 def configbool(self, section, name, default=False):
108 if self.overlay.has_key((section, name)):
108 if self.overlay.has_key((section, name)):
109 return self.overlay[(section, name)]
109 return self.overlay[(section, name)]
110 if self.cdata.has_option(section, name):
110 if self.cdata.has_option(section, name):
111 try:
111 try:
112 return self.cdata.getboolean(section, name)
112 return self.cdata.getboolean(section, name)
113 except ConfigParser.InterpolationError, inst:
113 except ConfigParser.InterpolationError, inst:
114 raise util.Abort(_("Error in configuration:\n%s") % inst)
114 raise util.Abort(_("Error in configuration:\n%s") % inst)
115 if self.parentui is None:
115 if self.parentui is None:
116 return default
116 return default
117 else:
117 else:
118 return self.parentui.configbool(section, name, default)
118 return self.parentui.configbool(section, name, default)
119
119
120 def has_config(self, section):
120 def has_config(self, section):
121 '''tell whether section exists in config.'''
121 '''tell whether section exists in config.'''
122 return self.cdata.has_section(section)
122 return self.cdata.has_section(section)
123
123
124 def configitems(self, section):
124 def configitems(self, section):
125 items = {}
125 items = {}
126 if self.parentui is not None:
126 if self.parentui is not None:
127 items = dict(self.parentui.configitems(section))
127 items = dict(self.parentui.configitems(section))
128 if self.cdata.has_section(section):
128 if self.cdata.has_section(section):
129 try:
129 try:
130 items.update(dict(self.cdata.items(section)))
130 items.update(dict(self.cdata.items(section)))
131 except ConfigParser.InterpolationError, inst:
131 except ConfigParser.InterpolationError, inst:
132 raise util.Abort(_("Error in configuration:\n%s") % inst)
132 raise util.Abort(_("Error in configuration:\n%s") % inst)
133 x = items.items()
133 x = items.items()
134 x.sort()
134 x.sort()
135 return x
135 return x
136
136
137 def walkconfig(self, seen=None):
137 def walkconfig(self, seen=None):
138 if seen is None:
138 if seen is None:
139 seen = {}
139 seen = {}
140 for (section, name), value in self.overlay.iteritems():
140 for (section, name), value in self.overlay.iteritems():
141 yield section, name, value
141 yield section, name, value
142 seen[section, name] = 1
142 seen[section, name] = 1
143 for section in self.cdata.sections():
143 for section in self.cdata.sections():
144 for name, value in self.cdata.items(section):
144 for name, value in self.cdata.items(section):
145 if (section, name) in seen: continue
145 if (section, name) in seen: continue
146 yield section, name, value.replace('\n', '\\n')
146 yield section, name, value.replace('\n', '\\n')
147 seen[section, name] = 1
147 seen[section, name] = 1
148 if self.parentui is not None:
148 if self.parentui is not None:
149 for parent in self.parentui.walkconfig(seen):
149 for parent in self.parentui.walkconfig(seen):
150 yield parent
150 yield parent
151
151
152 def extensions(self):
152 def extensions(self):
153 result = self.configitems("extensions")
153 result = self.configitems("extensions")
154 for i, (key, value) in enumerate(result):
154 for i, (key, value) in enumerate(result):
155 if value:
155 if value:
156 result[i] = (key, os.path.expanduser(value))
156 result[i] = (key, os.path.expanduser(value))
157 return result
157 return result
158
158
159 def hgignorefiles(self):
159 def hgignorefiles(self):
160 result = []
160 result = []
161 for key, value in self.configitems("ui"):
161 for key, value in self.configitems("ui"):
162 if key == 'ignore' or key.startswith('ignore.'):
162 if key == 'ignore' or key.startswith('ignore.'):
163 result.append(os.path.expanduser(value))
163 result.append(os.path.expanduser(value))
164 return result
164 return result
165
165
166 def configrevlog(self):
166 def configrevlog(self):
167 result = {}
167 result = {}
168 for key, value in self.configitems("revlog"):
168 for key, value in self.configitems("revlog"):
169 result[key.lower()] = value
169 result[key.lower()] = value
170 return result
170 return result
171
171
172 def diffopts(self):
172 def diffopts(self):
173 if self.diffcache:
173 if self.diffcache:
174 return self.diffcache
174 return self.diffcache
175 result = {'showfunc': True, 'ignorews': False}
175 result = {'showfunc': True, 'ignorews': False,
176 'ignorewsamount': False, 'ignoreblanklines': False}
176 for key, value in self.configitems("diff"):
177 for key, value in self.configitems("diff"):
177 if value:
178 if value:
178 result[key.lower()] = (value.lower() == 'true')
179 result[key.lower()] = (value.lower() == 'true')
179 self.diffcache = result
180 self.diffcache = result
180 return result
181 return result
181
182
182 def username(self):
183 def username(self):
183 """Return default username to be used in commits.
184 """Return default username to be used in commits.
184
185
185 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
186 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
186 and stop searching if one of these is set.
187 and stop searching if one of these is set.
187 Abort if found username is an empty string to force specifying
188 Abort if found username is an empty string to force specifying
188 the commit user elsewhere, e.g. with line option or repo hgrc.
189 the commit user elsewhere, e.g. with line option or repo hgrc.
189 If not found, use ($LOGNAME or $USER or $LNAME or
190 If not found, use ($LOGNAME or $USER or $LNAME or
190 $USERNAME) +"@full.hostname".
191 $USERNAME) +"@full.hostname".
191 """
192 """
192 user = os.environ.get("HGUSER")
193 user = os.environ.get("HGUSER")
193 if user is None:
194 if user is None:
194 user = self.config("ui", "username")
195 user = self.config("ui", "username")
195 if user is None:
196 if user is None:
196 user = os.environ.get("EMAIL")
197 user = os.environ.get("EMAIL")
197 if user is None:
198 if user is None:
198 try:
199 try:
199 user = '%s@%s' % (getpass.getuser(), socket.getfqdn())
200 user = '%s@%s' % (getpass.getuser(), socket.getfqdn())
200 except KeyError:
201 except KeyError:
201 raise util.Abort(_("Please specify a username."))
202 raise util.Abort(_("Please specify a username."))
202 return user
203 return user
203
204
204 def shortuser(self, user):
205 def shortuser(self, user):
205 """Return a short representation of a user name or email address."""
206 """Return a short representation of a user name or email address."""
206 if not self.verbose: user = util.shortuser(user)
207 if not self.verbose: user = util.shortuser(user)
207 return user
208 return user
208
209
209 def expandpath(self, loc, default=None):
210 def expandpath(self, loc, default=None):
210 """Return repository location relative to cwd or from [paths]"""
211 """Return repository location relative to cwd or from [paths]"""
211 if "://" in loc or os.path.exists(loc):
212 if "://" in loc or os.path.exists(loc):
212 return loc
213 return loc
213
214
214 path = self.config("paths", loc)
215 path = self.config("paths", loc)
215 if not path and default is not None:
216 if not path and default is not None:
216 path = self.config("paths", default)
217 path = self.config("paths", default)
217 return path or loc
218 return path or loc
218
219
219 def write(self, *args):
220 def write(self, *args):
220 if self.header:
221 if self.header:
221 if self.header != self.prev_header:
222 if self.header != self.prev_header:
222 self.prev_header = self.header
223 self.prev_header = self.header
223 self.write(*self.header)
224 self.write(*self.header)
224 self.header = []
225 self.header = []
225 for a in args:
226 for a in args:
226 sys.stdout.write(str(a))
227 sys.stdout.write(str(a))
227
228
228 def write_header(self, *args):
229 def write_header(self, *args):
229 for a in args:
230 for a in args:
230 self.header.append(str(a))
231 self.header.append(str(a))
231
232
232 def write_err(self, *args):
233 def write_err(self, *args):
233 try:
234 try:
234 if not sys.stdout.closed: sys.stdout.flush()
235 if not sys.stdout.closed: sys.stdout.flush()
235 for a in args:
236 for a in args:
236 sys.stderr.write(str(a))
237 sys.stderr.write(str(a))
237 except IOError, inst:
238 except IOError, inst:
238 if inst.errno != errno.EPIPE:
239 if inst.errno != errno.EPIPE:
239 raise
240 raise
240
241
241 def flush(self):
242 def flush(self):
242 try: sys.stdout.flush()
243 try: sys.stdout.flush()
243 except: pass
244 except: pass
244 try: sys.stderr.flush()
245 try: sys.stderr.flush()
245 except: pass
246 except: pass
246
247
247 def readline(self):
248 def readline(self):
248 return sys.stdin.readline()[:-1]
249 return sys.stdin.readline()[:-1]
249 def prompt(self, msg, pat=None, default="y"):
250 def prompt(self, msg, pat=None, default="y"):
250 if not self.interactive: return default
251 if not self.interactive: return default
251 while 1:
252 while 1:
252 self.write(msg, " ")
253 self.write(msg, " ")
253 r = self.readline()
254 r = self.readline()
254 if not pat or re.match(pat, r):
255 if not pat or re.match(pat, r):
255 return r
256 return r
256 else:
257 else:
257 self.write(_("unrecognized response\n"))
258 self.write(_("unrecognized response\n"))
258 def getpass(self, prompt=None, default=None):
259 def getpass(self, prompt=None, default=None):
259 if not self.interactive: return default
260 if not self.interactive: return default
260 return getpass.getpass(prompt or _('password: '))
261 return getpass.getpass(prompt or _('password: '))
261 def status(self, *msg):
262 def status(self, *msg):
262 if not self.quiet: self.write(*msg)
263 if not self.quiet: self.write(*msg)
263 def warn(self, *msg):
264 def warn(self, *msg):
264 self.write_err(*msg)
265 self.write_err(*msg)
265 def note(self, *msg):
266 def note(self, *msg):
266 if self.verbose: self.write(*msg)
267 if self.verbose: self.write(*msg)
267 def debug(self, *msg):
268 def debug(self, *msg):
268 if self.debugflag: self.write(*msg)
269 if self.debugflag: self.write(*msg)
269 def edit(self, text, user):
270 def edit(self, text, user):
270 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
271 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
271 text=True)
272 text=True)
272 try:
273 try:
273 f = os.fdopen(fd, "w")
274 f = os.fdopen(fd, "w")
274 f.write(text)
275 f.write(text)
275 f.close()
276 f.close()
276
277
277 editor = (os.environ.get("HGEDITOR") or
278 editor = (os.environ.get("HGEDITOR") or
278 self.config("ui", "editor") or
279 self.config("ui", "editor") or
279 os.environ.get("EDITOR", "vi"))
280 os.environ.get("EDITOR", "vi"))
280
281
281 util.system("%s \"%s\"" % (editor, name),
282 util.system("%s \"%s\"" % (editor, name),
282 environ={'HGUSER': user},
283 environ={'HGUSER': user},
283 onerr=util.Abort, errprefix=_("edit failed"))
284 onerr=util.Abort, errprefix=_("edit failed"))
284
285
285 f = open(name)
286 f = open(name)
286 t = f.read()
287 t = f.read()
287 f.close()
288 f.close()
288 t = re.sub("(?m)^HG:.*\n", "", t)
289 t = re.sub("(?m)^HG:.*\n", "", t)
289 finally:
290 finally:
290 os.unlink(name)
291 os.unlink(name)
291
292
292 return t
293 return t
293
294
294 def sendmail(self):
295 def sendmail(self):
295 '''send mail message. object returned has one method, sendmail.
296 '''send mail message. object returned has one method, sendmail.
296 call as sendmail(sender, list-of-recipients, msg).'''
297 call as sendmail(sender, list-of-recipients, msg).'''
297
298
298 def smtp():
299 def smtp():
299 '''send mail using smtp.'''
300 '''send mail using smtp.'''
300
301
301 s = smtplib.SMTP()
302 s = smtplib.SMTP()
302 mailhost = self.config('smtp', 'host')
303 mailhost = self.config('smtp', 'host')
303 if not mailhost:
304 if not mailhost:
304 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
305 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
305 mailport = int(self.config('smtp', 'port', 25))
306 mailport = int(self.config('smtp', 'port', 25))
306 self.note(_('sending mail: smtp host %s, port %s\n') %
307 self.note(_('sending mail: smtp host %s, port %s\n') %
307 (mailhost, mailport))
308 (mailhost, mailport))
308 s.connect(host=mailhost, port=mailport)
309 s.connect(host=mailhost, port=mailport)
309 if self.configbool('smtp', 'tls'):
310 if self.configbool('smtp', 'tls'):
310 self.note(_('(using tls)\n'))
311 self.note(_('(using tls)\n'))
311 s.ehlo()
312 s.ehlo()
312 s.starttls()
313 s.starttls()
313 s.ehlo()
314 s.ehlo()
314 username = self.config('smtp', 'username')
315 username = self.config('smtp', 'username')
315 password = self.config('smtp', 'password')
316 password = self.config('smtp', 'password')
316 if username and password:
317 if username and password:
317 self.note(_('(authenticating to mail server as %s)\n') %
318 self.note(_('(authenticating to mail server as %s)\n') %
318 (username))
319 (username))
319 s.login(username, password)
320 s.login(username, password)
320 return s
321 return s
321
322
322 class sendmail(object):
323 class sendmail(object):
323 '''send mail using sendmail.'''
324 '''send mail using sendmail.'''
324
325
325 def __init__(self, ui, program):
326 def __init__(self, ui, program):
326 self.ui = ui
327 self.ui = ui
327 self.program = program
328 self.program = program
328
329
329 def sendmail(self, sender, recipients, msg):
330 def sendmail(self, sender, recipients, msg):
330 cmdline = '%s -f %s %s' % (
331 cmdline = '%s -f %s %s' % (
331 self.program, templater.email(sender),
332 self.program, templater.email(sender),
332 ' '.join(map(templater.email, recipients)))
333 ' '.join(map(templater.email, recipients)))
333 self.ui.note(_('sending mail: %s\n') % cmdline)
334 self.ui.note(_('sending mail: %s\n') % cmdline)
334 fp = os.popen(cmdline, 'w')
335 fp = os.popen(cmdline, 'w')
335 fp.write(msg)
336 fp.write(msg)
336 ret = fp.close()
337 ret = fp.close()
337 if ret:
338 if ret:
338 raise util.Abort('%s %s' % (
339 raise util.Abort('%s %s' % (
339 os.path.basename(self.program.split(None, 1)[0]),
340 os.path.basename(self.program.split(None, 1)[0]),
340 util.explain_exit(ret)[0]))
341 util.explain_exit(ret)[0]))
341
342
342 method = self.config('email', 'method', 'smtp')
343 method = self.config('email', 'method', 'smtp')
343 if method == 'smtp':
344 if method == 'smtp':
344 mail = smtp()
345 mail = smtp()
345 else:
346 else:
346 mail = sendmail(self, method)
347 mail = sendmail(self, method)
347 return mail
348 return mail
348
349
349 def print_exc(self):
350 def print_exc(self):
350 '''print exception traceback if traceback printing enabled.
351 '''print exception traceback if traceback printing enabled.
351 only to call in exception handler. returns true if traceback
352 only to call in exception handler. returns true if traceback
352 printed.'''
353 printed.'''
353 if self.traceback:
354 if self.traceback:
354 traceback.print_exc()
355 traceback.print_exc()
355 return self.traceback
356 return self.traceback
@@ -1,255 +1,257 b''
1 Mercurial Distributed SCM
1 Mercurial Distributed SCM
2
2
3 basic commands (use "hg help" for the full list or option "-v" for details):
3 basic commands (use "hg help" for the full list or option "-v" for details):
4
4
5 add add the specified files on the next commit
5 add add the specified files on the next commit
6 annotate show changeset information per file line
6 annotate show changeset information per file line
7 clone make a copy of an existing repository
7 clone make a copy of an existing repository
8 commit commit the specified files or all outstanding changes
8 commit commit the specified files or all outstanding changes
9 diff diff repository (or selected files)
9 diff diff repository (or selected files)
10 export dump the header and diffs for one or more changesets
10 export dump the header and diffs for one or more changesets
11 init create a new repository in the given directory
11 init create a new repository in the given directory
12 log show revision history of entire repository or files
12 log show revision history of entire repository or files
13 parents show the parents of the working dir or revision
13 parents show the parents of the working dir or revision
14 pull pull changes from the specified source
14 pull pull changes from the specified source
15 push push changes to the specified destination
15 push push changes to the specified destination
16 remove remove the specified files on the next commit
16 remove remove the specified files on the next commit
17 revert revert files or dirs to their states as of some revision
17 revert revert files or dirs to their states as of some revision
18 serve export the repository via HTTP
18 serve export the repository via HTTP
19 status show changed files in the working directory
19 status show changed files in the working directory
20 update update or merge working directory
20 update update or merge working directory
21 add add the specified files on the next commit
21 add add the specified files on the next commit
22 annotate show changeset information per file line
22 annotate show changeset information per file line
23 clone make a copy of an existing repository
23 clone make a copy of an existing repository
24 commit commit the specified files or all outstanding changes
24 commit commit the specified files or all outstanding changes
25 diff diff repository (or selected files)
25 diff diff repository (or selected files)
26 export dump the header and diffs for one or more changesets
26 export dump the header and diffs for one or more changesets
27 init create a new repository in the given directory
27 init create a new repository in the given directory
28 log show revision history of entire repository or files
28 log show revision history of entire repository or files
29 parents show the parents of the working dir or revision
29 parents show the parents of the working dir or revision
30 pull pull changes from the specified source
30 pull pull changes from the specified source
31 push push changes to the specified destination
31 push push changes to the specified destination
32 remove remove the specified files on the next commit
32 remove remove the specified files on the next commit
33 revert revert files or dirs to their states as of some revision
33 revert revert files or dirs to their states as of some revision
34 serve export the repository via HTTP
34 serve export the repository via HTTP
35 status show changed files in the working directory
35 status show changed files in the working directory
36 update update or merge working directory
36 update update or merge working directory
37 Mercurial Distributed SCM
37 Mercurial Distributed SCM
38
38
39 list of commands (use "hg help -v" to show aliases and global options):
39 list of commands (use "hg help -v" to show aliases and global options):
40
40
41 add add the specified files on the next commit
41 add add the specified files on the next commit
42 annotate show changeset information per file line
42 annotate show changeset information per file line
43 archive create unversioned archive of a repository revision
43 archive create unversioned archive of a repository revision
44 backout reverse effect of earlier changeset
44 backout reverse effect of earlier changeset
45 bundle create a changegroup file
45 bundle create a changegroup file
46 cat output the latest or given revisions of files
46 cat output the latest or given revisions of files
47 clone make a copy of an existing repository
47 clone make a copy of an existing repository
48 commit commit the specified files or all outstanding changes
48 commit commit the specified files or all outstanding changes
49 copy mark files as copied for the next commit
49 copy mark files as copied for the next commit
50 diff diff repository (or selected files)
50 diff diff repository (or selected files)
51 export dump the header and diffs for one or more changesets
51 export dump the header and diffs for one or more changesets
52 grep search for a pattern in specified files and revisions
52 grep search for a pattern in specified files and revisions
53 heads show current repository heads
53 heads show current repository heads
54 help show help for a command, extension, or list of commands
54 help show help for a command, extension, or list of commands
55 identify print information about the working copy
55 identify print information about the working copy
56 import import an ordered set of patches
56 import import an ordered set of patches
57 incoming show new changesets found in source
57 incoming show new changesets found in source
58 init create a new repository in the given directory
58 init create a new repository in the given directory
59 locate locate files matching specific patterns
59 locate locate files matching specific patterns
60 log show revision history of entire repository or files
60 log show revision history of entire repository or files
61 manifest output the latest or given revision of the project manifest
61 manifest output the latest or given revision of the project manifest
62 merge Merge working directory with another revision
62 merge Merge working directory with another revision
63 outgoing show changesets not found in destination
63 outgoing show changesets not found in destination
64 parents show the parents of the working dir or revision
64 parents show the parents of the working dir or revision
65 paths show definition of symbolic path names
65 paths show definition of symbolic path names
66 pull pull changes from the specified source
66 pull pull changes from the specified source
67 push push changes to the specified destination
67 push push changes to the specified destination
68 recover roll back an interrupted transaction
68 recover roll back an interrupted transaction
69 remove remove the specified files on the next commit
69 remove remove the specified files on the next commit
70 rename rename files; equivalent of copy + remove
70 rename rename files; equivalent of copy + remove
71 revert revert files or dirs to their states as of some revision
71 revert revert files or dirs to their states as of some revision
72 rollback roll back the last transaction in this repository
72 rollback roll back the last transaction in this repository
73 root print the root (top) of the current working dir
73 root print the root (top) of the current working dir
74 serve export the repository via HTTP
74 serve export the repository via HTTP
75 status show changed files in the working directory
75 status show changed files in the working directory
76 tag add a tag for the current tip or a given revision
76 tag add a tag for the current tip or a given revision
77 tags list repository tags
77 tags list repository tags
78 tip show the tip revision
78 tip show the tip revision
79 unbundle apply a changegroup file
79 unbundle apply a changegroup file
80 update update or merge working directory
80 update update or merge working directory
81 verify verify the integrity of the repository
81 verify verify the integrity of the repository
82 version output version and copyright information
82 version output version and copyright information
83 add add the specified files on the next commit
83 add add the specified files on the next commit
84 annotate show changeset information per file line
84 annotate show changeset information per file line
85 archive create unversioned archive of a repository revision
85 archive create unversioned archive of a repository revision
86 backout reverse effect of earlier changeset
86 backout reverse effect of earlier changeset
87 bundle create a changegroup file
87 bundle create a changegroup file
88 cat output the latest or given revisions of files
88 cat output the latest or given revisions of files
89 clone make a copy of an existing repository
89 clone make a copy of an existing repository
90 commit commit the specified files or all outstanding changes
90 commit commit the specified files or all outstanding changes
91 copy mark files as copied for the next commit
91 copy mark files as copied for the next commit
92 diff diff repository (or selected files)
92 diff diff repository (or selected files)
93 export dump the header and diffs for one or more changesets
93 export dump the header and diffs for one or more changesets
94 grep search for a pattern in specified files and revisions
94 grep search for a pattern in specified files and revisions
95 heads show current repository heads
95 heads show current repository heads
96 help show help for a command, extension, or list of commands
96 help show help for a command, extension, or list of commands
97 identify print information about the working copy
97 identify print information about the working copy
98 import import an ordered set of patches
98 import import an ordered set of patches
99 incoming show new changesets found in source
99 incoming show new changesets found in source
100 init create a new repository in the given directory
100 init create a new repository in the given directory
101 locate locate files matching specific patterns
101 locate locate files matching specific patterns
102 log show revision history of entire repository or files
102 log show revision history of entire repository or files
103 manifest output the latest or given revision of the project manifest
103 manifest output the latest or given revision of the project manifest
104 merge Merge working directory with another revision
104 merge Merge working directory with another revision
105 outgoing show changesets not found in destination
105 outgoing show changesets not found in destination
106 parents show the parents of the working dir or revision
106 parents show the parents of the working dir or revision
107 paths show definition of symbolic path names
107 paths show definition of symbolic path names
108 pull pull changes from the specified source
108 pull pull changes from the specified source
109 push push changes to the specified destination
109 push push changes to the specified destination
110 recover roll back an interrupted transaction
110 recover roll back an interrupted transaction
111 remove remove the specified files on the next commit
111 remove remove the specified files on the next commit
112 rename rename files; equivalent of copy + remove
112 rename rename files; equivalent of copy + remove
113 revert revert files or dirs to their states as of some revision
113 revert revert files or dirs to their states as of some revision
114 rollback roll back the last transaction in this repository
114 rollback roll back the last transaction in this repository
115 root print the root (top) of the current working dir
115 root print the root (top) of the current working dir
116 serve export the repository via HTTP
116 serve export the repository via HTTP
117 status show changed files in the working directory
117 status show changed files in the working directory
118 tag add a tag for the current tip or a given revision
118 tag add a tag for the current tip or a given revision
119 tags list repository tags
119 tags list repository tags
120 tip show the tip revision
120 tip show the tip revision
121 unbundle apply a changegroup file
121 unbundle apply a changegroup file
122 update update or merge working directory
122 update update or merge working directory
123 verify verify the integrity of the repository
123 verify verify the integrity of the repository
124 version output version and copyright information
124 version output version and copyright information
125 hg add [OPTION]... [FILE]...
125 hg add [OPTION]... [FILE]...
126
126
127 add the specified files on the next commit
127 add the specified files on the next commit
128
128
129 Schedule files to be version controlled and added to the repository.
129 Schedule files to be version controlled and added to the repository.
130
130
131 The files will be added to the repository at the next commit.
131 The files will be added to the repository at the next commit.
132
132
133 If no names are given, add all files in the repository.
133 If no names are given, add all files in the repository.
134
134
135 options:
135 options:
136
136
137 -I --include include names matching the given patterns
137 -I --include include names matching the given patterns
138 -X --exclude exclude names matching the given patterns
138 -X --exclude exclude names matching the given patterns
139 -n --dry-run do not perform actions, just print output
139 -n --dry-run do not perform actions, just print output
140 hg add: option --skjdfks not recognized
140 hg add: option --skjdfks not recognized
141 hg add [OPTION]... [FILE]...
141 hg add [OPTION]... [FILE]...
142
142
143 add the specified files on the next commit
143 add the specified files on the next commit
144
144
145 Schedule files to be version controlled and added to the repository.
145 Schedule files to be version controlled and added to the repository.
146
146
147 The files will be added to the repository at the next commit.
147 The files will be added to the repository at the next commit.
148
148
149 If no names are given, add all files in the repository.
149 If no names are given, add all files in the repository.
150
150
151 options:
151 options:
152
152
153 -I --include include names matching the given patterns
153 -I --include include names matching the given patterns
154 -X --exclude exclude names matching the given patterns
154 -X --exclude exclude names matching the given patterns
155 -n --dry-run do not perform actions, just print output
155 -n --dry-run do not perform actions, just print output
156 hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...
156 hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...
157
157
158 diff repository (or selected files)
158 diff repository (or selected files)
159
159
160 Show differences between revisions for the specified files.
160 Show differences between revisions for the specified files.
161
161
162 Differences between files are shown using the unified diff format.
162 Differences between files are shown using the unified diff format.
163
163
164 When two revision arguments are given, then changes are shown
164 When two revision arguments are given, then changes are shown
165 between those revisions. If only one revision is specified then
165 between those revisions. If only one revision is specified then
166 that revision is compared to the working directory, and, when no
166 that revision is compared to the working directory, and, when no
167 revisions are specified, the working directory files are compared
167 revisions are specified, the working directory files are compared
168 to its parent.
168 to its parent.
169
169
170 Without the -a option, diff will avoid generating diffs of files
170 Without the -a option, diff will avoid generating diffs of files
171 it detects as binary. With -a, diff will generate a diff anyway,
171 it detects as binary. With -a, diff will generate a diff anyway,
172 probably with undesirable results.
172 probably with undesirable results.
173
173
174 options:
174 options:
175
175
176 -r --rev revision
176 -r --rev revision
177 -a --text treat all files as text
177 -a --text treat all files as text
178 -p --show-function show which function each change is in
178 -p --show-function show which function each change is in
179 -w --ignore-all-space ignore white space when comparing lines
179 -w --ignore-all-space ignore white space when comparing lines
180 -b --ignore-space-change ignore changes in the amount of white space
181 -B --ignore-blank-lines ignore changes whose lines are all blank
180 -I --include include names matching the given patterns
182 -I --include include names matching the given patterns
181 -X --exclude exclude names matching the given patterns
183 -X --exclude exclude names matching the given patterns
182 hg status [OPTION]... [FILE]...
184 hg status [OPTION]... [FILE]...
183
185
184 show changed files in the working directory
186 show changed files in the working directory
185
187
186 Show changed files in the repository. If names are
188 Show changed files in the repository. If names are
187 given, only files that match are shown.
189 given, only files that match are shown.
188
190
189 The codes used to show the status of files are:
191 The codes used to show the status of files are:
190 M = modified
192 M = modified
191 A = added
193 A = added
192 R = removed
194 R = removed
193 ! = deleted, but still tracked
195 ! = deleted, but still tracked
194 ? = not tracked
196 ? = not tracked
195 I = ignored (not shown by default)
197 I = ignored (not shown by default)
196
198
197 aliases: st
199 aliases: st
198
200
199 options:
201 options:
200
202
201 -m --modified show only modified files
203 -m --modified show only modified files
202 -a --added show only added files
204 -a --added show only added files
203 -r --removed show only removed files
205 -r --removed show only removed files
204 -d --deleted show only deleted (but tracked) files
206 -d --deleted show only deleted (but tracked) files
205 -u --unknown show only unknown (not tracked) files
207 -u --unknown show only unknown (not tracked) files
206 -i --ignored show ignored files
208 -i --ignored show ignored files
207 -n --no-status hide status prefix
209 -n --no-status hide status prefix
208 -0 --print0 end filenames with NUL, for use with xargs
210 -0 --print0 end filenames with NUL, for use with xargs
209 -I --include include names matching the given patterns
211 -I --include include names matching the given patterns
210 -X --exclude exclude names matching the given patterns
212 -X --exclude exclude names matching the given patterns
211 hg status [OPTION]... [FILE]...
213 hg status [OPTION]... [FILE]...
212
214
213 show changed files in the working directory
215 show changed files in the working directory
214 hg: unknown command 'foo'
216 hg: unknown command 'foo'
215 Mercurial Distributed SCM
217 Mercurial Distributed SCM
216
218
217 basic commands (use "hg help" for the full list or option "-v" for details):
219 basic commands (use "hg help" for the full list or option "-v" for details):
218
220
219 add add the specified files on the next commit
221 add add the specified files on the next commit
220 annotate show changeset information per file line
222 annotate show changeset information per file line
221 clone make a copy of an existing repository
223 clone make a copy of an existing repository
222 commit commit the specified files or all outstanding changes
224 commit commit the specified files or all outstanding changes
223 diff diff repository (or selected files)
225 diff diff repository (or selected files)
224 export dump the header and diffs for one or more changesets
226 export dump the header and diffs for one or more changesets
225 init create a new repository in the given directory
227 init create a new repository in the given directory
226 log show revision history of entire repository or files
228 log show revision history of entire repository or files
227 parents show the parents of the working dir or revision
229 parents show the parents of the working dir or revision
228 pull pull changes from the specified source
230 pull pull changes from the specified source
229 push push changes to the specified destination
231 push push changes to the specified destination
230 remove remove the specified files on the next commit
232 remove remove the specified files on the next commit
231 revert revert files or dirs to their states as of some revision
233 revert revert files or dirs to their states as of some revision
232 serve export the repository via HTTP
234 serve export the repository via HTTP
233 status show changed files in the working directory
235 status show changed files in the working directory
234 update update or merge working directory
236 update update or merge working directory
235 hg: unknown command 'skjdfks'
237 hg: unknown command 'skjdfks'
236 Mercurial Distributed SCM
238 Mercurial Distributed SCM
237
239
238 basic commands (use "hg help" for the full list or option "-v" for details):
240 basic commands (use "hg help" for the full list or option "-v" for details):
239
241
240 add add the specified files on the next commit
242 add add the specified files on the next commit
241 annotate show changeset information per file line
243 annotate show changeset information per file line
242 clone make a copy of an existing repository
244 clone make a copy of an existing repository
243 commit commit the specified files or all outstanding changes
245 commit commit the specified files or all outstanding changes
244 diff diff repository (or selected files)
246 diff diff repository (or selected files)
245 export dump the header and diffs for one or more changesets
247 export dump the header and diffs for one or more changesets
246 init create a new repository in the given directory
248 init create a new repository in the given directory
247 log show revision history of entire repository or files
249 log show revision history of entire repository or files
248 parents show the parents of the working dir or revision
250 parents show the parents of the working dir or revision
249 pull pull changes from the specified source
251 pull pull changes from the specified source
250 push push changes to the specified destination
252 push push changes to the specified destination
251 remove remove the specified files on the next commit
253 remove remove the specified files on the next commit
252 revert revert files or dirs to their states as of some revision
254 revert revert files or dirs to their states as of some revision
253 serve export the repository via HTTP
255 serve export the repository via HTTP
254 status show changed files in the working directory
256 status show changed files in the working directory
255 update update or merge working directory
257 update update or merge working directory
General Comments 0
You need to be logged in to leave comments. Login now