##// END OF EJS Templates
commands: initial audit of exit codes...
Matt Mackall -
r11177:6a648132 default
parent child Browse files
Show More
@@ -1,1246 +1,1246 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, glob, tempfile
10 import os, sys, errno, re, glob, tempfile
11 import mdiff, bdiff, util, templater, patch, error, encoding, templatekw
11 import mdiff, bdiff, util, templater, patch, error, encoding, templatekw
12 import match as _match
12 import match as _match
13 import similar
13 import similar
14
14
15 revrangesep = ':'
15 revrangesep = ':'
16
16
17 def parsealiases(cmd):
17 def parsealiases(cmd):
18 return cmd.lstrip("^").split("|")
18 return cmd.lstrip("^").split("|")
19
19
20 def findpossible(cmd, table, strict=False):
20 def findpossible(cmd, table, strict=False):
21 """
21 """
22 Return cmd -> (aliases, command table entry)
22 Return cmd -> (aliases, command table entry)
23 for each matching command.
23 for each matching command.
24 Return debug commands (or their aliases) only if no normal command matches.
24 Return debug commands (or their aliases) only if no normal command matches.
25 """
25 """
26 choice = {}
26 choice = {}
27 debugchoice = {}
27 debugchoice = {}
28 for e in table.keys():
28 for e in table.keys():
29 aliases = parsealiases(e)
29 aliases = parsealiases(e)
30 found = None
30 found = None
31 if cmd in aliases:
31 if cmd in aliases:
32 found = cmd
32 found = cmd
33 elif not strict:
33 elif not strict:
34 for a in aliases:
34 for a in aliases:
35 if a.startswith(cmd):
35 if a.startswith(cmd):
36 found = a
36 found = a
37 break
37 break
38 if found is not None:
38 if found is not None:
39 if aliases[0].startswith("debug") or found.startswith("debug"):
39 if aliases[0].startswith("debug") or found.startswith("debug"):
40 debugchoice[found] = (aliases, table[e])
40 debugchoice[found] = (aliases, table[e])
41 else:
41 else:
42 choice[found] = (aliases, table[e])
42 choice[found] = (aliases, table[e])
43
43
44 if not choice and debugchoice:
44 if not choice and debugchoice:
45 choice = debugchoice
45 choice = debugchoice
46
46
47 return choice
47 return choice
48
48
49 def findcmd(cmd, table, strict=True):
49 def findcmd(cmd, table, strict=True):
50 """Return (aliases, command table entry) for command string."""
50 """Return (aliases, command table entry) for command string."""
51 choice = findpossible(cmd, table, strict)
51 choice = findpossible(cmd, table, strict)
52
52
53 if cmd in choice:
53 if cmd in choice:
54 return choice[cmd]
54 return choice[cmd]
55
55
56 if len(choice) > 1:
56 if len(choice) > 1:
57 clist = choice.keys()
57 clist = choice.keys()
58 clist.sort()
58 clist.sort()
59 raise error.AmbiguousCommand(cmd, clist)
59 raise error.AmbiguousCommand(cmd, clist)
60
60
61 if choice:
61 if choice:
62 return choice.values()[0]
62 return choice.values()[0]
63
63
64 raise error.UnknownCommand(cmd)
64 raise error.UnknownCommand(cmd)
65
65
66 def findrepo(p):
66 def findrepo(p):
67 while not os.path.isdir(os.path.join(p, ".hg")):
67 while not os.path.isdir(os.path.join(p, ".hg")):
68 oldp, p = p, os.path.dirname(p)
68 oldp, p = p, os.path.dirname(p)
69 if p == oldp:
69 if p == oldp:
70 return None
70 return None
71
71
72 return p
72 return p
73
73
74 def bail_if_changed(repo):
74 def bail_if_changed(repo):
75 if repo.dirstate.parents()[1] != nullid:
75 if repo.dirstate.parents()[1] != nullid:
76 raise util.Abort(_('outstanding uncommitted merge'))
76 raise util.Abort(_('outstanding uncommitted merge'))
77 modified, added, removed, deleted = repo.status()[:4]
77 modified, added, removed, deleted = repo.status()[:4]
78 if modified or added or removed or deleted:
78 if modified or added or removed or deleted:
79 raise util.Abort(_("outstanding uncommitted changes"))
79 raise util.Abort(_("outstanding uncommitted changes"))
80
80
81 def logmessage(opts):
81 def logmessage(opts):
82 """ get the log message according to -m and -l option """
82 """ get the log message according to -m and -l option """
83 message = opts.get('message')
83 message = opts.get('message')
84 logfile = opts.get('logfile')
84 logfile = opts.get('logfile')
85
85
86 if message and logfile:
86 if message and logfile:
87 raise util.Abort(_('options --message and --logfile are mutually '
87 raise util.Abort(_('options --message and --logfile are mutually '
88 'exclusive'))
88 'exclusive'))
89 if not message and logfile:
89 if not message and logfile:
90 try:
90 try:
91 if logfile == '-':
91 if logfile == '-':
92 message = sys.stdin.read()
92 message = sys.stdin.read()
93 else:
93 else:
94 message = open(logfile).read()
94 message = open(logfile).read()
95 except IOError, inst:
95 except IOError, inst:
96 raise util.Abort(_("can't read commit message '%s': %s") %
96 raise util.Abort(_("can't read commit message '%s': %s") %
97 (logfile, inst.strerror))
97 (logfile, inst.strerror))
98 return message
98 return message
99
99
100 def loglimit(opts):
100 def loglimit(opts):
101 """get the log limit according to option -l/--limit"""
101 """get the log limit according to option -l/--limit"""
102 limit = opts.get('limit')
102 limit = opts.get('limit')
103 if limit:
103 if limit:
104 try:
104 try:
105 limit = int(limit)
105 limit = int(limit)
106 except ValueError:
106 except ValueError:
107 raise util.Abort(_('limit must be a positive integer'))
107 raise util.Abort(_('limit must be a positive integer'))
108 if limit <= 0:
108 if limit <= 0:
109 raise util.Abort(_('limit must be positive'))
109 raise util.Abort(_('limit must be positive'))
110 else:
110 else:
111 limit = None
111 limit = None
112 return limit
112 return limit
113
113
114 def remoteui(src, opts):
114 def remoteui(src, opts):
115 'build a remote ui from ui or repo and opts'
115 'build a remote ui from ui or repo and opts'
116 if hasattr(src, 'baseui'): # looks like a repository
116 if hasattr(src, 'baseui'): # looks like a repository
117 dst = src.baseui.copy() # drop repo-specific config
117 dst = src.baseui.copy() # drop repo-specific config
118 src = src.ui # copy target options from repo
118 src = src.ui # copy target options from repo
119 else: # assume it's a global ui object
119 else: # assume it's a global ui object
120 dst = src.copy() # keep all global options
120 dst = src.copy() # keep all global options
121
121
122 # copy ssh-specific options
122 # copy ssh-specific options
123 for o in 'ssh', 'remotecmd':
123 for o in 'ssh', 'remotecmd':
124 v = opts.get(o) or src.config('ui', o)
124 v = opts.get(o) or src.config('ui', o)
125 if v:
125 if v:
126 dst.setconfig("ui", o, v)
126 dst.setconfig("ui", o, v)
127
127
128 # copy bundle-specific options
128 # copy bundle-specific options
129 r = src.config('bundle', 'mainreporoot')
129 r = src.config('bundle', 'mainreporoot')
130 if r:
130 if r:
131 dst.setconfig('bundle', 'mainreporoot', r)
131 dst.setconfig('bundle', 'mainreporoot', r)
132
132
133 # copy auth and http_proxy section settings
133 # copy auth and http_proxy section settings
134 for sect in ('auth', 'http_proxy'):
134 for sect in ('auth', 'http_proxy'):
135 for key, val in src.configitems(sect):
135 for key, val in src.configitems(sect):
136 dst.setconfig(sect, key, val)
136 dst.setconfig(sect, key, val)
137
137
138 return dst
138 return dst
139
139
140 def revpair(repo, revs):
140 def revpair(repo, revs):
141 '''return pair of nodes, given list of revisions. second item can
141 '''return pair of nodes, given list of revisions. second item can
142 be None, meaning use working dir.'''
142 be None, meaning use working dir.'''
143
143
144 def revfix(repo, val, defval):
144 def revfix(repo, val, defval):
145 if not val and val != 0 and defval is not None:
145 if not val and val != 0 and defval is not None:
146 val = defval
146 val = defval
147 return repo.lookup(val)
147 return repo.lookup(val)
148
148
149 if not revs:
149 if not revs:
150 return repo.dirstate.parents()[0], None
150 return repo.dirstate.parents()[0], None
151 end = None
151 end = None
152 if len(revs) == 1:
152 if len(revs) == 1:
153 if revrangesep in revs[0]:
153 if revrangesep in revs[0]:
154 start, end = revs[0].split(revrangesep, 1)
154 start, end = revs[0].split(revrangesep, 1)
155 start = revfix(repo, start, 0)
155 start = revfix(repo, start, 0)
156 end = revfix(repo, end, len(repo) - 1)
156 end = revfix(repo, end, len(repo) - 1)
157 else:
157 else:
158 start = revfix(repo, revs[0], None)
158 start = revfix(repo, revs[0], None)
159 elif len(revs) == 2:
159 elif len(revs) == 2:
160 if revrangesep in revs[0] or revrangesep in revs[1]:
160 if revrangesep in revs[0] or revrangesep in revs[1]:
161 raise util.Abort(_('too many revisions specified'))
161 raise util.Abort(_('too many revisions specified'))
162 start = revfix(repo, revs[0], None)
162 start = revfix(repo, revs[0], None)
163 end = revfix(repo, revs[1], None)
163 end = revfix(repo, revs[1], None)
164 else:
164 else:
165 raise util.Abort(_('too many revisions specified'))
165 raise util.Abort(_('too many revisions specified'))
166 return start, end
166 return start, end
167
167
168 def revrange(repo, revs):
168 def revrange(repo, revs):
169 """Yield revision as strings from a list of revision specifications."""
169 """Yield revision as strings from a list of revision specifications."""
170
170
171 def revfix(repo, val, defval):
171 def revfix(repo, val, defval):
172 if not val and val != 0 and defval is not None:
172 if not val and val != 0 and defval is not None:
173 return defval
173 return defval
174 return repo.changelog.rev(repo.lookup(val))
174 return repo.changelog.rev(repo.lookup(val))
175
175
176 seen, l = set(), []
176 seen, l = set(), []
177 for spec in revs:
177 for spec in revs:
178 if revrangesep in spec:
178 if revrangesep in spec:
179 start, end = spec.split(revrangesep, 1)
179 start, end = spec.split(revrangesep, 1)
180 start = revfix(repo, start, 0)
180 start = revfix(repo, start, 0)
181 end = revfix(repo, end, len(repo) - 1)
181 end = revfix(repo, end, len(repo) - 1)
182 step = start > end and -1 or 1
182 step = start > end and -1 or 1
183 for rev in xrange(start, end + step, step):
183 for rev in xrange(start, end + step, step):
184 if rev in seen:
184 if rev in seen:
185 continue
185 continue
186 seen.add(rev)
186 seen.add(rev)
187 l.append(rev)
187 l.append(rev)
188 else:
188 else:
189 rev = revfix(repo, spec, None)
189 rev = revfix(repo, spec, None)
190 if rev in seen:
190 if rev in seen:
191 continue
191 continue
192 seen.add(rev)
192 seen.add(rev)
193 l.append(rev)
193 l.append(rev)
194
194
195 return l
195 return l
196
196
197 def make_filename(repo, pat, node,
197 def make_filename(repo, pat, node,
198 total=None, seqno=None, revwidth=None, pathname=None):
198 total=None, seqno=None, revwidth=None, pathname=None):
199 node_expander = {
199 node_expander = {
200 'H': lambda: hex(node),
200 'H': lambda: hex(node),
201 'R': lambda: str(repo.changelog.rev(node)),
201 'R': lambda: str(repo.changelog.rev(node)),
202 'h': lambda: short(node),
202 'h': lambda: short(node),
203 }
203 }
204 expander = {
204 expander = {
205 '%': lambda: '%',
205 '%': lambda: '%',
206 'b': lambda: os.path.basename(repo.root),
206 'b': lambda: os.path.basename(repo.root),
207 }
207 }
208
208
209 try:
209 try:
210 if node:
210 if node:
211 expander.update(node_expander)
211 expander.update(node_expander)
212 if node:
212 if node:
213 expander['r'] = (lambda:
213 expander['r'] = (lambda:
214 str(repo.changelog.rev(node)).zfill(revwidth or 0))
214 str(repo.changelog.rev(node)).zfill(revwidth or 0))
215 if total is not None:
215 if total is not None:
216 expander['N'] = lambda: str(total)
216 expander['N'] = lambda: str(total)
217 if seqno is not None:
217 if seqno is not None:
218 expander['n'] = lambda: str(seqno)
218 expander['n'] = lambda: str(seqno)
219 if total is not None and seqno is not None:
219 if total is not None and seqno is not None:
220 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
220 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
221 if pathname is not None:
221 if pathname is not None:
222 expander['s'] = lambda: os.path.basename(pathname)
222 expander['s'] = lambda: os.path.basename(pathname)
223 expander['d'] = lambda: os.path.dirname(pathname) or '.'
223 expander['d'] = lambda: os.path.dirname(pathname) or '.'
224 expander['p'] = lambda: pathname
224 expander['p'] = lambda: pathname
225
225
226 newname = []
226 newname = []
227 patlen = len(pat)
227 patlen = len(pat)
228 i = 0
228 i = 0
229 while i < patlen:
229 while i < patlen:
230 c = pat[i]
230 c = pat[i]
231 if c == '%':
231 if c == '%':
232 i += 1
232 i += 1
233 c = pat[i]
233 c = pat[i]
234 c = expander[c]()
234 c = expander[c]()
235 newname.append(c)
235 newname.append(c)
236 i += 1
236 i += 1
237 return ''.join(newname)
237 return ''.join(newname)
238 except KeyError, inst:
238 except KeyError, inst:
239 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
239 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
240 inst.args[0])
240 inst.args[0])
241
241
242 def make_file(repo, pat, node=None,
242 def make_file(repo, pat, node=None,
243 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
243 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
244
244
245 writable = 'w' in mode or 'a' in mode
245 writable = 'w' in mode or 'a' in mode
246
246
247 if not pat or pat == '-':
247 if not pat or pat == '-':
248 return writable and sys.stdout or sys.stdin
248 return writable and sys.stdout or sys.stdin
249 if hasattr(pat, 'write') and writable:
249 if hasattr(pat, 'write') and writable:
250 return pat
250 return pat
251 if hasattr(pat, 'read') and 'r' in mode:
251 if hasattr(pat, 'read') and 'r' in mode:
252 return pat
252 return pat
253 return open(make_filename(repo, pat, node, total, seqno, revwidth,
253 return open(make_filename(repo, pat, node, total, seqno, revwidth,
254 pathname),
254 pathname),
255 mode)
255 mode)
256
256
257 def expandpats(pats):
257 def expandpats(pats):
258 if not util.expandglobs:
258 if not util.expandglobs:
259 return list(pats)
259 return list(pats)
260 ret = []
260 ret = []
261 for p in pats:
261 for p in pats:
262 kind, name = _match._patsplit(p, None)
262 kind, name = _match._patsplit(p, None)
263 if kind is None:
263 if kind is None:
264 try:
264 try:
265 globbed = glob.glob(name)
265 globbed = glob.glob(name)
266 except re.error:
266 except re.error:
267 globbed = [name]
267 globbed = [name]
268 if globbed:
268 if globbed:
269 ret.extend(globbed)
269 ret.extend(globbed)
270 continue
270 continue
271 ret.append(p)
271 ret.append(p)
272 return ret
272 return ret
273
273
274 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
274 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
275 if not globbed and default == 'relpath':
275 if not globbed and default == 'relpath':
276 pats = expandpats(pats or [])
276 pats = expandpats(pats or [])
277 m = _match.match(repo.root, repo.getcwd(), pats,
277 m = _match.match(repo.root, repo.getcwd(), pats,
278 opts.get('include'), opts.get('exclude'), default)
278 opts.get('include'), opts.get('exclude'), default)
279 def badfn(f, msg):
279 def badfn(f, msg):
280 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
280 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
281 m.bad = badfn
281 m.bad = badfn
282 return m
282 return m
283
283
284 def matchall(repo):
284 def matchall(repo):
285 return _match.always(repo.root, repo.getcwd())
285 return _match.always(repo.root, repo.getcwd())
286
286
287 def matchfiles(repo, files):
287 def matchfiles(repo, files):
288 return _match.exact(repo.root, repo.getcwd(), files)
288 return _match.exact(repo.root, repo.getcwd(), files)
289
289
290 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
290 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
291 if dry_run is None:
291 if dry_run is None:
292 dry_run = opts.get('dry_run')
292 dry_run = opts.get('dry_run')
293 if similarity is None:
293 if similarity is None:
294 similarity = float(opts.get('similarity') or 0)
294 similarity = float(opts.get('similarity') or 0)
295 # we'd use status here, except handling of symlinks and ignore is tricky
295 # we'd use status here, except handling of symlinks and ignore is tricky
296 added, unknown, deleted, removed = [], [], [], []
296 added, unknown, deleted, removed = [], [], [], []
297 audit_path = util.path_auditor(repo.root)
297 audit_path = util.path_auditor(repo.root)
298 m = match(repo, pats, opts)
298 m = match(repo, pats, opts)
299 for abs in repo.walk(m):
299 for abs in repo.walk(m):
300 target = repo.wjoin(abs)
300 target = repo.wjoin(abs)
301 good = True
301 good = True
302 try:
302 try:
303 audit_path(abs)
303 audit_path(abs)
304 except:
304 except:
305 good = False
305 good = False
306 rel = m.rel(abs)
306 rel = m.rel(abs)
307 exact = m.exact(abs)
307 exact = m.exact(abs)
308 if good and abs not in repo.dirstate:
308 if good and abs not in repo.dirstate:
309 unknown.append(abs)
309 unknown.append(abs)
310 if repo.ui.verbose or not exact:
310 if repo.ui.verbose or not exact:
311 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
311 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
312 elif repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
312 elif repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
313 or (os.path.isdir(target) and not os.path.islink(target))):
313 or (os.path.isdir(target) and not os.path.islink(target))):
314 deleted.append(abs)
314 deleted.append(abs)
315 if repo.ui.verbose or not exact:
315 if repo.ui.verbose or not exact:
316 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
316 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
317 # for finding renames
317 # for finding renames
318 elif repo.dirstate[abs] == 'r':
318 elif repo.dirstate[abs] == 'r':
319 removed.append(abs)
319 removed.append(abs)
320 elif repo.dirstate[abs] == 'a':
320 elif repo.dirstate[abs] == 'a':
321 added.append(abs)
321 added.append(abs)
322 copies = {}
322 copies = {}
323 if similarity > 0:
323 if similarity > 0:
324 for old, new, score in similar.findrenames(repo,
324 for old, new, score in similar.findrenames(repo,
325 added + unknown, removed + deleted, similarity):
325 added + unknown, removed + deleted, similarity):
326 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
326 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
327 repo.ui.status(_('recording removal of %s as rename to %s '
327 repo.ui.status(_('recording removal of %s as rename to %s '
328 '(%d%% similar)\n') %
328 '(%d%% similar)\n') %
329 (m.rel(old), m.rel(new), score * 100))
329 (m.rel(old), m.rel(new), score * 100))
330 copies[new] = old
330 copies[new] = old
331
331
332 if not dry_run:
332 if not dry_run:
333 wlock = repo.wlock()
333 wlock = repo.wlock()
334 try:
334 try:
335 repo.remove(deleted)
335 repo.remove(deleted)
336 repo.add(unknown)
336 repo.add(unknown)
337 for new, old in copies.iteritems():
337 for new, old in copies.iteritems():
338 repo.copy(old, new)
338 repo.copy(old, new)
339 finally:
339 finally:
340 wlock.release()
340 wlock.release()
341
341
342 def copy(ui, repo, pats, opts, rename=False):
342 def copy(ui, repo, pats, opts, rename=False):
343 # called with the repo lock held
343 # called with the repo lock held
344 #
344 #
345 # hgsep => pathname that uses "/" to separate directories
345 # hgsep => pathname that uses "/" to separate directories
346 # ossep => pathname that uses os.sep to separate directories
346 # ossep => pathname that uses os.sep to separate directories
347 cwd = repo.getcwd()
347 cwd = repo.getcwd()
348 targets = {}
348 targets = {}
349 after = opts.get("after")
349 after = opts.get("after")
350 dryrun = opts.get("dry_run")
350 dryrun = opts.get("dry_run")
351
351
352 def walkpat(pat):
352 def walkpat(pat):
353 srcs = []
353 srcs = []
354 m = match(repo, [pat], opts, globbed=True)
354 m = match(repo, [pat], opts, globbed=True)
355 for abs in repo.walk(m):
355 for abs in repo.walk(m):
356 state = repo.dirstate[abs]
356 state = repo.dirstate[abs]
357 rel = m.rel(abs)
357 rel = m.rel(abs)
358 exact = m.exact(abs)
358 exact = m.exact(abs)
359 if state in '?r':
359 if state in '?r':
360 if exact and state == '?':
360 if exact and state == '?':
361 ui.warn(_('%s: not copying - file is not managed\n') % rel)
361 ui.warn(_('%s: not copying - file is not managed\n') % rel)
362 if exact and state == 'r':
362 if exact and state == 'r':
363 ui.warn(_('%s: not copying - file has been marked for'
363 ui.warn(_('%s: not copying - file has been marked for'
364 ' remove\n') % rel)
364 ' remove\n') % rel)
365 continue
365 continue
366 # abs: hgsep
366 # abs: hgsep
367 # rel: ossep
367 # rel: ossep
368 srcs.append((abs, rel, exact))
368 srcs.append((abs, rel, exact))
369 return srcs
369 return srcs
370
370
371 # abssrc: hgsep
371 # abssrc: hgsep
372 # relsrc: ossep
372 # relsrc: ossep
373 # otarget: ossep
373 # otarget: ossep
374 def copyfile(abssrc, relsrc, otarget, exact):
374 def copyfile(abssrc, relsrc, otarget, exact):
375 abstarget = util.canonpath(repo.root, cwd, otarget)
375 abstarget = util.canonpath(repo.root, cwd, otarget)
376 reltarget = repo.pathto(abstarget, cwd)
376 reltarget = repo.pathto(abstarget, cwd)
377 target = repo.wjoin(abstarget)
377 target = repo.wjoin(abstarget)
378 src = repo.wjoin(abssrc)
378 src = repo.wjoin(abssrc)
379 state = repo.dirstate[abstarget]
379 state = repo.dirstate[abstarget]
380
380
381 # check for collisions
381 # check for collisions
382 prevsrc = targets.get(abstarget)
382 prevsrc = targets.get(abstarget)
383 if prevsrc is not None:
383 if prevsrc is not None:
384 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
384 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
385 (reltarget, repo.pathto(abssrc, cwd),
385 (reltarget, repo.pathto(abssrc, cwd),
386 repo.pathto(prevsrc, cwd)))
386 repo.pathto(prevsrc, cwd)))
387 return
387 return
388
388
389 # check for overwrites
389 # check for overwrites
390 exists = os.path.exists(target)
390 exists = os.path.exists(target)
391 if not after and exists or after and state in 'mn':
391 if not after and exists or after and state in 'mn':
392 if not opts['force']:
392 if not opts['force']:
393 ui.warn(_('%s: not overwriting - file exists\n') %
393 ui.warn(_('%s: not overwriting - file exists\n') %
394 reltarget)
394 reltarget)
395 return
395 return
396
396
397 if after:
397 if after:
398 if not exists:
398 if not exists:
399 if rename:
399 if rename:
400 ui.warn(_('%s: not recording move - %s does not exist\n') %
400 ui.warn(_('%s: not recording move - %s does not exist\n') %
401 (relsrc, reltarget))
401 (relsrc, reltarget))
402 else:
402 else:
403 ui.warn(_('%s: not recording copy - %s does not exist\n') %
403 ui.warn(_('%s: not recording copy - %s does not exist\n') %
404 (relsrc, reltarget))
404 (relsrc, reltarget))
405 return
405 return
406 elif not dryrun:
406 elif not dryrun:
407 try:
407 try:
408 if exists:
408 if exists:
409 os.unlink(target)
409 os.unlink(target)
410 targetdir = os.path.dirname(target) or '.'
410 targetdir = os.path.dirname(target) or '.'
411 if not os.path.isdir(targetdir):
411 if not os.path.isdir(targetdir):
412 os.makedirs(targetdir)
412 os.makedirs(targetdir)
413 util.copyfile(src, target)
413 util.copyfile(src, target)
414 except IOError, inst:
414 except IOError, inst:
415 if inst.errno == errno.ENOENT:
415 if inst.errno == errno.ENOENT:
416 ui.warn(_('%s: deleted in working copy\n') % relsrc)
416 ui.warn(_('%s: deleted in working copy\n') % relsrc)
417 else:
417 else:
418 ui.warn(_('%s: cannot copy - %s\n') %
418 ui.warn(_('%s: cannot copy - %s\n') %
419 (relsrc, inst.strerror))
419 (relsrc, inst.strerror))
420 return True # report a failure
420 return True # report a failure
421
421
422 if ui.verbose or not exact:
422 if ui.verbose or not exact:
423 if rename:
423 if rename:
424 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
424 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
425 else:
425 else:
426 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
426 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
427
427
428 targets[abstarget] = abssrc
428 targets[abstarget] = abssrc
429
429
430 # fix up dirstate
430 # fix up dirstate
431 origsrc = repo.dirstate.copied(abssrc) or abssrc
431 origsrc = repo.dirstate.copied(abssrc) or abssrc
432 if abstarget == origsrc: # copying back a copy?
432 if abstarget == origsrc: # copying back a copy?
433 if state not in 'mn' and not dryrun:
433 if state not in 'mn' and not dryrun:
434 repo.dirstate.normallookup(abstarget)
434 repo.dirstate.normallookup(abstarget)
435 else:
435 else:
436 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
436 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
437 if not ui.quiet:
437 if not ui.quiet:
438 ui.warn(_("%s has not been committed yet, so no copy "
438 ui.warn(_("%s has not been committed yet, so no copy "
439 "data will be stored for %s.\n")
439 "data will be stored for %s.\n")
440 % (repo.pathto(origsrc, cwd), reltarget))
440 % (repo.pathto(origsrc, cwd), reltarget))
441 if repo.dirstate[abstarget] in '?r' and not dryrun:
441 if repo.dirstate[abstarget] in '?r' and not dryrun:
442 repo.add([abstarget])
442 repo.add([abstarget])
443 elif not dryrun:
443 elif not dryrun:
444 repo.copy(origsrc, abstarget)
444 repo.copy(origsrc, abstarget)
445
445
446 if rename and not dryrun:
446 if rename and not dryrun:
447 repo.remove([abssrc], not after)
447 repo.remove([abssrc], not after)
448
448
449 # pat: ossep
449 # pat: ossep
450 # dest ossep
450 # dest ossep
451 # srcs: list of (hgsep, hgsep, ossep, bool)
451 # srcs: list of (hgsep, hgsep, ossep, bool)
452 # return: function that takes hgsep and returns ossep
452 # return: function that takes hgsep and returns ossep
453 def targetpathfn(pat, dest, srcs):
453 def targetpathfn(pat, dest, srcs):
454 if os.path.isdir(pat):
454 if os.path.isdir(pat):
455 abspfx = util.canonpath(repo.root, cwd, pat)
455 abspfx = util.canonpath(repo.root, cwd, pat)
456 abspfx = util.localpath(abspfx)
456 abspfx = util.localpath(abspfx)
457 if destdirexists:
457 if destdirexists:
458 striplen = len(os.path.split(abspfx)[0])
458 striplen = len(os.path.split(abspfx)[0])
459 else:
459 else:
460 striplen = len(abspfx)
460 striplen = len(abspfx)
461 if striplen:
461 if striplen:
462 striplen += len(os.sep)
462 striplen += len(os.sep)
463 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
463 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
464 elif destdirexists:
464 elif destdirexists:
465 res = lambda p: os.path.join(dest,
465 res = lambda p: os.path.join(dest,
466 os.path.basename(util.localpath(p)))
466 os.path.basename(util.localpath(p)))
467 else:
467 else:
468 res = lambda p: dest
468 res = lambda p: dest
469 return res
469 return res
470
470
471 # pat: ossep
471 # pat: ossep
472 # dest ossep
472 # dest ossep
473 # srcs: list of (hgsep, hgsep, ossep, bool)
473 # srcs: list of (hgsep, hgsep, ossep, bool)
474 # return: function that takes hgsep and returns ossep
474 # return: function that takes hgsep and returns ossep
475 def targetpathafterfn(pat, dest, srcs):
475 def targetpathafterfn(pat, dest, srcs):
476 if _match.patkind(pat):
476 if _match.patkind(pat):
477 # a mercurial pattern
477 # a mercurial pattern
478 res = lambda p: os.path.join(dest,
478 res = lambda p: os.path.join(dest,
479 os.path.basename(util.localpath(p)))
479 os.path.basename(util.localpath(p)))
480 else:
480 else:
481 abspfx = util.canonpath(repo.root, cwd, pat)
481 abspfx = util.canonpath(repo.root, cwd, pat)
482 if len(abspfx) < len(srcs[0][0]):
482 if len(abspfx) < len(srcs[0][0]):
483 # A directory. Either the target path contains the last
483 # A directory. Either the target path contains the last
484 # component of the source path or it does not.
484 # component of the source path or it does not.
485 def evalpath(striplen):
485 def evalpath(striplen):
486 score = 0
486 score = 0
487 for s in srcs:
487 for s in srcs:
488 t = os.path.join(dest, util.localpath(s[0])[striplen:])
488 t = os.path.join(dest, util.localpath(s[0])[striplen:])
489 if os.path.exists(t):
489 if os.path.exists(t):
490 score += 1
490 score += 1
491 return score
491 return score
492
492
493 abspfx = util.localpath(abspfx)
493 abspfx = util.localpath(abspfx)
494 striplen = len(abspfx)
494 striplen = len(abspfx)
495 if striplen:
495 if striplen:
496 striplen += len(os.sep)
496 striplen += len(os.sep)
497 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
497 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
498 score = evalpath(striplen)
498 score = evalpath(striplen)
499 striplen1 = len(os.path.split(abspfx)[0])
499 striplen1 = len(os.path.split(abspfx)[0])
500 if striplen1:
500 if striplen1:
501 striplen1 += len(os.sep)
501 striplen1 += len(os.sep)
502 if evalpath(striplen1) > score:
502 if evalpath(striplen1) > score:
503 striplen = striplen1
503 striplen = striplen1
504 res = lambda p: os.path.join(dest,
504 res = lambda p: os.path.join(dest,
505 util.localpath(p)[striplen:])
505 util.localpath(p)[striplen:])
506 else:
506 else:
507 # a file
507 # a file
508 if destdirexists:
508 if destdirexists:
509 res = lambda p: os.path.join(dest,
509 res = lambda p: os.path.join(dest,
510 os.path.basename(util.localpath(p)))
510 os.path.basename(util.localpath(p)))
511 else:
511 else:
512 res = lambda p: dest
512 res = lambda p: dest
513 return res
513 return res
514
514
515
515
516 pats = expandpats(pats)
516 pats = expandpats(pats)
517 if not pats:
517 if not pats:
518 raise util.Abort(_('no source or destination specified'))
518 raise util.Abort(_('no source or destination specified'))
519 if len(pats) == 1:
519 if len(pats) == 1:
520 raise util.Abort(_('no destination specified'))
520 raise util.Abort(_('no destination specified'))
521 dest = pats.pop()
521 dest = pats.pop()
522 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
522 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
523 if not destdirexists:
523 if not destdirexists:
524 if len(pats) > 1 or _match.patkind(pats[0]):
524 if len(pats) > 1 or _match.patkind(pats[0]):
525 raise util.Abort(_('with multiple sources, destination must be an '
525 raise util.Abort(_('with multiple sources, destination must be an '
526 'existing directory'))
526 'existing directory'))
527 if util.endswithsep(dest):
527 if util.endswithsep(dest):
528 raise util.Abort(_('destination %s is not a directory') % dest)
528 raise util.Abort(_('destination %s is not a directory') % dest)
529
529
530 tfn = targetpathfn
530 tfn = targetpathfn
531 if after:
531 if after:
532 tfn = targetpathafterfn
532 tfn = targetpathafterfn
533 copylist = []
533 copylist = []
534 for pat in pats:
534 for pat in pats:
535 srcs = walkpat(pat)
535 srcs = walkpat(pat)
536 if not srcs:
536 if not srcs:
537 continue
537 continue
538 copylist.append((tfn(pat, dest, srcs), srcs))
538 copylist.append((tfn(pat, dest, srcs), srcs))
539 if not copylist:
539 if not copylist:
540 raise util.Abort(_('no files to copy'))
540 raise util.Abort(_('no files to copy'))
541
541
542 errors = 0
542 errors = 0
543 for targetpath, srcs in copylist:
543 for targetpath, srcs in copylist:
544 for abssrc, relsrc, exact in srcs:
544 for abssrc, relsrc, exact in srcs:
545 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
545 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
546 errors += 1
546 errors += 1
547
547
548 if errors:
548 if errors:
549 ui.warn(_('(consider using --after)\n'))
549 ui.warn(_('(consider using --after)\n'))
550
550
551 return errors
551 return errors != 0
552
552
553 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
553 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
554 runargs=None, appendpid=False):
554 runargs=None, appendpid=False):
555 '''Run a command as a service.'''
555 '''Run a command as a service.'''
556
556
557 if opts['daemon'] and not opts['daemon_pipefds']:
557 if opts['daemon'] and not opts['daemon_pipefds']:
558 # Signal child process startup with file removal
558 # Signal child process startup with file removal
559 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
559 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
560 os.close(lockfd)
560 os.close(lockfd)
561 try:
561 try:
562 if not runargs:
562 if not runargs:
563 runargs = util.hgcmd() + sys.argv[1:]
563 runargs = util.hgcmd() + sys.argv[1:]
564 runargs.append('--daemon-pipefds=%s' % lockpath)
564 runargs.append('--daemon-pipefds=%s' % lockpath)
565 # Don't pass --cwd to the child process, because we've already
565 # Don't pass --cwd to the child process, because we've already
566 # changed directory.
566 # changed directory.
567 for i in xrange(1, len(runargs)):
567 for i in xrange(1, len(runargs)):
568 if runargs[i].startswith('--cwd='):
568 if runargs[i].startswith('--cwd='):
569 del runargs[i]
569 del runargs[i]
570 break
570 break
571 elif runargs[i].startswith('--cwd'):
571 elif runargs[i].startswith('--cwd'):
572 del runargs[i:i + 2]
572 del runargs[i:i + 2]
573 break
573 break
574 def condfn():
574 def condfn():
575 return not os.path.exists(lockpath)
575 return not os.path.exists(lockpath)
576 pid = util.rundetached(runargs, condfn)
576 pid = util.rundetached(runargs, condfn)
577 if pid < 0:
577 if pid < 0:
578 raise util.Abort(_('child process failed to start'))
578 raise util.Abort(_('child process failed to start'))
579 finally:
579 finally:
580 try:
580 try:
581 os.unlink(lockpath)
581 os.unlink(lockpath)
582 except OSError, e:
582 except OSError, e:
583 if e.errno != errno.ENOENT:
583 if e.errno != errno.ENOENT:
584 raise
584 raise
585 if parentfn:
585 if parentfn:
586 return parentfn(pid)
586 return parentfn(pid)
587 else:
587 else:
588 return
588 return
589
589
590 if initfn:
590 if initfn:
591 initfn()
591 initfn()
592
592
593 if opts['pid_file']:
593 if opts['pid_file']:
594 mode = appendpid and 'a' or 'w'
594 mode = appendpid and 'a' or 'w'
595 fp = open(opts['pid_file'], mode)
595 fp = open(opts['pid_file'], mode)
596 fp.write(str(os.getpid()) + '\n')
596 fp.write(str(os.getpid()) + '\n')
597 fp.close()
597 fp.close()
598
598
599 if opts['daemon_pipefds']:
599 if opts['daemon_pipefds']:
600 lockpath = opts['daemon_pipefds']
600 lockpath = opts['daemon_pipefds']
601 try:
601 try:
602 os.setsid()
602 os.setsid()
603 except AttributeError:
603 except AttributeError:
604 pass
604 pass
605 os.unlink(lockpath)
605 os.unlink(lockpath)
606 util.hidewindow()
606 util.hidewindow()
607 sys.stdout.flush()
607 sys.stdout.flush()
608 sys.stderr.flush()
608 sys.stderr.flush()
609
609
610 nullfd = os.open(util.nulldev, os.O_RDWR)
610 nullfd = os.open(util.nulldev, os.O_RDWR)
611 logfilefd = nullfd
611 logfilefd = nullfd
612 if logfile:
612 if logfile:
613 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
613 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
614 os.dup2(nullfd, 0)
614 os.dup2(nullfd, 0)
615 os.dup2(logfilefd, 1)
615 os.dup2(logfilefd, 1)
616 os.dup2(logfilefd, 2)
616 os.dup2(logfilefd, 2)
617 if nullfd not in (0, 1, 2):
617 if nullfd not in (0, 1, 2):
618 os.close(nullfd)
618 os.close(nullfd)
619 if logfile and logfilefd not in (0, 1, 2):
619 if logfile and logfilefd not in (0, 1, 2):
620 os.close(logfilefd)
620 os.close(logfilefd)
621
621
622 if runfn:
622 if runfn:
623 return runfn()
623 return runfn()
624
624
625 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
625 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
626 opts=None):
626 opts=None):
627 '''export changesets as hg patches.'''
627 '''export changesets as hg patches.'''
628
628
629 total = len(revs)
629 total = len(revs)
630 revwidth = max([len(str(rev)) for rev in revs])
630 revwidth = max([len(str(rev)) for rev in revs])
631
631
632 def single(rev, seqno, fp):
632 def single(rev, seqno, fp):
633 ctx = repo[rev]
633 ctx = repo[rev]
634 node = ctx.node()
634 node = ctx.node()
635 parents = [p.node() for p in ctx.parents() if p]
635 parents = [p.node() for p in ctx.parents() if p]
636 branch = ctx.branch()
636 branch = ctx.branch()
637 if switch_parent:
637 if switch_parent:
638 parents.reverse()
638 parents.reverse()
639 prev = (parents and parents[0]) or nullid
639 prev = (parents and parents[0]) or nullid
640
640
641 if not fp:
641 if not fp:
642 fp = make_file(repo, template, node, total=total, seqno=seqno,
642 fp = make_file(repo, template, node, total=total, seqno=seqno,
643 revwidth=revwidth, mode='ab')
643 revwidth=revwidth, mode='ab')
644 if fp != sys.stdout and hasattr(fp, 'name'):
644 if fp != sys.stdout and hasattr(fp, 'name'):
645 repo.ui.note("%s\n" % fp.name)
645 repo.ui.note("%s\n" % fp.name)
646
646
647 fp.write("# HG changeset patch\n")
647 fp.write("# HG changeset patch\n")
648 fp.write("# User %s\n" % ctx.user())
648 fp.write("# User %s\n" % ctx.user())
649 fp.write("# Date %d %d\n" % ctx.date())
649 fp.write("# Date %d %d\n" % ctx.date())
650 if branch and (branch != 'default'):
650 if branch and (branch != 'default'):
651 fp.write("# Branch %s\n" % branch)
651 fp.write("# Branch %s\n" % branch)
652 fp.write("# Node ID %s\n" % hex(node))
652 fp.write("# Node ID %s\n" % hex(node))
653 fp.write("# Parent %s\n" % hex(prev))
653 fp.write("# Parent %s\n" % hex(prev))
654 if len(parents) > 1:
654 if len(parents) > 1:
655 fp.write("# Parent %s\n" % hex(parents[1]))
655 fp.write("# Parent %s\n" % hex(parents[1]))
656 fp.write(ctx.description().rstrip())
656 fp.write(ctx.description().rstrip())
657 fp.write("\n\n")
657 fp.write("\n\n")
658
658
659 for chunk in patch.diff(repo, prev, node, opts=opts):
659 for chunk in patch.diff(repo, prev, node, opts=opts):
660 fp.write(chunk)
660 fp.write(chunk)
661
661
662 for seqno, rev in enumerate(revs):
662 for seqno, rev in enumerate(revs):
663 single(rev, seqno + 1, fp)
663 single(rev, seqno + 1, fp)
664
664
665 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
665 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
666 changes=None, stat=False, fp=None):
666 changes=None, stat=False, fp=None):
667 '''show diff or diffstat.'''
667 '''show diff or diffstat.'''
668 if fp is None:
668 if fp is None:
669 write = ui.write
669 write = ui.write
670 else:
670 else:
671 def write(s, **kw):
671 def write(s, **kw):
672 fp.write(s)
672 fp.write(s)
673
673
674 if stat:
674 if stat:
675 diffopts.context = 0
675 diffopts.context = 0
676 width = 80
676 width = 80
677 if not ui.plain():
677 if not ui.plain():
678 width = util.termwidth()
678 width = util.termwidth()
679 chunks = patch.diff(repo, node1, node2, match, changes, diffopts)
679 chunks = patch.diff(repo, node1, node2, match, changes, diffopts)
680 for chunk, label in patch.diffstatui(util.iterlines(chunks),
680 for chunk, label in patch.diffstatui(util.iterlines(chunks),
681 width=width,
681 width=width,
682 git=diffopts.git):
682 git=diffopts.git):
683 write(chunk, label=label)
683 write(chunk, label=label)
684 else:
684 else:
685 for chunk, label in patch.diffui(repo, node1, node2, match,
685 for chunk, label in patch.diffui(repo, node1, node2, match,
686 changes, diffopts):
686 changes, diffopts):
687 write(chunk, label=label)
687 write(chunk, label=label)
688
688
689 class changeset_printer(object):
689 class changeset_printer(object):
690 '''show changeset information when templating not requested.'''
690 '''show changeset information when templating not requested.'''
691
691
692 def __init__(self, ui, repo, patch, diffopts, buffered):
692 def __init__(self, ui, repo, patch, diffopts, buffered):
693 self.ui = ui
693 self.ui = ui
694 self.repo = repo
694 self.repo = repo
695 self.buffered = buffered
695 self.buffered = buffered
696 self.patch = patch
696 self.patch = patch
697 self.diffopts = diffopts
697 self.diffopts = diffopts
698 self.header = {}
698 self.header = {}
699 self.hunk = {}
699 self.hunk = {}
700 self.lastheader = None
700 self.lastheader = None
701 self.footer = None
701 self.footer = None
702
702
703 def flush(self, rev):
703 def flush(self, rev):
704 if rev in self.header:
704 if rev in self.header:
705 h = self.header[rev]
705 h = self.header[rev]
706 if h != self.lastheader:
706 if h != self.lastheader:
707 self.lastheader = h
707 self.lastheader = h
708 self.ui.write(h)
708 self.ui.write(h)
709 del self.header[rev]
709 del self.header[rev]
710 if rev in self.hunk:
710 if rev in self.hunk:
711 self.ui.write(self.hunk[rev])
711 self.ui.write(self.hunk[rev])
712 del self.hunk[rev]
712 del self.hunk[rev]
713 return 1
713 return 1
714 return 0
714 return 0
715
715
716 def close(self):
716 def close(self):
717 if self.footer:
717 if self.footer:
718 self.ui.write(self.footer)
718 self.ui.write(self.footer)
719
719
720 def show(self, ctx, copies=None, **props):
720 def show(self, ctx, copies=None, **props):
721 if self.buffered:
721 if self.buffered:
722 self.ui.pushbuffer()
722 self.ui.pushbuffer()
723 self._show(ctx, copies, props)
723 self._show(ctx, copies, props)
724 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
724 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
725 else:
725 else:
726 self._show(ctx, copies, props)
726 self._show(ctx, copies, props)
727
727
728 def _show(self, ctx, copies, props):
728 def _show(self, ctx, copies, props):
729 '''show a single changeset or file revision'''
729 '''show a single changeset or file revision'''
730 changenode = ctx.node()
730 changenode = ctx.node()
731 rev = ctx.rev()
731 rev = ctx.rev()
732
732
733 if self.ui.quiet:
733 if self.ui.quiet:
734 self.ui.write("%d:%s\n" % (rev, short(changenode)),
734 self.ui.write("%d:%s\n" % (rev, short(changenode)),
735 label='log.node')
735 label='log.node')
736 return
736 return
737
737
738 log = self.repo.changelog
738 log = self.repo.changelog
739 date = util.datestr(ctx.date())
739 date = util.datestr(ctx.date())
740
740
741 hexfunc = self.ui.debugflag and hex or short
741 hexfunc = self.ui.debugflag and hex or short
742
742
743 parents = [(p, hexfunc(log.node(p)))
743 parents = [(p, hexfunc(log.node(p)))
744 for p in self._meaningful_parentrevs(log, rev)]
744 for p in self._meaningful_parentrevs(log, rev)]
745
745
746 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
746 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
747 label='log.changeset')
747 label='log.changeset')
748
748
749 branch = ctx.branch()
749 branch = ctx.branch()
750 # don't show the default branch name
750 # don't show the default branch name
751 if branch != 'default':
751 if branch != 'default':
752 branch = encoding.tolocal(branch)
752 branch = encoding.tolocal(branch)
753 self.ui.write(_("branch: %s\n") % branch,
753 self.ui.write(_("branch: %s\n") % branch,
754 label='log.branch')
754 label='log.branch')
755 for tag in self.repo.nodetags(changenode):
755 for tag in self.repo.nodetags(changenode):
756 self.ui.write(_("tag: %s\n") % tag,
756 self.ui.write(_("tag: %s\n") % tag,
757 label='log.tag')
757 label='log.tag')
758 for parent in parents:
758 for parent in parents:
759 self.ui.write(_("parent: %d:%s\n") % parent,
759 self.ui.write(_("parent: %d:%s\n") % parent,
760 label='log.parent')
760 label='log.parent')
761
761
762 if self.ui.debugflag:
762 if self.ui.debugflag:
763 mnode = ctx.manifestnode()
763 mnode = ctx.manifestnode()
764 self.ui.write(_("manifest: %d:%s\n") %
764 self.ui.write(_("manifest: %d:%s\n") %
765 (self.repo.manifest.rev(mnode), hex(mnode)),
765 (self.repo.manifest.rev(mnode), hex(mnode)),
766 label='ui.debug log.manifest')
766 label='ui.debug log.manifest')
767 self.ui.write(_("user: %s\n") % ctx.user(),
767 self.ui.write(_("user: %s\n") % ctx.user(),
768 label='log.user')
768 label='log.user')
769 self.ui.write(_("date: %s\n") % date,
769 self.ui.write(_("date: %s\n") % date,
770 label='log.date')
770 label='log.date')
771
771
772 if self.ui.debugflag:
772 if self.ui.debugflag:
773 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
773 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
774 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
774 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
775 files):
775 files):
776 if value:
776 if value:
777 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
777 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
778 label='ui.debug log.files')
778 label='ui.debug log.files')
779 elif ctx.files() and self.ui.verbose:
779 elif ctx.files() and self.ui.verbose:
780 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
780 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
781 label='ui.note log.files')
781 label='ui.note log.files')
782 if copies and self.ui.verbose:
782 if copies and self.ui.verbose:
783 copies = ['%s (%s)' % c for c in copies]
783 copies = ['%s (%s)' % c for c in copies]
784 self.ui.write(_("copies: %s\n") % ' '.join(copies),
784 self.ui.write(_("copies: %s\n") % ' '.join(copies),
785 label='ui.note log.copies')
785 label='ui.note log.copies')
786
786
787 extra = ctx.extra()
787 extra = ctx.extra()
788 if extra and self.ui.debugflag:
788 if extra and self.ui.debugflag:
789 for key, value in sorted(extra.items()):
789 for key, value in sorted(extra.items()):
790 self.ui.write(_("extra: %s=%s\n")
790 self.ui.write(_("extra: %s=%s\n")
791 % (key, value.encode('string_escape')),
791 % (key, value.encode('string_escape')),
792 label='ui.debug log.extra')
792 label='ui.debug log.extra')
793
793
794 description = ctx.description().strip()
794 description = ctx.description().strip()
795 if description:
795 if description:
796 if self.ui.verbose:
796 if self.ui.verbose:
797 self.ui.write(_("description:\n"),
797 self.ui.write(_("description:\n"),
798 label='ui.note log.description')
798 label='ui.note log.description')
799 self.ui.write(description,
799 self.ui.write(description,
800 label='ui.note log.description')
800 label='ui.note log.description')
801 self.ui.write("\n\n")
801 self.ui.write("\n\n")
802 else:
802 else:
803 self.ui.write(_("summary: %s\n") %
803 self.ui.write(_("summary: %s\n") %
804 description.splitlines()[0],
804 description.splitlines()[0],
805 label='log.summary')
805 label='log.summary')
806 self.ui.write("\n")
806 self.ui.write("\n")
807
807
808 self.showpatch(changenode)
808 self.showpatch(changenode)
809
809
810 def showpatch(self, node):
810 def showpatch(self, node):
811 if self.patch:
811 if self.patch:
812 stat = self.diffopts.get('stat')
812 stat = self.diffopts.get('stat')
813 diffopts = patch.diffopts(self.ui, self.diffopts)
813 diffopts = patch.diffopts(self.ui, self.diffopts)
814 prev = self.repo.changelog.parents(node)[0]
814 prev = self.repo.changelog.parents(node)[0]
815 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
815 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
816 match=self.patch, stat=stat)
816 match=self.patch, stat=stat)
817 self.ui.write("\n")
817 self.ui.write("\n")
818
818
819 def _meaningful_parentrevs(self, log, rev):
819 def _meaningful_parentrevs(self, log, rev):
820 """Return list of meaningful (or all if debug) parentrevs for rev.
820 """Return list of meaningful (or all if debug) parentrevs for rev.
821
821
822 For merges (two non-nullrev revisions) both parents are meaningful.
822 For merges (two non-nullrev revisions) both parents are meaningful.
823 Otherwise the first parent revision is considered meaningful if it
823 Otherwise the first parent revision is considered meaningful if it
824 is not the preceding revision.
824 is not the preceding revision.
825 """
825 """
826 parents = log.parentrevs(rev)
826 parents = log.parentrevs(rev)
827 if not self.ui.debugflag and parents[1] == nullrev:
827 if not self.ui.debugflag and parents[1] == nullrev:
828 if parents[0] >= rev - 1:
828 if parents[0] >= rev - 1:
829 parents = []
829 parents = []
830 else:
830 else:
831 parents = [parents[0]]
831 parents = [parents[0]]
832 return parents
832 return parents
833
833
834
834
835 class changeset_templater(changeset_printer):
835 class changeset_templater(changeset_printer):
836 '''format changeset information.'''
836 '''format changeset information.'''
837
837
838 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
838 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
839 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
839 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
840 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
840 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
841 defaulttempl = {
841 defaulttempl = {
842 'parent': '{rev}:{node|formatnode} ',
842 'parent': '{rev}:{node|formatnode} ',
843 'manifest': '{rev}:{node|formatnode}',
843 'manifest': '{rev}:{node|formatnode}',
844 'file_copy': '{name} ({source})',
844 'file_copy': '{name} ({source})',
845 'extra': '{key}={value|stringescape}'
845 'extra': '{key}={value|stringescape}'
846 }
846 }
847 # filecopy is preserved for compatibility reasons
847 # filecopy is preserved for compatibility reasons
848 defaulttempl['filecopy'] = defaulttempl['file_copy']
848 defaulttempl['filecopy'] = defaulttempl['file_copy']
849 self.t = templater.templater(mapfile, {'formatnode': formatnode},
849 self.t = templater.templater(mapfile, {'formatnode': formatnode},
850 cache=defaulttempl)
850 cache=defaulttempl)
851 self.cache = {}
851 self.cache = {}
852
852
853 def use_template(self, t):
853 def use_template(self, t):
854 '''set template string to use'''
854 '''set template string to use'''
855 self.t.cache['changeset'] = t
855 self.t.cache['changeset'] = t
856
856
857 def _meaningful_parentrevs(self, ctx):
857 def _meaningful_parentrevs(self, ctx):
858 """Return list of meaningful (or all if debug) parentrevs for rev.
858 """Return list of meaningful (or all if debug) parentrevs for rev.
859 """
859 """
860 parents = ctx.parents()
860 parents = ctx.parents()
861 if len(parents) > 1:
861 if len(parents) > 1:
862 return parents
862 return parents
863 if self.ui.debugflag:
863 if self.ui.debugflag:
864 return [parents[0], self.repo['null']]
864 return [parents[0], self.repo['null']]
865 if parents[0].rev() >= ctx.rev() - 1:
865 if parents[0].rev() >= ctx.rev() - 1:
866 return []
866 return []
867 return parents
867 return parents
868
868
869 def _show(self, ctx, copies, props):
869 def _show(self, ctx, copies, props):
870 '''show a single changeset or file revision'''
870 '''show a single changeset or file revision'''
871
871
872 showlist = templatekw.showlist
872 showlist = templatekw.showlist
873
873
874 # showparents() behaviour depends on ui trace level which
874 # showparents() behaviour depends on ui trace level which
875 # causes unexpected behaviours at templating level and makes
875 # causes unexpected behaviours at templating level and makes
876 # it harder to extract it in a standalone function. Its
876 # it harder to extract it in a standalone function. Its
877 # behaviour cannot be changed so leave it here for now.
877 # behaviour cannot be changed so leave it here for now.
878 def showparents(**args):
878 def showparents(**args):
879 ctx = args['ctx']
879 ctx = args['ctx']
880 parents = [[('rev', p.rev()), ('node', p.hex())]
880 parents = [[('rev', p.rev()), ('node', p.hex())]
881 for p in self._meaningful_parentrevs(ctx)]
881 for p in self._meaningful_parentrevs(ctx)]
882 return showlist('parent', parents, **args)
882 return showlist('parent', parents, **args)
883
883
884 props = props.copy()
884 props = props.copy()
885 props.update(templatekw.keywords)
885 props.update(templatekw.keywords)
886 props['parents'] = showparents
886 props['parents'] = showparents
887 props['templ'] = self.t
887 props['templ'] = self.t
888 props['ctx'] = ctx
888 props['ctx'] = ctx
889 props['repo'] = self.repo
889 props['repo'] = self.repo
890 props['revcache'] = {'copies': copies}
890 props['revcache'] = {'copies': copies}
891 props['cache'] = self.cache
891 props['cache'] = self.cache
892
892
893 # find correct templates for current mode
893 # find correct templates for current mode
894
894
895 tmplmodes = [
895 tmplmodes = [
896 (True, None),
896 (True, None),
897 (self.ui.verbose, 'verbose'),
897 (self.ui.verbose, 'verbose'),
898 (self.ui.quiet, 'quiet'),
898 (self.ui.quiet, 'quiet'),
899 (self.ui.debugflag, 'debug'),
899 (self.ui.debugflag, 'debug'),
900 ]
900 ]
901
901
902 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
902 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
903 for mode, postfix in tmplmodes:
903 for mode, postfix in tmplmodes:
904 for type in types:
904 for type in types:
905 cur = postfix and ('%s_%s' % (type, postfix)) or type
905 cur = postfix and ('%s_%s' % (type, postfix)) or type
906 if mode and cur in self.t:
906 if mode and cur in self.t:
907 types[type] = cur
907 types[type] = cur
908
908
909 try:
909 try:
910
910
911 # write header
911 # write header
912 if types['header']:
912 if types['header']:
913 h = templater.stringify(self.t(types['header'], **props))
913 h = templater.stringify(self.t(types['header'], **props))
914 if self.buffered:
914 if self.buffered:
915 self.header[ctx.rev()] = h
915 self.header[ctx.rev()] = h
916 else:
916 else:
917 self.ui.write(h)
917 self.ui.write(h)
918
918
919 # write changeset metadata, then patch if requested
919 # write changeset metadata, then patch if requested
920 key = types['changeset']
920 key = types['changeset']
921 self.ui.write(templater.stringify(self.t(key, **props)))
921 self.ui.write(templater.stringify(self.t(key, **props)))
922 self.showpatch(ctx.node())
922 self.showpatch(ctx.node())
923
923
924 if types['footer']:
924 if types['footer']:
925 if not self.footer:
925 if not self.footer:
926 self.footer = templater.stringify(self.t(types['footer'],
926 self.footer = templater.stringify(self.t(types['footer'],
927 **props))
927 **props))
928
928
929 except KeyError, inst:
929 except KeyError, inst:
930 msg = _("%s: no key named '%s'")
930 msg = _("%s: no key named '%s'")
931 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
931 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
932 except SyntaxError, inst:
932 except SyntaxError, inst:
933 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
933 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
934
934
935 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
935 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
936 """show one changeset using template or regular display.
936 """show one changeset using template or regular display.
937
937
938 Display format will be the first non-empty hit of:
938 Display format will be the first non-empty hit of:
939 1. option 'template'
939 1. option 'template'
940 2. option 'style'
940 2. option 'style'
941 3. [ui] setting 'logtemplate'
941 3. [ui] setting 'logtemplate'
942 4. [ui] setting 'style'
942 4. [ui] setting 'style'
943 If all of these values are either the unset or the empty string,
943 If all of these values are either the unset or the empty string,
944 regular display via changeset_printer() is done.
944 regular display via changeset_printer() is done.
945 """
945 """
946 # options
946 # options
947 patch = False
947 patch = False
948 if opts.get('patch') or opts.get('stat'):
948 if opts.get('patch') or opts.get('stat'):
949 patch = matchfn or matchall(repo)
949 patch = matchfn or matchall(repo)
950
950
951 tmpl = opts.get('template')
951 tmpl = opts.get('template')
952 style = None
952 style = None
953 if tmpl:
953 if tmpl:
954 tmpl = templater.parsestring(tmpl, quoted=False)
954 tmpl = templater.parsestring(tmpl, quoted=False)
955 else:
955 else:
956 style = opts.get('style')
956 style = opts.get('style')
957
957
958 # ui settings
958 # ui settings
959 if not (tmpl or style):
959 if not (tmpl or style):
960 tmpl = ui.config('ui', 'logtemplate')
960 tmpl = ui.config('ui', 'logtemplate')
961 if tmpl:
961 if tmpl:
962 tmpl = templater.parsestring(tmpl)
962 tmpl = templater.parsestring(tmpl)
963 else:
963 else:
964 style = util.expandpath(ui.config('ui', 'style', ''))
964 style = util.expandpath(ui.config('ui', 'style', ''))
965
965
966 if not (tmpl or style):
966 if not (tmpl or style):
967 return changeset_printer(ui, repo, patch, opts, buffered)
967 return changeset_printer(ui, repo, patch, opts, buffered)
968
968
969 mapfile = None
969 mapfile = None
970 if style and not tmpl:
970 if style and not tmpl:
971 mapfile = style
971 mapfile = style
972 if not os.path.split(mapfile)[0]:
972 if not os.path.split(mapfile)[0]:
973 mapname = (templater.templatepath('map-cmdline.' + mapfile)
973 mapname = (templater.templatepath('map-cmdline.' + mapfile)
974 or templater.templatepath(mapfile))
974 or templater.templatepath(mapfile))
975 if mapname:
975 if mapname:
976 mapfile = mapname
976 mapfile = mapname
977
977
978 try:
978 try:
979 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
979 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
980 except SyntaxError, inst:
980 except SyntaxError, inst:
981 raise util.Abort(inst.args[0])
981 raise util.Abort(inst.args[0])
982 if tmpl:
982 if tmpl:
983 t.use_template(tmpl)
983 t.use_template(tmpl)
984 return t
984 return t
985
985
986 def finddate(ui, repo, date):
986 def finddate(ui, repo, date):
987 """Find the tipmost changeset that matches the given date spec"""
987 """Find the tipmost changeset that matches the given date spec"""
988
988
989 df = util.matchdate(date)
989 df = util.matchdate(date)
990 m = matchall(repo)
990 m = matchall(repo)
991 results = {}
991 results = {}
992
992
993 def prep(ctx, fns):
993 def prep(ctx, fns):
994 d = ctx.date()
994 d = ctx.date()
995 if df(d[0]):
995 if df(d[0]):
996 results[ctx.rev()] = d
996 results[ctx.rev()] = d
997
997
998 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
998 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
999 rev = ctx.rev()
999 rev = ctx.rev()
1000 if rev in results:
1000 if rev in results:
1001 ui.status(_("Found revision %s from %s\n") %
1001 ui.status(_("Found revision %s from %s\n") %
1002 (rev, util.datestr(results[rev])))
1002 (rev, util.datestr(results[rev])))
1003 return str(rev)
1003 return str(rev)
1004
1004
1005 raise util.Abort(_("revision matching date not found"))
1005 raise util.Abort(_("revision matching date not found"))
1006
1006
1007 def walkchangerevs(repo, match, opts, prepare):
1007 def walkchangerevs(repo, match, opts, prepare):
1008 '''Iterate over files and the revs in which they changed.
1008 '''Iterate over files and the revs in which they changed.
1009
1009
1010 Callers most commonly need to iterate backwards over the history
1010 Callers most commonly need to iterate backwards over the history
1011 in which they are interested. Doing so has awful (quadratic-looking)
1011 in which they are interested. Doing so has awful (quadratic-looking)
1012 performance, so we use iterators in a "windowed" way.
1012 performance, so we use iterators in a "windowed" way.
1013
1013
1014 We walk a window of revisions in the desired order. Within the
1014 We walk a window of revisions in the desired order. Within the
1015 window, we first walk forwards to gather data, then in the desired
1015 window, we first walk forwards to gather data, then in the desired
1016 order (usually backwards) to display it.
1016 order (usually backwards) to display it.
1017
1017
1018 This function returns an iterator yielding contexts. Before
1018 This function returns an iterator yielding contexts. Before
1019 yielding each context, the iterator will first call the prepare
1019 yielding each context, the iterator will first call the prepare
1020 function on each context in the window in forward order.'''
1020 function on each context in the window in forward order.'''
1021
1021
1022 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1022 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1023 if start < end:
1023 if start < end:
1024 while start < end:
1024 while start < end:
1025 yield start, min(windowsize, end - start)
1025 yield start, min(windowsize, end - start)
1026 start += windowsize
1026 start += windowsize
1027 if windowsize < sizelimit:
1027 if windowsize < sizelimit:
1028 windowsize *= 2
1028 windowsize *= 2
1029 else:
1029 else:
1030 while start > end:
1030 while start > end:
1031 yield start, min(windowsize, start - end - 1)
1031 yield start, min(windowsize, start - end - 1)
1032 start -= windowsize
1032 start -= windowsize
1033 if windowsize < sizelimit:
1033 if windowsize < sizelimit:
1034 windowsize *= 2
1034 windowsize *= 2
1035
1035
1036 follow = opts.get('follow') or opts.get('follow_first')
1036 follow = opts.get('follow') or opts.get('follow_first')
1037
1037
1038 if not len(repo):
1038 if not len(repo):
1039 return []
1039 return []
1040
1040
1041 if follow:
1041 if follow:
1042 defrange = '%s:0' % repo['.'].rev()
1042 defrange = '%s:0' % repo['.'].rev()
1043 else:
1043 else:
1044 defrange = '-1:0'
1044 defrange = '-1:0'
1045 revs = revrange(repo, opts['rev'] or [defrange])
1045 revs = revrange(repo, opts['rev'] or [defrange])
1046 wanted = set()
1046 wanted = set()
1047 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1047 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1048 fncache = {}
1048 fncache = {}
1049 change = util.cachefunc(repo.changectx)
1049 change = util.cachefunc(repo.changectx)
1050
1050
1051 if not slowpath and not match.files():
1051 if not slowpath and not match.files():
1052 # No files, no patterns. Display all revs.
1052 # No files, no patterns. Display all revs.
1053 wanted = set(revs)
1053 wanted = set(revs)
1054 copies = []
1054 copies = []
1055
1055
1056 if not slowpath:
1056 if not slowpath:
1057 # Only files, no patterns. Check the history of each file.
1057 # Only files, no patterns. Check the history of each file.
1058 def filerevgen(filelog, node):
1058 def filerevgen(filelog, node):
1059 cl_count = len(repo)
1059 cl_count = len(repo)
1060 if node is None:
1060 if node is None:
1061 last = len(filelog) - 1
1061 last = len(filelog) - 1
1062 else:
1062 else:
1063 last = filelog.rev(node)
1063 last = filelog.rev(node)
1064 for i, window in increasing_windows(last, nullrev):
1064 for i, window in increasing_windows(last, nullrev):
1065 revs = []
1065 revs = []
1066 for j in xrange(i - window, i + 1):
1066 for j in xrange(i - window, i + 1):
1067 n = filelog.node(j)
1067 n = filelog.node(j)
1068 revs.append((filelog.linkrev(j),
1068 revs.append((filelog.linkrev(j),
1069 follow and filelog.renamed(n)))
1069 follow and filelog.renamed(n)))
1070 for rev in reversed(revs):
1070 for rev in reversed(revs):
1071 # only yield rev for which we have the changelog, it can
1071 # only yield rev for which we have the changelog, it can
1072 # happen while doing "hg log" during a pull or commit
1072 # happen while doing "hg log" during a pull or commit
1073 if rev[0] < cl_count:
1073 if rev[0] < cl_count:
1074 yield rev
1074 yield rev
1075 def iterfiles():
1075 def iterfiles():
1076 for filename in match.files():
1076 for filename in match.files():
1077 yield filename, None
1077 yield filename, None
1078 for filename_node in copies:
1078 for filename_node in copies:
1079 yield filename_node
1079 yield filename_node
1080 minrev, maxrev = min(revs), max(revs)
1080 minrev, maxrev = min(revs), max(revs)
1081 for file_, node in iterfiles():
1081 for file_, node in iterfiles():
1082 filelog = repo.file(file_)
1082 filelog = repo.file(file_)
1083 if not len(filelog):
1083 if not len(filelog):
1084 if node is None:
1084 if node is None:
1085 # A zero count may be a directory or deleted file, so
1085 # A zero count may be a directory or deleted file, so
1086 # try to find matching entries on the slow path.
1086 # try to find matching entries on the slow path.
1087 if follow:
1087 if follow:
1088 raise util.Abort(
1088 raise util.Abort(
1089 _('cannot follow nonexistent file: "%s"') % file_)
1089 _('cannot follow nonexistent file: "%s"') % file_)
1090 slowpath = True
1090 slowpath = True
1091 break
1091 break
1092 else:
1092 else:
1093 continue
1093 continue
1094 for rev, copied in filerevgen(filelog, node):
1094 for rev, copied in filerevgen(filelog, node):
1095 if rev <= maxrev:
1095 if rev <= maxrev:
1096 if rev < minrev:
1096 if rev < minrev:
1097 break
1097 break
1098 fncache.setdefault(rev, [])
1098 fncache.setdefault(rev, [])
1099 fncache[rev].append(file_)
1099 fncache[rev].append(file_)
1100 wanted.add(rev)
1100 wanted.add(rev)
1101 if copied:
1101 if copied:
1102 copies.append(copied)
1102 copies.append(copied)
1103 if slowpath:
1103 if slowpath:
1104 if follow:
1104 if follow:
1105 raise util.Abort(_('can only follow copies/renames for explicit '
1105 raise util.Abort(_('can only follow copies/renames for explicit '
1106 'filenames'))
1106 'filenames'))
1107
1107
1108 # The slow path checks files modified in every changeset.
1108 # The slow path checks files modified in every changeset.
1109 def changerevgen():
1109 def changerevgen():
1110 for i, window in increasing_windows(len(repo) - 1, nullrev):
1110 for i, window in increasing_windows(len(repo) - 1, nullrev):
1111 for j in xrange(i - window, i + 1):
1111 for j in xrange(i - window, i + 1):
1112 yield change(j)
1112 yield change(j)
1113
1113
1114 for ctx in changerevgen():
1114 for ctx in changerevgen():
1115 matches = filter(match, ctx.files())
1115 matches = filter(match, ctx.files())
1116 if matches:
1116 if matches:
1117 fncache[ctx.rev()] = matches
1117 fncache[ctx.rev()] = matches
1118 wanted.add(ctx.rev())
1118 wanted.add(ctx.rev())
1119
1119
1120 class followfilter(object):
1120 class followfilter(object):
1121 def __init__(self, onlyfirst=False):
1121 def __init__(self, onlyfirst=False):
1122 self.startrev = nullrev
1122 self.startrev = nullrev
1123 self.roots = set()
1123 self.roots = set()
1124 self.onlyfirst = onlyfirst
1124 self.onlyfirst = onlyfirst
1125
1125
1126 def match(self, rev):
1126 def match(self, rev):
1127 def realparents(rev):
1127 def realparents(rev):
1128 if self.onlyfirst:
1128 if self.onlyfirst:
1129 return repo.changelog.parentrevs(rev)[0:1]
1129 return repo.changelog.parentrevs(rev)[0:1]
1130 else:
1130 else:
1131 return filter(lambda x: x != nullrev,
1131 return filter(lambda x: x != nullrev,
1132 repo.changelog.parentrevs(rev))
1132 repo.changelog.parentrevs(rev))
1133
1133
1134 if self.startrev == nullrev:
1134 if self.startrev == nullrev:
1135 self.startrev = rev
1135 self.startrev = rev
1136 return True
1136 return True
1137
1137
1138 if rev > self.startrev:
1138 if rev > self.startrev:
1139 # forward: all descendants
1139 # forward: all descendants
1140 if not self.roots:
1140 if not self.roots:
1141 self.roots.add(self.startrev)
1141 self.roots.add(self.startrev)
1142 for parent in realparents(rev):
1142 for parent in realparents(rev):
1143 if parent in self.roots:
1143 if parent in self.roots:
1144 self.roots.add(rev)
1144 self.roots.add(rev)
1145 return True
1145 return True
1146 else:
1146 else:
1147 # backwards: all parents
1147 # backwards: all parents
1148 if not self.roots:
1148 if not self.roots:
1149 self.roots.update(realparents(self.startrev))
1149 self.roots.update(realparents(self.startrev))
1150 if rev in self.roots:
1150 if rev in self.roots:
1151 self.roots.remove(rev)
1151 self.roots.remove(rev)
1152 self.roots.update(realparents(rev))
1152 self.roots.update(realparents(rev))
1153 return True
1153 return True
1154
1154
1155 return False
1155 return False
1156
1156
1157 # it might be worthwhile to do this in the iterator if the rev range
1157 # it might be worthwhile to do this in the iterator if the rev range
1158 # is descending and the prune args are all within that range
1158 # is descending and the prune args are all within that range
1159 for rev in opts.get('prune', ()):
1159 for rev in opts.get('prune', ()):
1160 rev = repo.changelog.rev(repo.lookup(rev))
1160 rev = repo.changelog.rev(repo.lookup(rev))
1161 ff = followfilter()
1161 ff = followfilter()
1162 stop = min(revs[0], revs[-1])
1162 stop = min(revs[0], revs[-1])
1163 for x in xrange(rev, stop - 1, -1):
1163 for x in xrange(rev, stop - 1, -1):
1164 if ff.match(x):
1164 if ff.match(x):
1165 wanted.discard(x)
1165 wanted.discard(x)
1166
1166
1167 def iterate():
1167 def iterate():
1168 if follow and not match.files():
1168 if follow and not match.files():
1169 ff = followfilter(onlyfirst=opts.get('follow_first'))
1169 ff = followfilter(onlyfirst=opts.get('follow_first'))
1170 def want(rev):
1170 def want(rev):
1171 return ff.match(rev) and rev in wanted
1171 return ff.match(rev) and rev in wanted
1172 else:
1172 else:
1173 def want(rev):
1173 def want(rev):
1174 return rev in wanted
1174 return rev in wanted
1175
1175
1176 for i, window in increasing_windows(0, len(revs)):
1176 for i, window in increasing_windows(0, len(revs)):
1177 change = util.cachefunc(repo.changectx)
1177 change = util.cachefunc(repo.changectx)
1178 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1178 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1179 for rev in sorted(nrevs):
1179 for rev in sorted(nrevs):
1180 fns = fncache.get(rev)
1180 fns = fncache.get(rev)
1181 ctx = change(rev)
1181 ctx = change(rev)
1182 if not fns:
1182 if not fns:
1183 def fns_generator():
1183 def fns_generator():
1184 for f in ctx.files():
1184 for f in ctx.files():
1185 if match(f):
1185 if match(f):
1186 yield f
1186 yield f
1187 fns = fns_generator()
1187 fns = fns_generator()
1188 prepare(ctx, fns)
1188 prepare(ctx, fns)
1189 for rev in nrevs:
1189 for rev in nrevs:
1190 yield change(rev)
1190 yield change(rev)
1191 return iterate()
1191 return iterate()
1192
1192
1193 def commit(ui, repo, commitfunc, pats, opts):
1193 def commit(ui, repo, commitfunc, pats, opts):
1194 '''commit the specified files or all outstanding changes'''
1194 '''commit the specified files or all outstanding changes'''
1195 date = opts.get('date')
1195 date = opts.get('date')
1196 if date:
1196 if date:
1197 opts['date'] = util.parsedate(date)
1197 opts['date'] = util.parsedate(date)
1198 message = logmessage(opts)
1198 message = logmessage(opts)
1199
1199
1200 # extract addremove carefully -- this function can be called from a command
1200 # extract addremove carefully -- this function can be called from a command
1201 # that doesn't support addremove
1201 # that doesn't support addremove
1202 if opts.get('addremove'):
1202 if opts.get('addremove'):
1203 addremove(repo, pats, opts)
1203 addremove(repo, pats, opts)
1204
1204
1205 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1205 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1206
1206
1207 def commiteditor(repo, ctx, subs):
1207 def commiteditor(repo, ctx, subs):
1208 if ctx.description():
1208 if ctx.description():
1209 return ctx.description()
1209 return ctx.description()
1210 return commitforceeditor(repo, ctx, subs)
1210 return commitforceeditor(repo, ctx, subs)
1211
1211
1212 def commitforceeditor(repo, ctx, subs):
1212 def commitforceeditor(repo, ctx, subs):
1213 edittext = []
1213 edittext = []
1214 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1214 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1215 if ctx.description():
1215 if ctx.description():
1216 edittext.append(ctx.description())
1216 edittext.append(ctx.description())
1217 edittext.append("")
1217 edittext.append("")
1218 edittext.append("") # Empty line between message and comments.
1218 edittext.append("") # Empty line between message and comments.
1219 edittext.append(_("HG: Enter commit message."
1219 edittext.append(_("HG: Enter commit message."
1220 " Lines beginning with 'HG:' are removed."))
1220 " Lines beginning with 'HG:' are removed."))
1221 edittext.append(_("HG: Leave message empty to abort commit."))
1221 edittext.append(_("HG: Leave message empty to abort commit."))
1222 edittext.append("HG: --")
1222 edittext.append("HG: --")
1223 edittext.append(_("HG: user: %s") % ctx.user())
1223 edittext.append(_("HG: user: %s") % ctx.user())
1224 if ctx.p2():
1224 if ctx.p2():
1225 edittext.append(_("HG: branch merge"))
1225 edittext.append(_("HG: branch merge"))
1226 if ctx.branch():
1226 if ctx.branch():
1227 edittext.append(_("HG: branch '%s'")
1227 edittext.append(_("HG: branch '%s'")
1228 % encoding.tolocal(ctx.branch()))
1228 % encoding.tolocal(ctx.branch()))
1229 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1229 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1230 edittext.extend([_("HG: added %s") % f for f in added])
1230 edittext.extend([_("HG: added %s") % f for f in added])
1231 edittext.extend([_("HG: changed %s") % f for f in modified])
1231 edittext.extend([_("HG: changed %s") % f for f in modified])
1232 edittext.extend([_("HG: removed %s") % f for f in removed])
1232 edittext.extend([_("HG: removed %s") % f for f in removed])
1233 if not added and not modified and not removed:
1233 if not added and not modified and not removed:
1234 edittext.append(_("HG: no files changed"))
1234 edittext.append(_("HG: no files changed"))
1235 edittext.append("")
1235 edittext.append("")
1236 # run editor in the repository root
1236 # run editor in the repository root
1237 olddir = os.getcwd()
1237 olddir = os.getcwd()
1238 os.chdir(repo.root)
1238 os.chdir(repo.root)
1239 text = repo.ui.edit("\n".join(edittext), ctx.user())
1239 text = repo.ui.edit("\n".join(edittext), ctx.user())
1240 text = re.sub("(?m)^HG:.*\n", "", text)
1240 text = re.sub("(?m)^HG:.*\n", "", text)
1241 os.chdir(olddir)
1241 os.chdir(olddir)
1242
1242
1243 if not text.strip():
1243 if not text.strip():
1244 raise util.Abort(_("empty commit message"))
1244 raise util.Abort(_("empty commit message"))
1245
1245
1246 return text
1246 return text
@@ -1,3951 +1,4068 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, error
12 import hg, util, revlog, bundlerepo, extensions, copies, error
13 import patch, help, mdiff, url, encoding, templatekw
13 import patch, help, mdiff, url, encoding, templatekw
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 import merge as mergemod
15 import merge as mergemod
16 import minirst
16 import minirst
17
17
18 # Commands start here, listed alphabetically
18 # Commands start here, listed alphabetically
19
19
20 def add(ui, repo, *pats, **opts):
20 def add(ui, repo, *pats, **opts):
21 """add the specified files on the next commit
21 """add the specified files on the next commit
22
22
23 Schedule files to be version controlled and added to the
23 Schedule files to be version controlled and added to the
24 repository.
24 repository.
25
25
26 The files will be added to the repository at the next commit. To
26 The files will be added to the repository at the next commit. To
27 undo an add before that, see hg forget.
27 undo an add before that, see hg forget.
28
28
29 If no names are given, add all files to the repository.
29 If no names are given, add all files to the repository.
30
30
31 .. container:: verbose
31 .. container:: verbose
32
32
33 An example showing how new (unknown) files are added
33 An example showing how new (unknown) files are added
34 automatically by :hg:`add`::
34 automatically by :hg:`add`::
35
35
36 $ ls
36 $ ls
37 foo.c
37 foo.c
38 $ hg status
38 $ hg status
39 ? foo.c
39 ? foo.c
40 $ hg add
40 $ hg add
41 adding foo.c
41 adding foo.c
42 $ hg status
42 $ hg status
43 A foo.c
43 A foo.c
44 """
44 """
45
45
46 bad = []
46 bad = []
47 names = []
47 names = []
48 m = cmdutil.match(repo, pats, opts)
48 m = cmdutil.match(repo, pats, opts)
49 oldbad = m.bad
49 oldbad = m.bad
50 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
50 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
51
51
52 for f in repo.walk(m):
52 for f in repo.walk(m):
53 exact = m.exact(f)
53 exact = m.exact(f)
54 if exact or f not in repo.dirstate:
54 if exact or f not in repo.dirstate:
55 names.append(f)
55 names.append(f)
56 if ui.verbose or not exact:
56 if ui.verbose or not exact:
57 ui.status(_('adding %s\n') % m.rel(f))
57 ui.status(_('adding %s\n') % m.rel(f))
58 if not opts.get('dry_run'):
58 if not opts.get('dry_run'):
59 bad += [f for f in repo.add(names) if f in m.files()]
59 bad += [f for f in repo.add(names) if f in m.files()]
60 return bad and 1 or 0
60 return bad and 1 or 0
61
61
62 def addremove(ui, repo, *pats, **opts):
62 def addremove(ui, repo, *pats, **opts):
63 """add all new files, delete all missing files
63 """add all new files, delete all missing files
64
64
65 Add all new files and remove all missing files from the
65 Add all new files and remove all missing files from the
66 repository.
66 repository.
67
67
68 New files are ignored if they match any of the patterns in
68 New files are ignored if they match any of the patterns in
69 .hgignore. As with add, these changes take effect at the next
69 .hgignore. As with add, these changes take effect at the next
70 commit.
70 commit.
71
71
72 Use the -s/--similarity option to detect renamed files. With a
72 Use the -s/--similarity option to detect renamed files. With a
73 parameter greater than 0, this compares every removed file with
73 parameter greater than 0, this compares every removed file with
74 every added file and records those similar enough as renames. This
74 every added file and records those similar enough as renames. This
75 option takes a percentage between 0 (disabled) and 100 (files must
75 option takes a percentage between 0 (disabled) and 100 (files must
76 be identical) as its parameter. Detecting renamed files this way
76 be identical) as its parameter. Detecting renamed files this way
77 can be expensive.
77 can be expensive.
78
79 Returns 0 if all files are successfully added.
78 """
80 """
79 try:
81 try:
80 sim = float(opts.get('similarity') or 0)
82 sim = float(opts.get('similarity') or 0)
81 except ValueError:
83 except ValueError:
82 raise util.Abort(_('similarity must be a number'))
84 raise util.Abort(_('similarity must be a number'))
83 if sim < 0 or sim > 100:
85 if sim < 0 or sim > 100:
84 raise util.Abort(_('similarity must be between 0 and 100'))
86 raise util.Abort(_('similarity must be between 0 and 100'))
85 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
87 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
86
88
87 def annotate(ui, repo, *pats, **opts):
89 def annotate(ui, repo, *pats, **opts):
88 """show changeset information by line for each file
90 """show changeset information by line for each file
89
91
90 List changes in files, showing the revision id responsible for
92 List changes in files, showing the revision id responsible for
91 each line
93 each line
92
94
93 This command is useful for discovering when a change was made and
95 This command is useful for discovering when a change was made and
94 by whom.
96 by whom.
95
97
96 Without the -a/--text option, annotate will avoid processing files
98 Without the -a/--text option, annotate will avoid processing files
97 it detects as binary. With -a, annotate will annotate the file
99 it detects as binary. With -a, annotate will annotate the file
98 anyway, although the results will probably be neither useful
100 anyway, although the results will probably be neither useful
99 nor desirable.
101 nor desirable.
102
103 Returns 0 on success.
100 """
104 """
101 if opts.get('follow'):
105 if opts.get('follow'):
102 # --follow is deprecated and now just an alias for -f/--file
106 # --follow is deprecated and now just an alias for -f/--file
103 # to mimic the behavior of Mercurial before version 1.5
107 # to mimic the behavior of Mercurial before version 1.5
104 opts['file'] = 1
108 opts['file'] = 1
105
109
106 datefunc = ui.quiet and util.shortdate or util.datestr
110 datefunc = ui.quiet and util.shortdate or util.datestr
107 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
111 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
108
112
109 if not pats:
113 if not pats:
110 raise util.Abort(_('at least one filename or pattern is required'))
114 raise util.Abort(_('at least one filename or pattern is required'))
111
115
112 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
116 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
113 ('number', lambda x: str(x[0].rev())),
117 ('number', lambda x: str(x[0].rev())),
114 ('changeset', lambda x: short(x[0].node())),
118 ('changeset', lambda x: short(x[0].node())),
115 ('date', getdate),
119 ('date', getdate),
116 ('file', lambda x: x[0].path()),
120 ('file', lambda x: x[0].path()),
117 ]
121 ]
118
122
119 if (not opts.get('user') and not opts.get('changeset')
123 if (not opts.get('user') and not opts.get('changeset')
120 and not opts.get('date') and not opts.get('file')):
124 and not opts.get('date') and not opts.get('file')):
121 opts['number'] = 1
125 opts['number'] = 1
122
126
123 linenumber = opts.get('line_number') is not None
127 linenumber = opts.get('line_number') is not None
124 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
128 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
125 raise util.Abort(_('at least one of -n/-c is required for -l'))
129 raise util.Abort(_('at least one of -n/-c is required for -l'))
126
130
127 funcmap = [func for op, func in opmap if opts.get(op)]
131 funcmap = [func for op, func in opmap if opts.get(op)]
128 if linenumber:
132 if linenumber:
129 lastfunc = funcmap[-1]
133 lastfunc = funcmap[-1]
130 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
134 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
131
135
132 ctx = repo[opts.get('rev')]
136 ctx = repo[opts.get('rev')]
133 m = cmdutil.match(repo, pats, opts)
137 m = cmdutil.match(repo, pats, opts)
134 follow = not opts.get('no_follow')
138 follow = not opts.get('no_follow')
135 for abs in ctx.walk(m):
139 for abs in ctx.walk(m):
136 fctx = ctx[abs]
140 fctx = ctx[abs]
137 if not opts.get('text') and util.binary(fctx.data()):
141 if not opts.get('text') and util.binary(fctx.data()):
138 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
142 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
139 continue
143 continue
140
144
141 lines = fctx.annotate(follow=follow, linenumber=linenumber)
145 lines = fctx.annotate(follow=follow, linenumber=linenumber)
142 pieces = []
146 pieces = []
143
147
144 for f in funcmap:
148 for f in funcmap:
145 l = [f(n) for n, dummy in lines]
149 l = [f(n) for n, dummy in lines]
146 if l:
150 if l:
147 ml = max(map(len, l))
151 ml = max(map(len, l))
148 pieces.append(["%*s" % (ml, x) for x in l])
152 pieces.append(["%*s" % (ml, x) for x in l])
149
153
150 if pieces:
154 if pieces:
151 for p, l in zip(zip(*pieces), lines):
155 for p, l in zip(zip(*pieces), lines):
152 ui.write("%s: %s" % (" ".join(p), l[1]))
156 ui.write("%s: %s" % (" ".join(p), l[1]))
153
157
154 def archive(ui, repo, dest, **opts):
158 def archive(ui, repo, dest, **opts):
155 '''create an unversioned archive of a repository revision
159 '''create an unversioned archive of a repository revision
156
160
157 By default, the revision used is the parent of the working
161 By default, the revision used is the parent of the working
158 directory; use -r/--rev to specify a different revision.
162 directory; use -r/--rev to specify a different revision.
159
163
160 The archive type is automatically detected based on file
164 The archive type is automatically detected based on file
161 extension (or override using -t/--type).
165 extension (or override using -t/--type).
162
166
163 Valid types are:
167 Valid types are:
164
168
165 :``files``: a directory full of files (default)
169 :``files``: a directory full of files (default)
166 :``tar``: tar archive, uncompressed
170 :``tar``: tar archive, uncompressed
167 :``tbz2``: tar archive, compressed using bzip2
171 :``tbz2``: tar archive, compressed using bzip2
168 :``tgz``: tar archive, compressed using gzip
172 :``tgz``: tar archive, compressed using gzip
169 :``uzip``: zip archive, uncompressed
173 :``uzip``: zip archive, uncompressed
170 :``zip``: zip archive, compressed using deflate
174 :``zip``: zip archive, compressed using deflate
171
175
172 The exact name of the destination archive or directory is given
176 The exact name of the destination archive or directory is given
173 using a format string; see :hg:`help export` for details.
177 using a format string; see :hg:`help export` for details.
174
178
175 Each member added to an archive file has a directory prefix
179 Each member added to an archive file has a directory prefix
176 prepended. Use -p/--prefix to specify a format string for the
180 prepended. Use -p/--prefix to specify a format string for the
177 prefix. The default is the basename of the archive, with suffixes
181 prefix. The default is the basename of the archive, with suffixes
178 removed.
182 removed.
183
184 Returns 0 on success.
179 '''
185 '''
180
186
181 ctx = repo[opts.get('rev')]
187 ctx = repo[opts.get('rev')]
182 if not ctx:
188 if not ctx:
183 raise util.Abort(_('no working directory: please specify a revision'))
189 raise util.Abort(_('no working directory: please specify a revision'))
184 node = ctx.node()
190 node = ctx.node()
185 dest = cmdutil.make_filename(repo, dest, node)
191 dest = cmdutil.make_filename(repo, dest, node)
186 if os.path.realpath(dest) == repo.root:
192 if os.path.realpath(dest) == repo.root:
187 raise util.Abort(_('repository root cannot be destination'))
193 raise util.Abort(_('repository root cannot be destination'))
188
194
189 def guess_type():
195 def guess_type():
190 exttypes = {
196 exttypes = {
191 'tar': ['.tar'],
197 'tar': ['.tar'],
192 'tbz2': ['.tbz2', '.tar.bz2'],
198 'tbz2': ['.tbz2', '.tar.bz2'],
193 'tgz': ['.tgz', '.tar.gz'],
199 'tgz': ['.tgz', '.tar.gz'],
194 'zip': ['.zip'],
200 'zip': ['.zip'],
195 }
201 }
196
202
197 for type, extensions in exttypes.items():
203 for type, extensions in exttypes.items():
198 if util.any(dest.endswith(ext) for ext in extensions):
204 if util.any(dest.endswith(ext) for ext in extensions):
199 return type
205 return type
200 return None
206 return None
201
207
202 kind = opts.get('type') or guess_type() or 'files'
208 kind = opts.get('type') or guess_type() or 'files'
203 prefix = opts.get('prefix')
209 prefix = opts.get('prefix')
204
210
205 if dest == '-':
211 if dest == '-':
206 if kind == 'files':
212 if kind == 'files':
207 raise util.Abort(_('cannot archive plain files to stdout'))
213 raise util.Abort(_('cannot archive plain files to stdout'))
208 dest = sys.stdout
214 dest = sys.stdout
209 if not prefix:
215 if not prefix:
210 prefix = os.path.basename(repo.root) + '-%h'
216 prefix = os.path.basename(repo.root) + '-%h'
211
217
212 prefix = cmdutil.make_filename(repo, prefix, node)
218 prefix = cmdutil.make_filename(repo, prefix, node)
213 matchfn = cmdutil.match(repo, [], opts)
219 matchfn = cmdutil.match(repo, [], opts)
214 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
220 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
215 matchfn, prefix)
221 matchfn, prefix)
216
222
217 def backout(ui, repo, node=None, rev=None, **opts):
223 def backout(ui, repo, node=None, rev=None, **opts):
218 '''reverse effect of earlier changeset
224 '''reverse effect of earlier changeset
219
225
220 Commit the backed out changes as a new changeset. The new
226 Commit the backed out changes as a new changeset. The new
221 changeset is a child of the backed out changeset.
227 changeset is a child of the backed out changeset.
222
228
223 If you backout a changeset other than the tip, a new head is
229 If you backout a changeset other than the tip, a new head is
224 created. This head will be the new tip and you should merge this
230 created. This head will be the new tip and you should merge this
225 backout changeset with another head.
231 backout changeset with another head.
226
232
227 The --merge option remembers the parent of the working directory
233 The --merge option remembers the parent of the working directory
228 before starting the backout, then merges the new head with that
234 before starting the backout, then merges the new head with that
229 changeset afterwards. This saves you from doing the merge by hand.
235 changeset afterwards. This saves you from doing the merge by hand.
230 The result of this merge is not committed, as with a normal merge.
236 The result of this merge is not committed, as with a normal merge.
231
237
232 See :hg:`help dates` for a list of formats valid for -d/--date.
238 See :hg:`help dates` for a list of formats valid for -d/--date.
239
240 Returns 0 on success.
233 '''
241 '''
234 if rev and node:
242 if rev and node:
235 raise util.Abort(_("please specify just one revision"))
243 raise util.Abort(_("please specify just one revision"))
236
244
237 if not rev:
245 if not rev:
238 rev = node
246 rev = node
239
247
240 if not rev:
248 if not rev:
241 raise util.Abort(_("please specify a revision to backout"))
249 raise util.Abort(_("please specify a revision to backout"))
242
250
243 date = opts.get('date')
251 date = opts.get('date')
244 if date:
252 if date:
245 opts['date'] = util.parsedate(date)
253 opts['date'] = util.parsedate(date)
246
254
247 cmdutil.bail_if_changed(repo)
255 cmdutil.bail_if_changed(repo)
248 node = repo.lookup(rev)
256 node = repo.lookup(rev)
249
257
250 op1, op2 = repo.dirstate.parents()
258 op1, op2 = repo.dirstate.parents()
251 a = repo.changelog.ancestor(op1, node)
259 a = repo.changelog.ancestor(op1, node)
252 if a != node:
260 if a != node:
253 raise util.Abort(_('cannot backout change on a different branch'))
261 raise util.Abort(_('cannot backout change on a different branch'))
254
262
255 p1, p2 = repo.changelog.parents(node)
263 p1, p2 = repo.changelog.parents(node)
256 if p1 == nullid:
264 if p1 == nullid:
257 raise util.Abort(_('cannot backout a change with no parents'))
265 raise util.Abort(_('cannot backout a change with no parents'))
258 if p2 != nullid:
266 if p2 != nullid:
259 if not opts.get('parent'):
267 if not opts.get('parent'):
260 raise util.Abort(_('cannot backout a merge changeset without '
268 raise util.Abort(_('cannot backout a merge changeset without '
261 '--parent'))
269 '--parent'))
262 p = repo.lookup(opts['parent'])
270 p = repo.lookup(opts['parent'])
263 if p not in (p1, p2):
271 if p not in (p1, p2):
264 raise util.Abort(_('%s is not a parent of %s') %
272 raise util.Abort(_('%s is not a parent of %s') %
265 (short(p), short(node)))
273 (short(p), short(node)))
266 parent = p
274 parent = p
267 else:
275 else:
268 if opts.get('parent'):
276 if opts.get('parent'):
269 raise util.Abort(_('cannot use --parent on non-merge changeset'))
277 raise util.Abort(_('cannot use --parent on non-merge changeset'))
270 parent = p1
278 parent = p1
271
279
272 # the backout should appear on the same branch
280 # the backout should appear on the same branch
273 branch = repo.dirstate.branch()
281 branch = repo.dirstate.branch()
274 hg.clean(repo, node, show_stats=False)
282 hg.clean(repo, node, show_stats=False)
275 repo.dirstate.setbranch(branch)
283 repo.dirstate.setbranch(branch)
276 revert_opts = opts.copy()
284 revert_opts = opts.copy()
277 revert_opts['date'] = None
285 revert_opts['date'] = None
278 revert_opts['all'] = True
286 revert_opts['all'] = True
279 revert_opts['rev'] = hex(parent)
287 revert_opts['rev'] = hex(parent)
280 revert_opts['no_backup'] = None
288 revert_opts['no_backup'] = None
281 revert(ui, repo, **revert_opts)
289 revert(ui, repo, **revert_opts)
282 commit_opts = opts.copy()
290 commit_opts = opts.copy()
283 commit_opts['addremove'] = False
291 commit_opts['addremove'] = False
284 if not commit_opts['message'] and not commit_opts['logfile']:
292 if not commit_opts['message'] and not commit_opts['logfile']:
285 # we don't translate commit messages
293 # we don't translate commit messages
286 commit_opts['message'] = "Backed out changeset %s" % short(node)
294 commit_opts['message'] = "Backed out changeset %s" % short(node)
287 commit_opts['force_editor'] = True
295 commit_opts['force_editor'] = True
288 commit(ui, repo, **commit_opts)
296 commit(ui, repo, **commit_opts)
289 def nice(node):
297 def nice(node):
290 return '%d:%s' % (repo.changelog.rev(node), short(node))
298 return '%d:%s' % (repo.changelog.rev(node), short(node))
291 ui.status(_('changeset %s backs out changeset %s\n') %
299 ui.status(_('changeset %s backs out changeset %s\n') %
292 (nice(repo.changelog.tip()), nice(node)))
300 (nice(repo.changelog.tip()), nice(node)))
293 if op1 != node:
301 if op1 != node:
294 hg.clean(repo, op1, show_stats=False)
302 hg.clean(repo, op1, show_stats=False)
295 if opts.get('merge'):
303 if opts.get('merge'):
296 ui.status(_('merging with changeset %s\n')
304 ui.status(_('merging with changeset %s\n')
297 % nice(repo.changelog.tip()))
305 % nice(repo.changelog.tip()))
298 hg.merge(repo, hex(repo.changelog.tip()))
306 hg.merge(repo, hex(repo.changelog.tip()))
299 else:
307 else:
300 ui.status(_('the backout changeset is a new head - '
308 ui.status(_('the backout changeset is a new head - '
301 'do not forget to merge\n'))
309 'do not forget to merge\n'))
302 ui.status(_('(use "backout --merge" '
310 ui.status(_('(use "backout --merge" '
303 'if you want to auto-merge)\n'))
311 'if you want to auto-merge)\n'))
304
312
305 def bisect(ui, repo, rev=None, extra=None, command=None,
313 def bisect(ui, repo, rev=None, extra=None, command=None,
306 reset=None, good=None, bad=None, skip=None, noupdate=None):
314 reset=None, good=None, bad=None, skip=None, noupdate=None):
307 """subdivision search of changesets
315 """subdivision search of changesets
308
316
309 This command helps to find changesets which introduce problems. To
317 This command helps to find changesets which introduce problems. To
310 use, mark the earliest changeset you know exhibits the problem as
318 use, mark the earliest changeset you know exhibits the problem as
311 bad, then mark the latest changeset which is free from the problem
319 bad, then mark the latest changeset which is free from the problem
312 as good. Bisect will update your working directory to a revision
320 as good. Bisect will update your working directory to a revision
313 for testing (unless the -U/--noupdate option is specified). Once
321 for testing (unless the -U/--noupdate option is specified). Once
314 you have performed tests, mark the working directory as good or
322 you have performed tests, mark the working directory as good or
315 bad, and bisect will either update to another candidate changeset
323 bad, and bisect will either update to another candidate changeset
316 or announce that it has found the bad revision.
324 or announce that it has found the bad revision.
317
325
318 As a shortcut, you can also use the revision argument to mark a
326 As a shortcut, you can also use the revision argument to mark a
319 revision as good or bad without checking it out first.
327 revision as good or bad without checking it out first.
320
328
321 If you supply a command, it will be used for automatic bisection.
329 If you supply a command, it will be used for automatic bisection.
322 Its exit status will be used to mark revisions as good or bad:
330 Its exit status will be used to mark revisions as good or bad:
323 status 0 means good, 125 means to skip the revision, 127
331 status 0 means good, 125 means to skip the revision, 127
324 (command not found) will abort the bisection, and any other
332 (command not found) will abort the bisection, and any other
325 non-zero exit status means the revision is bad.
333 non-zero exit status means the revision is bad.
334
335 Returns 0 on success.
326 """
336 """
327 def print_result(nodes, good):
337 def print_result(nodes, good):
328 displayer = cmdutil.show_changeset(ui, repo, {})
338 displayer = cmdutil.show_changeset(ui, repo, {})
329 if len(nodes) == 1:
339 if len(nodes) == 1:
330 # narrowed it down to a single revision
340 # narrowed it down to a single revision
331 if good:
341 if good:
332 ui.write(_("The first good revision is:\n"))
342 ui.write(_("The first good revision is:\n"))
333 else:
343 else:
334 ui.write(_("The first bad revision is:\n"))
344 ui.write(_("The first bad revision is:\n"))
335 displayer.show(repo[nodes[0]])
345 displayer.show(repo[nodes[0]])
336 else:
346 else:
337 # multiple possible revisions
347 # multiple possible revisions
338 if good:
348 if good:
339 ui.write(_("Due to skipped revisions, the first "
349 ui.write(_("Due to skipped revisions, the first "
340 "good revision could be any of:\n"))
350 "good revision could be any of:\n"))
341 else:
351 else:
342 ui.write(_("Due to skipped revisions, the first "
352 ui.write(_("Due to skipped revisions, the first "
343 "bad revision could be any of:\n"))
353 "bad revision could be any of:\n"))
344 for n in nodes:
354 for n in nodes:
345 displayer.show(repo[n])
355 displayer.show(repo[n])
346 displayer.close()
356 displayer.close()
347
357
348 def check_state(state, interactive=True):
358 def check_state(state, interactive=True):
349 if not state['good'] or not state['bad']:
359 if not state['good'] or not state['bad']:
350 if (good or bad or skip or reset) and interactive:
360 if (good or bad or skip or reset) and interactive:
351 return
361 return
352 if not state['good']:
362 if not state['good']:
353 raise util.Abort(_('cannot bisect (no known good revisions)'))
363 raise util.Abort(_('cannot bisect (no known good revisions)'))
354 else:
364 else:
355 raise util.Abort(_('cannot bisect (no known bad revisions)'))
365 raise util.Abort(_('cannot bisect (no known bad revisions)'))
356 return True
366 return True
357
367
358 # backward compatibility
368 # backward compatibility
359 if rev in "good bad reset init".split():
369 if rev in "good bad reset init".split():
360 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
370 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
361 cmd, rev, extra = rev, extra, None
371 cmd, rev, extra = rev, extra, None
362 if cmd == "good":
372 if cmd == "good":
363 good = True
373 good = True
364 elif cmd == "bad":
374 elif cmd == "bad":
365 bad = True
375 bad = True
366 else:
376 else:
367 reset = True
377 reset = True
368 elif extra or good + bad + skip + reset + bool(command) > 1:
378 elif extra or good + bad + skip + reset + bool(command) > 1:
369 raise util.Abort(_('incompatible arguments'))
379 raise util.Abort(_('incompatible arguments'))
370
380
371 if reset:
381 if reset:
372 p = repo.join("bisect.state")
382 p = repo.join("bisect.state")
373 if os.path.exists(p):
383 if os.path.exists(p):
374 os.unlink(p)
384 os.unlink(p)
375 return
385 return
376
386
377 state = hbisect.load_state(repo)
387 state = hbisect.load_state(repo)
378
388
379 if command:
389 if command:
380 changesets = 1
390 changesets = 1
381 try:
391 try:
382 while changesets:
392 while changesets:
383 # update state
393 # update state
384 status = util.system(command)
394 status = util.system(command)
385 if status == 125:
395 if status == 125:
386 transition = "skip"
396 transition = "skip"
387 elif status == 0:
397 elif status == 0:
388 transition = "good"
398 transition = "good"
389 # status < 0 means process was killed
399 # status < 0 means process was killed
390 elif status == 127:
400 elif status == 127:
391 raise util.Abort(_("failed to execute %s") % command)
401 raise util.Abort(_("failed to execute %s") % command)
392 elif status < 0:
402 elif status < 0:
393 raise util.Abort(_("%s killed") % command)
403 raise util.Abort(_("%s killed") % command)
394 else:
404 else:
395 transition = "bad"
405 transition = "bad"
396 ctx = repo[rev or '.']
406 ctx = repo[rev or '.']
397 state[transition].append(ctx.node())
407 state[transition].append(ctx.node())
398 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
408 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
399 check_state(state, interactive=False)
409 check_state(state, interactive=False)
400 # bisect
410 # bisect
401 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
411 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
402 # update to next check
412 # update to next check
403 cmdutil.bail_if_changed(repo)
413 cmdutil.bail_if_changed(repo)
404 hg.clean(repo, nodes[0], show_stats=False)
414 hg.clean(repo, nodes[0], show_stats=False)
405 finally:
415 finally:
406 hbisect.save_state(repo, state)
416 hbisect.save_state(repo, state)
407 return print_result(nodes, good)
417 print_result(nodes, good)
418 return
408
419
409 # update state
420 # update state
410 node = repo.lookup(rev or '.')
421 node = repo.lookup(rev or '.')
411 if good or bad or skip:
422 if good or bad or skip:
412 if good:
423 if good:
413 state['good'].append(node)
424 state['good'].append(node)
414 elif bad:
425 elif bad:
415 state['bad'].append(node)
426 state['bad'].append(node)
416 elif skip:
427 elif skip:
417 state['skip'].append(node)
428 state['skip'].append(node)
418 hbisect.save_state(repo, state)
429 hbisect.save_state(repo, state)
419
430
420 if not check_state(state):
431 if not check_state(state):
421 return
432 return
422
433
423 # actually bisect
434 # actually bisect
424 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
435 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
425 if changesets == 0:
436 if changesets == 0:
426 print_result(nodes, good)
437 print_result(nodes, good)
427 else:
438 else:
428 assert len(nodes) == 1 # only a single node can be tested next
439 assert len(nodes) == 1 # only a single node can be tested next
429 node = nodes[0]
440 node = nodes[0]
430 # compute the approximate number of remaining tests
441 # compute the approximate number of remaining tests
431 tests, size = 0, 2
442 tests, size = 0, 2
432 while size <= changesets:
443 while size <= changesets:
433 tests, size = tests + 1, size * 2
444 tests, size = tests + 1, size * 2
434 rev = repo.changelog.rev(node)
445 rev = repo.changelog.rev(node)
435 ui.write(_("Testing changeset %d:%s "
446 ui.write(_("Testing changeset %d:%s "
436 "(%d changesets remaining, ~%d tests)\n")
447 "(%d changesets remaining, ~%d tests)\n")
437 % (rev, short(node), changesets, tests))
448 % (rev, short(node), changesets, tests))
438 if not noupdate:
449 if not noupdate:
439 cmdutil.bail_if_changed(repo)
450 cmdutil.bail_if_changed(repo)
440 return hg.clean(repo, node)
451 return hg.clean(repo, node)
441
452
442 def branch(ui, repo, label=None, **opts):
453 def branch(ui, repo, label=None, **opts):
443 """set or show the current branch name
454 """set or show the current branch name
444
455
445 With no argument, show the current branch name. With one argument,
456 With no argument, show the current branch name. With one argument,
446 set the working directory branch name (the branch will not exist
457 set the working directory branch name (the branch will not exist
447 in the repository until the next commit). Standard practice
458 in the repository until the next commit). Standard practice
448 recommends that primary development take place on the 'default'
459 recommends that primary development take place on the 'default'
449 branch.
460 branch.
450
461
451 Unless -f/--force is specified, branch will not let you set a
462 Unless -f/--force is specified, branch will not let you set a
452 branch name that already exists, even if it's inactive.
463 branch name that already exists, even if it's inactive.
453
464
454 Use -C/--clean to reset the working directory branch to that of
465 Use -C/--clean to reset the working directory branch to that of
455 the parent of the working directory, negating a previous branch
466 the parent of the working directory, negating a previous branch
456 change.
467 change.
457
468
458 Use the command :hg:`update` to switch to an existing branch. Use
469 Use the command :hg:`update` to switch to an existing branch. Use
459 :hg:`commit --close-branch` to mark this branch as closed.
470 :hg:`commit --close-branch` to mark this branch as closed.
471
472 Returns 0 on success.
460 """
473 """
461
474
462 if opts.get('clean'):
475 if opts.get('clean'):
463 label = repo[None].parents()[0].branch()
476 label = repo[None].parents()[0].branch()
464 repo.dirstate.setbranch(label)
477 repo.dirstate.setbranch(label)
465 ui.status(_('reset working directory to branch %s\n') % label)
478 ui.status(_('reset working directory to branch %s\n') % label)
466 elif label:
479 elif label:
467 utflabel = encoding.fromlocal(label)
480 utflabel = encoding.fromlocal(label)
468 if not opts.get('force') and utflabel in repo.branchtags():
481 if not opts.get('force') and utflabel in repo.branchtags():
469 if label not in [p.branch() for p in repo.parents()]:
482 if label not in [p.branch() for p in repo.parents()]:
470 raise util.Abort(_('a branch of the same name already exists'
483 raise util.Abort(_('a branch of the same name already exists'
471 " (use 'hg update' to switch to it)"))
484 " (use 'hg update' to switch to it)"))
472 repo.dirstate.setbranch(utflabel)
485 repo.dirstate.setbranch(utflabel)
473 ui.status(_('marked working directory as branch %s\n') % label)
486 ui.status(_('marked working directory as branch %s\n') % label)
474 else:
487 else:
475 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
488 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
476
489
477 def branches(ui, repo, active=False, closed=False):
490 def branches(ui, repo, active=False, closed=False):
478 """list repository named branches
491 """list repository named branches
479
492
480 List the repository's named branches, indicating which ones are
493 List the repository's named branches, indicating which ones are
481 inactive. If -c/--closed is specified, also list branches which have
494 inactive. If -c/--closed is specified, also list branches which have
482 been marked closed (see hg commit --close-branch).
495 been marked closed (see hg commit --close-branch).
483
496
484 If -a/--active is specified, only show active branches. A branch
497 If -a/--active is specified, only show active branches. A branch
485 is considered active if it contains repository heads.
498 is considered active if it contains repository heads.
486
499
487 Use the command :hg:`update` to switch to an existing branch.
500 Use the command :hg:`update` to switch to an existing branch.
501
502 Returns 0.
488 """
503 """
489
504
490 hexfunc = ui.debugflag and hex or short
505 hexfunc = ui.debugflag and hex or short
491 activebranches = [repo[n].branch() for n in repo.heads()]
506 activebranches = [repo[n].branch() for n in repo.heads()]
492 def testactive(tag, node):
507 def testactive(tag, node):
493 realhead = tag in activebranches
508 realhead = tag in activebranches
494 open = node in repo.branchheads(tag, closed=False)
509 open = node in repo.branchheads(tag, closed=False)
495 return realhead and open
510 return realhead and open
496 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
511 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
497 for tag, node in repo.branchtags().items()],
512 for tag, node in repo.branchtags().items()],
498 reverse=True)
513 reverse=True)
499
514
500 for isactive, node, tag in branches:
515 for isactive, node, tag in branches:
501 if (not active) or isactive:
516 if (not active) or isactive:
502 encodedtag = encoding.tolocal(tag)
517 encodedtag = encoding.tolocal(tag)
503 if ui.quiet:
518 if ui.quiet:
504 ui.write("%s\n" % encodedtag)
519 ui.write("%s\n" % encodedtag)
505 else:
520 else:
506 hn = repo.lookup(node)
521 hn = repo.lookup(node)
507 if isactive:
522 if isactive:
508 notice = ''
523 notice = ''
509 elif hn not in repo.branchheads(tag, closed=False):
524 elif hn not in repo.branchheads(tag, closed=False):
510 if not closed:
525 if not closed:
511 continue
526 continue
512 notice = _(' (closed)')
527 notice = _(' (closed)')
513 else:
528 else:
514 notice = _(' (inactive)')
529 notice = _(' (inactive)')
515 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
530 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
516 data = encodedtag, rev, hexfunc(hn), notice
531 data = encodedtag, rev, hexfunc(hn), notice
517 ui.write("%s %s:%s%s\n" % data)
532 ui.write("%s %s:%s%s\n" % data)
518
533
519 def bundle(ui, repo, fname, dest=None, **opts):
534 def bundle(ui, repo, fname, dest=None, **opts):
520 """create a changegroup file
535 """create a changegroup file
521
536
522 Generate a compressed changegroup file collecting changesets not
537 Generate a compressed changegroup file collecting changesets not
523 known to be in another repository.
538 known to be in another repository.
524
539
525 If you omit the destination repository, then hg assumes the
540 If you omit the destination repository, then hg assumes the
526 destination will have all the nodes you specify with --base
541 destination will have all the nodes you specify with --base
527 parameters. To create a bundle containing all changesets, use
542 parameters. To create a bundle containing all changesets, use
528 -a/--all (or --base null).
543 -a/--all (or --base null).
529
544
530 You can change compression method with the -t/--type option.
545 You can change compression method with the -t/--type option.
531 The available compression methods are: none, bzip2, and
546 The available compression methods are: none, bzip2, and
532 gzip (by default, bundles are compressed using bzip2).
547 gzip (by default, bundles are compressed using bzip2).
533
548
534 The bundle file can then be transferred using conventional means
549 The bundle file can then be transferred using conventional means
535 and applied to another repository with the unbundle or pull
550 and applied to another repository with the unbundle or pull
536 command. This is useful when direct push and pull are not
551 command. This is useful when direct push and pull are not
537 available or when exporting an entire repository is undesirable.
552 available or when exporting an entire repository is undesirable.
538
553
539 Applying bundles preserves all changeset contents including
554 Applying bundles preserves all changeset contents including
540 permissions, copy/rename information, and revision history.
555 permissions, copy/rename information, and revision history.
556
557 Returns 0 on success, 1 if no changes found.
541 """
558 """
542 revs = opts.get('rev') or None
559 revs = opts.get('rev') or None
543 if revs:
560 if revs:
544 revs = [repo.lookup(rev) for rev in revs]
561 revs = [repo.lookup(rev) for rev in revs]
545 if opts.get('all'):
562 if opts.get('all'):
546 base = ['null']
563 base = ['null']
547 else:
564 else:
548 base = opts.get('base')
565 base = opts.get('base')
549 if base:
566 if base:
550 if dest:
567 if dest:
551 raise util.Abort(_("--base is incompatible with specifying "
568 raise util.Abort(_("--base is incompatible with specifying "
552 "a destination"))
569 "a destination"))
553 base = [repo.lookup(rev) for rev in base]
570 base = [repo.lookup(rev) for rev in base]
554 # create the right base
571 # create the right base
555 # XXX: nodesbetween / changegroup* should be "fixed" instead
572 # XXX: nodesbetween / changegroup* should be "fixed" instead
556 o = []
573 o = []
557 has = set((nullid,))
574 has = set((nullid,))
558 for n in base:
575 for n in base:
559 has.update(repo.changelog.reachable(n))
576 has.update(repo.changelog.reachable(n))
560 if revs:
577 if revs:
561 visit = list(revs)
578 visit = list(revs)
562 has.difference_update(revs)
579 has.difference_update(revs)
563 else:
580 else:
564 visit = repo.changelog.heads()
581 visit = repo.changelog.heads()
565 seen = {}
582 seen = {}
566 while visit:
583 while visit:
567 n = visit.pop(0)
584 n = visit.pop(0)
568 parents = [p for p in repo.changelog.parents(n) if p not in has]
585 parents = [p for p in repo.changelog.parents(n) if p not in has]
569 if len(parents) == 0:
586 if len(parents) == 0:
570 if n not in has:
587 if n not in has:
571 o.append(n)
588 o.append(n)
572 else:
589 else:
573 for p in parents:
590 for p in parents:
574 if p not in seen:
591 if p not in seen:
575 seen[p] = 1
592 seen[p] = 1
576 visit.append(p)
593 visit.append(p)
577 else:
594 else:
578 dest = ui.expandpath(dest or 'default-push', dest or 'default')
595 dest = ui.expandpath(dest or 'default-push', dest or 'default')
579 dest, branches = hg.parseurl(dest, opts.get('branch'))
596 dest, branches = hg.parseurl(dest, opts.get('branch'))
580 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
597 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
581 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
598 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
582 o = repo.findoutgoing(other, force=opts.get('force'))
599 o = repo.findoutgoing(other, force=opts.get('force'))
583
600
584 if not o:
601 if not o:
585 ui.status(_("no changes found\n"))
602 ui.status(_("no changes found\n"))
586 return
603 return 1
587
604
588 if revs:
605 if revs:
589 cg = repo.changegroupsubset(o, revs, 'bundle')
606 cg = repo.changegroupsubset(o, revs, 'bundle')
590 else:
607 else:
591 cg = repo.changegroup(o, 'bundle')
608 cg = repo.changegroup(o, 'bundle')
592
609
593 bundletype = opts.get('type', 'bzip2').lower()
610 bundletype = opts.get('type', 'bzip2').lower()
594 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
611 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
595 bundletype = btypes.get(bundletype)
612 bundletype = btypes.get(bundletype)
596 if bundletype not in changegroup.bundletypes:
613 if bundletype not in changegroup.bundletypes:
597 raise util.Abort(_('unknown bundle type specified with --type'))
614 raise util.Abort(_('unknown bundle type specified with --type'))
598
615
599 changegroup.writebundle(cg, fname, bundletype)
616 changegroup.writebundle(cg, fname, bundletype)
600
617
601 def cat(ui, repo, file1, *pats, **opts):
618 def cat(ui, repo, file1, *pats, **opts):
602 """output the current or given revision of files
619 """output the current or given revision of files
603
620
604 Print the specified files as they were at the given revision. If
621 Print the specified files as they were at the given revision. If
605 no revision is given, the parent of the working directory is used,
622 no revision is given, the parent of the working directory is used,
606 or tip if no revision is checked out.
623 or tip if no revision is checked out.
607
624
608 Output may be to a file, in which case the name of the file is
625 Output may be to a file, in which case the name of the file is
609 given using a format string. The formatting rules are the same as
626 given using a format string. The formatting rules are the same as
610 for the export command, with the following additions:
627 for the export command, with the following additions:
611
628
612 :``%s``: basename of file being printed
629 :``%s``: basename of file being printed
613 :``%d``: dirname of file being printed, or '.' if in repository root
630 :``%d``: dirname of file being printed, or '.' if in repository root
614 :``%p``: root-relative path name of file being printed
631 :``%p``: root-relative path name of file being printed
632
633 Returns 0 on success.
615 """
634 """
616 ctx = repo[opts.get('rev')]
635 ctx = repo[opts.get('rev')]
617 err = 1
636 err = 1
618 m = cmdutil.match(repo, (file1,) + pats, opts)
637 m = cmdutil.match(repo, (file1,) + pats, opts)
619 for abs in ctx.walk(m):
638 for abs in ctx.walk(m):
620 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
639 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
621 data = ctx[abs].data()
640 data = ctx[abs].data()
622 if opts.get('decode'):
641 if opts.get('decode'):
623 data = repo.wwritedata(abs, data)
642 data = repo.wwritedata(abs, data)
624 fp.write(data)
643 fp.write(data)
625 err = 0
644 err = 0
626 return err
645 return err
627
646
628 def clone(ui, source, dest=None, **opts):
647 def clone(ui, source, dest=None, **opts):
629 """make a copy of an existing repository
648 """make a copy of an existing repository
630
649
631 Create a copy of an existing repository in a new directory.
650 Create a copy of an existing repository in a new directory.
632
651
633 If no destination directory name is specified, it defaults to the
652 If no destination directory name is specified, it defaults to the
634 basename of the source.
653 basename of the source.
635
654
636 The location of the source is added to the new repository's
655 The location of the source is added to the new repository's
637 .hg/hgrc file, as the default to be used for future pulls.
656 .hg/hgrc file, as the default to be used for future pulls.
638
657
639 See :hg:`help urls` for valid source format details.
658 See :hg:`help urls` for valid source format details.
640
659
641 It is possible to specify an ``ssh://`` URL as the destination, but no
660 It is possible to specify an ``ssh://`` URL as the destination, but no
642 .hg/hgrc and working directory will be created on the remote side.
661 .hg/hgrc and working directory will be created on the remote side.
643 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
662 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
644
663
645 A set of changesets (tags, or branch names) to pull may be specified
664 A set of changesets (tags, or branch names) to pull may be specified
646 by listing each changeset (tag, or branch name) with -r/--rev.
665 by listing each changeset (tag, or branch name) with -r/--rev.
647 If -r/--rev is used, the cloned repository will contain only a subset
666 If -r/--rev is used, the cloned repository will contain only a subset
648 of the changesets of the source repository. Only the set of changesets
667 of the changesets of the source repository. Only the set of changesets
649 defined by all -r/--rev options (including all their ancestors)
668 defined by all -r/--rev options (including all their ancestors)
650 will be pulled into the destination repository.
669 will be pulled into the destination repository.
651 No subsequent changesets (including subsequent tags) will be present
670 No subsequent changesets (including subsequent tags) will be present
652 in the destination.
671 in the destination.
653
672
654 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
673 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
655 local source repositories.
674 local source repositories.
656
675
657 For efficiency, hardlinks are used for cloning whenever the source
676 For efficiency, hardlinks are used for cloning whenever the source
658 and destination are on the same filesystem (note this applies only
677 and destination are on the same filesystem (note this applies only
659 to the repository data, not to the working directory). Some
678 to the repository data, not to the working directory). Some
660 filesystems, such as AFS, implement hardlinking incorrectly, but
679 filesystems, such as AFS, implement hardlinking incorrectly, but
661 do not report errors. In these cases, use the --pull option to
680 do not report errors. In these cases, use the --pull option to
662 avoid hardlinking.
681 avoid hardlinking.
663
682
664 In some cases, you can clone repositories and the working directory
683 In some cases, you can clone repositories and the working directory
665 using full hardlinks with ::
684 using full hardlinks with ::
666
685
667 $ cp -al REPO REPOCLONE
686 $ cp -al REPO REPOCLONE
668
687
669 This is the fastest way to clone, but it is not always safe. The
688 This is the fastest way to clone, but it is not always safe. The
670 operation is not atomic (making sure REPO is not modified during
689 operation is not atomic (making sure REPO is not modified during
671 the operation is up to you) and you have to make sure your editor
690 the operation is up to you) and you have to make sure your editor
672 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
691 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
673 this is not compatible with certain extensions that place their
692 this is not compatible with certain extensions that place their
674 metadata under the .hg directory, such as mq.
693 metadata under the .hg directory, such as mq.
675
694
676 Mercurial will update the working directory to the first applicable
695 Mercurial will update the working directory to the first applicable
677 revision from this list:
696 revision from this list:
678
697
679 a) null if -U or the source repository has no changesets
698 a) null if -U or the source repository has no changesets
680 b) if -u . and the source repository is local, the first parent of
699 b) if -u . and the source repository is local, the first parent of
681 the source repository's working directory
700 the source repository's working directory
682 c) the changeset specified with -u (if a branch name, this means the
701 c) the changeset specified with -u (if a branch name, this means the
683 latest head of that branch)
702 latest head of that branch)
684 d) the changeset specified with -r
703 d) the changeset specified with -r
685 e) the tipmost head specified with -b
704 e) the tipmost head specified with -b
686 f) the tipmost head specified with the url#branch source syntax
705 f) the tipmost head specified with the url#branch source syntax
687 g) the tipmost head of the default branch
706 g) the tipmost head of the default branch
688 h) tip
707 h) tip
708
709 Returns 0 on success.
689 """
710 """
690 if opts.get('noupdate') and opts.get('updaterev'):
711 if opts.get('noupdate') and opts.get('updaterev'):
691 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
712 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
692
713
693 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
714 r = hg.clone(cmdutil.remoteui(ui, opts), source, dest,
694 pull=opts.get('pull'),
715 pull=opts.get('pull'),
695 stream=opts.get('uncompressed'),
716 stream=opts.get('uncompressed'),
696 rev=opts.get('rev'),
717 rev=opts.get('rev'),
697 update=opts.get('updaterev') or not opts.get('noupdate'),
718 update=opts.get('updaterev') or not opts.get('noupdate'),
698 branch=opts.get('branch'))
719 branch=opts.get('branch'))
699
720
721 return r is None
722
700 def commit(ui, repo, *pats, **opts):
723 def commit(ui, repo, *pats, **opts):
701 """commit the specified files or all outstanding changes
724 """commit the specified files or all outstanding changes
702
725
703 Commit changes to the given files into the repository. Unlike a
726 Commit changes to the given files into the repository. Unlike a
704 centralized RCS, this operation is a local operation. See hg push
727 centralized RCS, this operation is a local operation. See hg push
705 for a way to actively distribute your changes.
728 for a way to actively distribute your changes.
706
729
707 If a list of files is omitted, all changes reported by :hg:`status`
730 If a list of files is omitted, all changes reported by :hg:`status`
708 will be committed.
731 will be committed.
709
732
710 If you are committing the result of a merge, do not provide any
733 If you are committing the result of a merge, do not provide any
711 filenames or -I/-X filters.
734 filenames or -I/-X filters.
712
735
713 If no commit message is specified, the configured editor is
736 If no commit message is specified, the configured editor is
714 started to prompt you for a message.
737 started to prompt you for a message.
715
738
716 See :hg:`help dates` for a list of formats valid for -d/--date.
739 See :hg:`help dates` for a list of formats valid for -d/--date.
740
741 Returns 0 on success, 1 if nothing changed.
717 """
742 """
718 extra = {}
743 extra = {}
719 if opts.get('close_branch'):
744 if opts.get('close_branch'):
720 if repo['.'].node() not in repo.branchheads():
745 if repo['.'].node() not in repo.branchheads():
721 # The topo heads set is included in the branch heads set of the
746 # The topo heads set is included in the branch heads set of the
722 # current branch, so it's sufficient to test branchheads
747 # current branch, so it's sufficient to test branchheads
723 raise util.Abort(_('can only close branch heads'))
748 raise util.Abort(_('can only close branch heads'))
724 extra['close'] = 1
749 extra['close'] = 1
725 e = cmdutil.commiteditor
750 e = cmdutil.commiteditor
726 if opts.get('force_editor'):
751 if opts.get('force_editor'):
727 e = cmdutil.commitforceeditor
752 e = cmdutil.commitforceeditor
728
753
729 def commitfunc(ui, repo, message, match, opts):
754 def commitfunc(ui, repo, message, match, opts):
730 return repo.commit(message, opts.get('user'), opts.get('date'), match,
755 return repo.commit(message, opts.get('user'), opts.get('date'), match,
731 editor=e, extra=extra)
756 editor=e, extra=extra)
732
757
733 branch = repo[None].branch()
758 branch = repo[None].branch()
734 bheads = repo.branchheads(branch)
759 bheads = repo.branchheads(branch)
735
760
736 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
761 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
737 if not node:
762 if not node:
738 ui.status(_("nothing changed\n"))
763 ui.status(_("nothing changed\n"))
739 return
764 return 1
740
765
741 ctx = repo[node]
766 ctx = repo[node]
742 parents = ctx.parents()
767 parents = ctx.parents()
743
768
744 if bheads and [x for x in parents if x.node() not in bheads]:
769 if bheads and [x for x in parents if x.node() not in bheads]:
745 ui.status(_('created new head\n'))
770 ui.status(_('created new head\n'))
746
771
747 if not opts.get('close_branch'):
772 if not opts.get('close_branch'):
748 for r in parents:
773 for r in parents:
749 if r.extra().get('close'):
774 if r.extra().get('close'):
750 ui.status(_('reopening closed branch head %d\n') % r)
775 ui.status(_('reopening closed branch head %d\n') % r)
751
776
752 if ui.debugflag:
777 if ui.debugflag:
753 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
778 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
754 elif ui.verbose:
779 elif ui.verbose:
755 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
780 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
756
781
757 def copy(ui, repo, *pats, **opts):
782 def copy(ui, repo, *pats, **opts):
758 """mark files as copied for the next commit
783 """mark files as copied for the next commit
759
784
760 Mark dest as having copies of source files. If dest is a
785 Mark dest as having copies of source files. If dest is a
761 directory, copies are put in that directory. If dest is a file,
786 directory, copies are put in that directory. If dest is a file,
762 the source must be a single file.
787 the source must be a single file.
763
788
764 By default, this command copies the contents of files as they
789 By default, this command copies the contents of files as they
765 exist in the working directory. If invoked with -A/--after, the
790 exist in the working directory. If invoked with -A/--after, the
766 operation is recorded, but no copying is performed.
791 operation is recorded, but no copying is performed.
767
792
768 This command takes effect with the next commit. To undo a copy
793 This command takes effect with the next commit. To undo a copy
769 before that, see hg revert.
794 before that, see hg revert.
795
796 Returns 0 on success, 1 if errors are encountered.
770 """
797 """
771 wlock = repo.wlock(False)
798 wlock = repo.wlock(False)
772 try:
799 try:
773 return cmdutil.copy(ui, repo, pats, opts)
800 return cmdutil.copy(ui, repo, pats, opts)
774 finally:
801 finally:
775 wlock.release()
802 wlock.release()
776
803
777 def debugancestor(ui, repo, *args):
804 def debugancestor(ui, repo, *args):
778 """find the ancestor revision of two revisions in a given index"""
805 """find the ancestor revision of two revisions in a given index"""
779 if len(args) == 3:
806 if len(args) == 3:
780 index, rev1, rev2 = args
807 index, rev1, rev2 = args
781 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
808 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
782 lookup = r.lookup
809 lookup = r.lookup
783 elif len(args) == 2:
810 elif len(args) == 2:
784 if not repo:
811 if not repo:
785 raise util.Abort(_("There is no Mercurial repository here "
812 raise util.Abort(_("There is no Mercurial repository here "
786 "(.hg not found)"))
813 "(.hg not found)"))
787 rev1, rev2 = args
814 rev1, rev2 = args
788 r = repo.changelog
815 r = repo.changelog
789 lookup = repo.lookup
816 lookup = repo.lookup
790 else:
817 else:
791 raise util.Abort(_('either two or three arguments required'))
818 raise util.Abort(_('either two or three arguments required'))
792 a = r.ancestor(lookup(rev1), lookup(rev2))
819 a = r.ancestor(lookup(rev1), lookup(rev2))
793 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
820 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
794
821
795 def debugcommands(ui, cmd='', *args):
822 def debugcommands(ui, cmd='', *args):
823 """list all available commands and options"""
796 for cmd, vals in sorted(table.iteritems()):
824 for cmd, vals in sorted(table.iteritems()):
797 cmd = cmd.split('|')[0].strip('^')
825 cmd = cmd.split('|')[0].strip('^')
798 opts = ', '.join([i[1] for i in vals[1]])
826 opts = ', '.join([i[1] for i in vals[1]])
799 ui.write('%s: %s\n' % (cmd, opts))
827 ui.write('%s: %s\n' % (cmd, opts))
800
828
801 def debugcomplete(ui, cmd='', **opts):
829 def debugcomplete(ui, cmd='', **opts):
802 """returns the completion list associated with the given command"""
830 """returns the completion list associated with the given command"""
803
831
804 if opts.get('options'):
832 if opts.get('options'):
805 options = []
833 options = []
806 otables = [globalopts]
834 otables = [globalopts]
807 if cmd:
835 if cmd:
808 aliases, entry = cmdutil.findcmd(cmd, table, False)
836 aliases, entry = cmdutil.findcmd(cmd, table, False)
809 otables.append(entry[1])
837 otables.append(entry[1])
810 for t in otables:
838 for t in otables:
811 for o in t:
839 for o in t:
812 if "(DEPRECATED)" in o[3]:
840 if "(DEPRECATED)" in o[3]:
813 continue
841 continue
814 if o[0]:
842 if o[0]:
815 options.append('-%s' % o[0])
843 options.append('-%s' % o[0])
816 options.append('--%s' % o[1])
844 options.append('--%s' % o[1])
817 ui.write("%s\n" % "\n".join(options))
845 ui.write("%s\n" % "\n".join(options))
818 return
846 return
819
847
820 cmdlist = cmdutil.findpossible(cmd, table)
848 cmdlist = cmdutil.findpossible(cmd, table)
821 if ui.verbose:
849 if ui.verbose:
822 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
850 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
823 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
851 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
824
852
825 def debugfsinfo(ui, path = "."):
853 def debugfsinfo(ui, path = "."):
854 """show information detected about current filesystem"""
826 open('.debugfsinfo', 'w').write('')
855 open('.debugfsinfo', 'w').write('')
827 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
856 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
828 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
857 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
829 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
858 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
830 and 'yes' or 'no'))
859 and 'yes' or 'no'))
831 os.unlink('.debugfsinfo')
860 os.unlink('.debugfsinfo')
832
861
833 def debugrebuildstate(ui, repo, rev="tip"):
862 def debugrebuildstate(ui, repo, rev="tip"):
834 """rebuild the dirstate as it would look like for the given revision"""
863 """rebuild the dirstate as it would look like for the given revision"""
835 ctx = repo[rev]
864 ctx = repo[rev]
836 wlock = repo.wlock()
865 wlock = repo.wlock()
837 try:
866 try:
838 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
867 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
839 finally:
868 finally:
840 wlock.release()
869 wlock.release()
841
870
842 def debugcheckstate(ui, repo):
871 def debugcheckstate(ui, repo):
843 """validate the correctness of the current dirstate"""
872 """validate the correctness of the current dirstate"""
844 parent1, parent2 = repo.dirstate.parents()
873 parent1, parent2 = repo.dirstate.parents()
845 m1 = repo[parent1].manifest()
874 m1 = repo[parent1].manifest()
846 m2 = repo[parent2].manifest()
875 m2 = repo[parent2].manifest()
847 errors = 0
876 errors = 0
848 for f in repo.dirstate:
877 for f in repo.dirstate:
849 state = repo.dirstate[f]
878 state = repo.dirstate[f]
850 if state in "nr" and f not in m1:
879 if state in "nr" and f not in m1:
851 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
880 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
852 errors += 1
881 errors += 1
853 if state in "a" and f in m1:
882 if state in "a" and f in m1:
854 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
883 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
855 errors += 1
884 errors += 1
856 if state in "m" and f not in m1 and f not in m2:
885 if state in "m" and f not in m1 and f not in m2:
857 ui.warn(_("%s in state %s, but not in either manifest\n") %
886 ui.warn(_("%s in state %s, but not in either manifest\n") %
858 (f, state))
887 (f, state))
859 errors += 1
888 errors += 1
860 for f in m1:
889 for f in m1:
861 state = repo.dirstate[f]
890 state = repo.dirstate[f]
862 if state not in "nrm":
891 if state not in "nrm":
863 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
892 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
864 errors += 1
893 errors += 1
865 if errors:
894 if errors:
866 error = _(".hg/dirstate inconsistent with current parent's manifest")
895 error = _(".hg/dirstate inconsistent with current parent's manifest")
867 raise util.Abort(error)
896 raise util.Abort(error)
868
897
869 def showconfig(ui, repo, *values, **opts):
898 def showconfig(ui, repo, *values, **opts):
870 """show combined config settings from all hgrc files
899 """show combined config settings from all hgrc files
871
900
872 With no arguments, print names and values of all config items.
901 With no arguments, print names and values of all config items.
873
902
874 With one argument of the form section.name, print just the value
903 With one argument of the form section.name, print just the value
875 of that config item.
904 of that config item.
876
905
877 With multiple arguments, print names and values of all config
906 With multiple arguments, print names and values of all config
878 items with matching section names.
907 items with matching section names.
879
908
880 With --debug, the source (filename and line number) is printed
909 With --debug, the source (filename and line number) is printed
881 for each config item.
910 for each config item.
911
912 Returns 0 on success.
882 """
913 """
883
914
884 for f in util.rcpath():
915 for f in util.rcpath():
885 ui.debug(_('read config from: %s\n') % f)
916 ui.debug(_('read config from: %s\n') % f)
886 untrusted = bool(opts.get('untrusted'))
917 untrusted = bool(opts.get('untrusted'))
887 if values:
918 if values:
888 if len([v for v in values if '.' in v]) > 1:
919 if len([v for v in values if '.' in v]) > 1:
889 raise util.Abort(_('only one config item permitted'))
920 raise util.Abort(_('only one config item permitted'))
890 for section, name, value in ui.walkconfig(untrusted=untrusted):
921 for section, name, value in ui.walkconfig(untrusted=untrusted):
891 sectname = section + '.' + name
922 sectname = section + '.' + name
892 if values:
923 if values:
893 for v in values:
924 for v in values:
894 if v == section:
925 if v == section:
895 ui.debug('%s: ' %
926 ui.debug('%s: ' %
896 ui.configsource(section, name, untrusted))
927 ui.configsource(section, name, untrusted))
897 ui.write('%s=%s\n' % (sectname, value))
928 ui.write('%s=%s\n' % (sectname, value))
898 elif v == sectname:
929 elif v == sectname:
899 ui.debug('%s: ' %
930 ui.debug('%s: ' %
900 ui.configsource(section, name, untrusted))
931 ui.configsource(section, name, untrusted))
901 ui.write(value, '\n')
932 ui.write(value, '\n')
902 else:
933 else:
903 ui.debug('%s: ' %
934 ui.debug('%s: ' %
904 ui.configsource(section, name, untrusted))
935 ui.configsource(section, name, untrusted))
905 ui.write('%s=%s\n' % (sectname, value))
936 ui.write('%s=%s\n' % (sectname, value))
906
937
907 def debugsetparents(ui, repo, rev1, rev2=None):
938 def debugsetparents(ui, repo, rev1, rev2=None):
908 """manually set the parents of the current working directory
939 """manually set the parents of the current working directory
909
940
910 This is useful for writing repository conversion tools, but should
941 This is useful for writing repository conversion tools, but should
911 be used with care.
942 be used with care.
943
944 Returns 0 on success.
912 """
945 """
913
946
914 if not rev2:
947 if not rev2:
915 rev2 = hex(nullid)
948 rev2 = hex(nullid)
916
949
917 wlock = repo.wlock()
950 wlock = repo.wlock()
918 try:
951 try:
919 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
952 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
920 finally:
953 finally:
921 wlock.release()
954 wlock.release()
922
955
923 def debugstate(ui, repo, nodates=None):
956 def debugstate(ui, repo, nodates=None):
924 """show the contents of the current dirstate"""
957 """show the contents of the current dirstate"""
925 timestr = ""
958 timestr = ""
926 showdate = not nodates
959 showdate = not nodates
927 for file_, ent in sorted(repo.dirstate._map.iteritems()):
960 for file_, ent in sorted(repo.dirstate._map.iteritems()):
928 if showdate:
961 if showdate:
929 if ent[3] == -1:
962 if ent[3] == -1:
930 # Pad or slice to locale representation
963 # Pad or slice to locale representation
931 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
964 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
932 time.localtime(0)))
965 time.localtime(0)))
933 timestr = 'unset'
966 timestr = 'unset'
934 timestr = (timestr[:locale_len] +
967 timestr = (timestr[:locale_len] +
935 ' ' * (locale_len - len(timestr)))
968 ' ' * (locale_len - len(timestr)))
936 else:
969 else:
937 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
970 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
938 time.localtime(ent[3]))
971 time.localtime(ent[3]))
939 if ent[1] & 020000:
972 if ent[1] & 020000:
940 mode = 'lnk'
973 mode = 'lnk'
941 else:
974 else:
942 mode = '%3o' % (ent[1] & 0777)
975 mode = '%3o' % (ent[1] & 0777)
943 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
976 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
944 for f in repo.dirstate.copies():
977 for f in repo.dirstate.copies():
945 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
978 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
946
979
947 def debugsub(ui, repo, rev=None):
980 def debugsub(ui, repo, rev=None):
948 if rev == '':
981 if rev == '':
949 rev = None
982 rev = None
950 for k, v in sorted(repo[rev].substate.items()):
983 for k, v in sorted(repo[rev].substate.items()):
951 ui.write('path %s\n' % k)
984 ui.write('path %s\n' % k)
952 ui.write(' source %s\n' % v[0])
985 ui.write(' source %s\n' % v[0])
953 ui.write(' revision %s\n' % v[1])
986 ui.write(' revision %s\n' % v[1])
954
987
955 def debugdata(ui, file_, rev):
988 def debugdata(ui, file_, rev):
956 """dump the contents of a data file revision"""
989 """dump the contents of a data file revision"""
957 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
990 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
958 try:
991 try:
959 ui.write(r.revision(r.lookup(rev)))
992 ui.write(r.revision(r.lookup(rev)))
960 except KeyError:
993 except KeyError:
961 raise util.Abort(_('invalid revision identifier %s') % rev)
994 raise util.Abort(_('invalid revision identifier %s') % rev)
962
995
963 def debugdate(ui, date, range=None, **opts):
996 def debugdate(ui, date, range=None, **opts):
964 """parse and display a date"""
997 """parse and display a date"""
965 if opts["extended"]:
998 if opts["extended"]:
966 d = util.parsedate(date, util.extendeddateformats)
999 d = util.parsedate(date, util.extendeddateformats)
967 else:
1000 else:
968 d = util.parsedate(date)
1001 d = util.parsedate(date)
969 ui.write("internal: %s %s\n" % d)
1002 ui.write("internal: %s %s\n" % d)
970 ui.write("standard: %s\n" % util.datestr(d))
1003 ui.write("standard: %s\n" % util.datestr(d))
971 if range:
1004 if range:
972 m = util.matchdate(range)
1005 m = util.matchdate(range)
973 ui.write("match: %s\n" % m(d[0]))
1006 ui.write("match: %s\n" % m(d[0]))
974
1007
975 def debugindex(ui, file_):
1008 def debugindex(ui, file_):
976 """dump the contents of an index file"""
1009 """dump the contents of an index file"""
977 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1010 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
978 ui.write(" rev offset length base linkrev"
1011 ui.write(" rev offset length base linkrev"
979 " nodeid p1 p2\n")
1012 " nodeid p1 p2\n")
980 for i in r:
1013 for i in r:
981 node = r.node(i)
1014 node = r.node(i)
982 try:
1015 try:
983 pp = r.parents(node)
1016 pp = r.parents(node)
984 except:
1017 except:
985 pp = [nullid, nullid]
1018 pp = [nullid, nullid]
986 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1019 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
987 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1020 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
988 short(node), short(pp[0]), short(pp[1])))
1021 short(node), short(pp[0]), short(pp[1])))
989
1022
990 def debugindexdot(ui, file_):
1023 def debugindexdot(ui, file_):
991 """dump an index DAG as a graphviz dot file"""
1024 """dump an index DAG as a graphviz dot file"""
992 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1025 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
993 ui.write("digraph G {\n")
1026 ui.write("digraph G {\n")
994 for i in r:
1027 for i in r:
995 node = r.node(i)
1028 node = r.node(i)
996 pp = r.parents(node)
1029 pp = r.parents(node)
997 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1030 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
998 if pp[1] != nullid:
1031 if pp[1] != nullid:
999 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1032 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1000 ui.write("}\n")
1033 ui.write("}\n")
1001
1034
1002 def debuginstall(ui):
1035 def debuginstall(ui):
1003 '''test Mercurial installation'''
1036 '''test Mercurial installation
1037
1038 Returns 0 on success.
1039 '''
1004
1040
1005 def writetemp(contents):
1041 def writetemp(contents):
1006 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1042 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1007 f = os.fdopen(fd, "wb")
1043 f = os.fdopen(fd, "wb")
1008 f.write(contents)
1044 f.write(contents)
1009 f.close()
1045 f.close()
1010 return name
1046 return name
1011
1047
1012 problems = 0
1048 problems = 0
1013
1049
1014 # encoding
1050 # encoding
1015 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1051 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1016 try:
1052 try:
1017 encoding.fromlocal("test")
1053 encoding.fromlocal("test")
1018 except util.Abort, inst:
1054 except util.Abort, inst:
1019 ui.write(" %s\n" % inst)
1055 ui.write(" %s\n" % inst)
1020 ui.write(_(" (check that your locale is properly set)\n"))
1056 ui.write(_(" (check that your locale is properly set)\n"))
1021 problems += 1
1057 problems += 1
1022
1058
1023 # compiled modules
1059 # compiled modules
1024 ui.status(_("Checking extensions...\n"))
1060 ui.status(_("Checking extensions...\n"))
1025 try:
1061 try:
1026 import bdiff, mpatch, base85
1062 import bdiff, mpatch, base85
1027 except Exception, inst:
1063 except Exception, inst:
1028 ui.write(" %s\n" % inst)
1064 ui.write(" %s\n" % inst)
1029 ui.write(_(" One or more extensions could not be found"))
1065 ui.write(_(" One or more extensions could not be found"))
1030 ui.write(_(" (check that you compiled the extensions)\n"))
1066 ui.write(_(" (check that you compiled the extensions)\n"))
1031 problems += 1
1067 problems += 1
1032
1068
1033 # templates
1069 # templates
1034 ui.status(_("Checking templates...\n"))
1070 ui.status(_("Checking templates...\n"))
1035 try:
1071 try:
1036 import templater
1072 import templater
1037 templater.templater(templater.templatepath("map-cmdline.default"))
1073 templater.templater(templater.templatepath("map-cmdline.default"))
1038 except Exception, inst:
1074 except Exception, inst:
1039 ui.write(" %s\n" % inst)
1075 ui.write(" %s\n" % inst)
1040 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1076 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1041 problems += 1
1077 problems += 1
1042
1078
1043 # patch
1079 # patch
1044 ui.status(_("Checking patch...\n"))
1080 ui.status(_("Checking patch...\n"))
1045 patchproblems = 0
1081 patchproblems = 0
1046 a = "1\n2\n3\n4\n"
1082 a = "1\n2\n3\n4\n"
1047 b = "1\n2\n3\ninsert\n4\n"
1083 b = "1\n2\n3\ninsert\n4\n"
1048 fa = writetemp(a)
1084 fa = writetemp(a)
1049 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1085 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1050 os.path.basename(fa))
1086 os.path.basename(fa))
1051 fd = writetemp(d)
1087 fd = writetemp(d)
1052
1088
1053 files = {}
1089 files = {}
1054 try:
1090 try:
1055 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1091 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1056 except util.Abort, e:
1092 except util.Abort, e:
1057 ui.write(_(" patch call failed:\n"))
1093 ui.write(_(" patch call failed:\n"))
1058 ui.write(" " + str(e) + "\n")
1094 ui.write(" " + str(e) + "\n")
1059 patchproblems += 1
1095 patchproblems += 1
1060 else:
1096 else:
1061 if list(files) != [os.path.basename(fa)]:
1097 if list(files) != [os.path.basename(fa)]:
1062 ui.write(_(" unexpected patch output!\n"))
1098 ui.write(_(" unexpected patch output!\n"))
1063 patchproblems += 1
1099 patchproblems += 1
1064 a = open(fa).read()
1100 a = open(fa).read()
1065 if a != b:
1101 if a != b:
1066 ui.write(_(" patch test failed!\n"))
1102 ui.write(_(" patch test failed!\n"))
1067 patchproblems += 1
1103 patchproblems += 1
1068
1104
1069 if patchproblems:
1105 if patchproblems:
1070 if ui.config('ui', 'patch'):
1106 if ui.config('ui', 'patch'):
1071 ui.write(_(" (Current patch tool may be incompatible with patch,"
1107 ui.write(_(" (Current patch tool may be incompatible with patch,"
1072 " or misconfigured. Please check your .hgrc file)\n"))
1108 " or misconfigured. Please check your .hgrc file)\n"))
1073 else:
1109 else:
1074 ui.write(_(" Internal patcher failure, please report this error"
1110 ui.write(_(" Internal patcher failure, please report this error"
1075 " to http://mercurial.selenic.com/bts/\n"))
1111 " to http://mercurial.selenic.com/bts/\n"))
1076 problems += patchproblems
1112 problems += patchproblems
1077
1113
1078 os.unlink(fa)
1114 os.unlink(fa)
1079 os.unlink(fd)
1115 os.unlink(fd)
1080
1116
1081 # editor
1117 # editor
1082 ui.status(_("Checking commit editor...\n"))
1118 ui.status(_("Checking commit editor...\n"))
1083 editor = ui.geteditor()
1119 editor = ui.geteditor()
1084 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1120 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1085 if not cmdpath:
1121 if not cmdpath:
1086 if editor == 'vi':
1122 if editor == 'vi':
1087 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1123 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1088 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1124 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1089 else:
1125 else:
1090 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1126 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1091 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1127 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1092 problems += 1
1128 problems += 1
1093
1129
1094 # check username
1130 # check username
1095 ui.status(_("Checking username...\n"))
1131 ui.status(_("Checking username...\n"))
1096 try:
1132 try:
1097 user = ui.username()
1133 user = ui.username()
1098 except util.Abort, e:
1134 except util.Abort, e:
1099 ui.write(" %s\n" % e)
1135 ui.write(" %s\n" % e)
1100 ui.write(_(" (specify a username in your .hgrc file)\n"))
1136 ui.write(_(" (specify a username in your .hgrc file)\n"))
1101 problems += 1
1137 problems += 1
1102
1138
1103 if not problems:
1139 if not problems:
1104 ui.status(_("No problems detected\n"))
1140 ui.status(_("No problems detected\n"))
1105 else:
1141 else:
1106 ui.write(_("%s problems detected,"
1142 ui.write(_("%s problems detected,"
1107 " please check your install!\n") % problems)
1143 " please check your install!\n") % problems)
1108
1144
1109 return problems
1145 return problems
1110
1146
1111 def debugrename(ui, repo, file1, *pats, **opts):
1147 def debugrename(ui, repo, file1, *pats, **opts):
1112 """dump rename information"""
1148 """dump rename information"""
1113
1149
1114 ctx = repo[opts.get('rev')]
1150 ctx = repo[opts.get('rev')]
1115 m = cmdutil.match(repo, (file1,) + pats, opts)
1151 m = cmdutil.match(repo, (file1,) + pats, opts)
1116 for abs in ctx.walk(m):
1152 for abs in ctx.walk(m):
1117 fctx = ctx[abs]
1153 fctx = ctx[abs]
1118 o = fctx.filelog().renamed(fctx.filenode())
1154 o = fctx.filelog().renamed(fctx.filenode())
1119 rel = m.rel(abs)
1155 rel = m.rel(abs)
1120 if o:
1156 if o:
1121 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1157 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1122 else:
1158 else:
1123 ui.write(_("%s not renamed\n") % rel)
1159 ui.write(_("%s not renamed\n") % rel)
1124
1160
1125 def debugwalk(ui, repo, *pats, **opts):
1161 def debugwalk(ui, repo, *pats, **opts):
1126 """show how files match on given patterns"""
1162 """show how files match on given patterns"""
1127 m = cmdutil.match(repo, pats, opts)
1163 m = cmdutil.match(repo, pats, opts)
1128 items = list(repo.walk(m))
1164 items = list(repo.walk(m))
1129 if not items:
1165 if not items:
1130 return
1166 return
1131 fmt = 'f %%-%ds %%-%ds %%s' % (
1167 fmt = 'f %%-%ds %%-%ds %%s' % (
1132 max([len(abs) for abs in items]),
1168 max([len(abs) for abs in items]),
1133 max([len(m.rel(abs)) for abs in items]))
1169 max([len(m.rel(abs)) for abs in items]))
1134 for abs in items:
1170 for abs in items:
1135 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1171 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1136 ui.write("%s\n" % line.rstrip())
1172 ui.write("%s\n" % line.rstrip())
1137
1173
1138 def diff(ui, repo, *pats, **opts):
1174 def diff(ui, repo, *pats, **opts):
1139 """diff repository (or selected files)
1175 """diff repository (or selected files)
1140
1176
1141 Show differences between revisions for the specified files.
1177 Show differences between revisions for the specified files.
1142
1178
1143 Differences between files are shown using the unified diff format.
1179 Differences between files are shown using the unified diff format.
1144
1180
1145 NOTE: diff may generate unexpected results for merges, as it will
1181 NOTE: diff may generate unexpected results for merges, as it will
1146 default to comparing against the working directory's first parent
1182 default to comparing against the working directory's first parent
1147 changeset if no revisions are specified.
1183 changeset if no revisions are specified.
1148
1184
1149 When two revision arguments are given, then changes are shown
1185 When two revision arguments are given, then changes are shown
1150 between those revisions. If only one revision is specified then
1186 between those revisions. If only one revision is specified then
1151 that revision is compared to the working directory, and, when no
1187 that revision is compared to the working directory, and, when no
1152 revisions are specified, the working directory files are compared
1188 revisions are specified, the working directory files are compared
1153 to its parent.
1189 to its parent.
1154
1190
1155 Alternatively you can specify -c/--change with a revision to see
1191 Alternatively you can specify -c/--change with a revision to see
1156 the changes in that changeset relative to its first parent.
1192 the changes in that changeset relative to its first parent.
1157
1193
1158 Without the -a/--text option, diff will avoid generating diffs of
1194 Without the -a/--text option, diff will avoid generating diffs of
1159 files it detects as binary. With -a, diff will generate a diff
1195 files it detects as binary. With -a, diff will generate a diff
1160 anyway, probably with undesirable results.
1196 anyway, probably with undesirable results.
1161
1197
1162 Use the -g/--git option to generate diffs in the git extended diff
1198 Use the -g/--git option to generate diffs in the git extended diff
1163 format. For more information, read :hg:`help diffs`.
1199 format. For more information, read :hg:`help diffs`.
1200
1201 Returns 0 on success.
1164 """
1202 """
1165
1203
1166 revs = opts.get('rev')
1204 revs = opts.get('rev')
1167 change = opts.get('change')
1205 change = opts.get('change')
1168 stat = opts.get('stat')
1206 stat = opts.get('stat')
1169 reverse = opts.get('reverse')
1207 reverse = opts.get('reverse')
1170
1208
1171 if revs and change:
1209 if revs and change:
1172 msg = _('cannot specify --rev and --change at the same time')
1210 msg = _('cannot specify --rev and --change at the same time')
1173 raise util.Abort(msg)
1211 raise util.Abort(msg)
1174 elif change:
1212 elif change:
1175 node2 = repo.lookup(change)
1213 node2 = repo.lookup(change)
1176 node1 = repo[node2].parents()[0].node()
1214 node1 = repo[node2].parents()[0].node()
1177 else:
1215 else:
1178 node1, node2 = cmdutil.revpair(repo, revs)
1216 node1, node2 = cmdutil.revpair(repo, revs)
1179
1217
1180 if reverse:
1218 if reverse:
1181 node1, node2 = node2, node1
1219 node1, node2 = node2, node1
1182
1220
1183 diffopts = patch.diffopts(ui, opts)
1221 diffopts = patch.diffopts(ui, opts)
1184 m = cmdutil.match(repo, pats, opts)
1222 m = cmdutil.match(repo, pats, opts)
1185 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
1223 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
1186
1224
1187 def export(ui, repo, *changesets, **opts):
1225 def export(ui, repo, *changesets, **opts):
1188 """dump the header and diffs for one or more changesets
1226 """dump the header and diffs for one or more changesets
1189
1227
1190 Print the changeset header and diffs for one or more revisions.
1228 Print the changeset header and diffs for one or more revisions.
1191
1229
1192 The information shown in the changeset header is: author, date,
1230 The information shown in the changeset header is: author, date,
1193 branch name (if non-default), changeset hash, parent(s) and commit
1231 branch name (if non-default), changeset hash, parent(s) and commit
1194 comment.
1232 comment.
1195
1233
1196 NOTE: export may generate unexpected diff output for merge
1234 NOTE: export may generate unexpected diff output for merge
1197 changesets, as it will compare the merge changeset against its
1235 changesets, as it will compare the merge changeset against its
1198 first parent only.
1236 first parent only.
1199
1237
1200 Output may be to a file, in which case the name of the file is
1238 Output may be to a file, in which case the name of the file is
1201 given using a format string. The formatting rules are as follows:
1239 given using a format string. The formatting rules are as follows:
1202
1240
1203 :``%%``: literal "%" character
1241 :``%%``: literal "%" character
1204 :``%H``: changeset hash (40 bytes of hexadecimal)
1242 :``%H``: changeset hash (40 bytes of hexadecimal)
1205 :``%N``: number of patches being generated
1243 :``%N``: number of patches being generated
1206 :``%R``: changeset revision number
1244 :``%R``: changeset revision number
1207 :``%b``: basename of the exporting repository
1245 :``%b``: basename of the exporting repository
1208 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1246 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1209 :``%n``: zero-padded sequence number, starting at 1
1247 :``%n``: zero-padded sequence number, starting at 1
1210 :``%r``: zero-padded changeset revision number
1248 :``%r``: zero-padded changeset revision number
1211
1249
1212 Without the -a/--text option, export will avoid generating diffs
1250 Without the -a/--text option, export will avoid generating diffs
1213 of files it detects as binary. With -a, export will generate a
1251 of files it detects as binary. With -a, export will generate a
1214 diff anyway, probably with undesirable results.
1252 diff anyway, probably with undesirable results.
1215
1253
1216 Use the -g/--git option to generate diffs in the git extended diff
1254 Use the -g/--git option to generate diffs in the git extended diff
1217 format. See :hg:`help diffs` for more information.
1255 format. See :hg:`help diffs` for more information.
1218
1256
1219 With the --switch-parent option, the diff will be against the
1257 With the --switch-parent option, the diff will be against the
1220 second parent. It can be useful to review a merge.
1258 second parent. It can be useful to review a merge.
1259
1260 Returns 0 on success.
1221 """
1261 """
1222 changesets += tuple(opts.get('rev', []))
1262 changesets += tuple(opts.get('rev', []))
1223 if not changesets:
1263 if not changesets:
1224 raise util.Abort(_("export requires at least one changeset"))
1264 raise util.Abort(_("export requires at least one changeset"))
1225 revs = cmdutil.revrange(repo, changesets)
1265 revs = cmdutil.revrange(repo, changesets)
1226 if len(revs) > 1:
1266 if len(revs) > 1:
1227 ui.note(_('exporting patches:\n'))
1267 ui.note(_('exporting patches:\n'))
1228 else:
1268 else:
1229 ui.note(_('exporting patch:\n'))
1269 ui.note(_('exporting patch:\n'))
1230 cmdutil.export(repo, revs, template=opts.get('output'),
1270 cmdutil.export(repo, revs, template=opts.get('output'),
1231 switch_parent=opts.get('switch_parent'),
1271 switch_parent=opts.get('switch_parent'),
1232 opts=patch.diffopts(ui, opts))
1272 opts=patch.diffopts(ui, opts))
1233
1273
1234 def forget(ui, repo, *pats, **opts):
1274 def forget(ui, repo, *pats, **opts):
1235 """forget the specified files on the next commit
1275 """forget the specified files on the next commit
1236
1276
1237 Mark the specified files so they will no longer be tracked
1277 Mark the specified files so they will no longer be tracked
1238 after the next commit.
1278 after the next commit.
1239
1279
1240 This only removes files from the current branch, not from the
1280 This only removes files from the current branch, not from the
1241 entire project history, and it does not delete them from the
1281 entire project history, and it does not delete them from the
1242 working directory.
1282 working directory.
1243
1283
1244 To undo a forget before the next commit, see hg add.
1284 To undo a forget before the next commit, see hg add.
1285
1286 Returns 0 on success.
1245 """
1287 """
1246
1288
1247 if not pats:
1289 if not pats:
1248 raise util.Abort(_('no files specified'))
1290 raise util.Abort(_('no files specified'))
1249
1291
1250 m = cmdutil.match(repo, pats, opts)
1292 m = cmdutil.match(repo, pats, opts)
1251 s = repo.status(match=m, clean=True)
1293 s = repo.status(match=m, clean=True)
1252 forget = sorted(s[0] + s[1] + s[3] + s[6])
1294 forget = sorted(s[0] + s[1] + s[3] + s[6])
1295 errs = 0
1253
1296
1254 for f in m.files():
1297 for f in m.files():
1255 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1298 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1256 ui.warn(_('not removing %s: file is already untracked\n')
1299 ui.warn(_('not removing %s: file is already untracked\n')
1257 % m.rel(f))
1300 % m.rel(f))
1301 errs = 1
1258
1302
1259 for f in forget:
1303 for f in forget:
1260 if ui.verbose or not m.exact(f):
1304 if ui.verbose or not m.exact(f):
1261 ui.status(_('removing %s\n') % m.rel(f))
1305 ui.status(_('removing %s\n') % m.rel(f))
1262
1306
1263 repo.remove(forget, unlink=False)
1307 repo.remove(forget, unlink=False)
1308 return errs
1264
1309
1265 def grep(ui, repo, pattern, *pats, **opts):
1310 def grep(ui, repo, pattern, *pats, **opts):
1266 """search for a pattern in specified files and revisions
1311 """search for a pattern in specified files and revisions
1267
1312
1268 Search revisions of files for a regular expression.
1313 Search revisions of files for a regular expression.
1269
1314
1270 This command behaves differently than Unix grep. It only accepts
1315 This command behaves differently than Unix grep. It only accepts
1271 Python/Perl regexps. It searches repository history, not the
1316 Python/Perl regexps. It searches repository history, not the
1272 working directory. It always prints the revision number in which a
1317 working directory. It always prints the revision number in which a
1273 match appears.
1318 match appears.
1274
1319
1275 By default, grep only prints output for the first revision of a
1320 By default, grep only prints output for the first revision of a
1276 file in which it finds a match. To get it to print every revision
1321 file in which it finds a match. To get it to print every revision
1277 that contains a change in match status ("-" for a match that
1322 that contains a change in match status ("-" for a match that
1278 becomes a non-match, or "+" for a non-match that becomes a match),
1323 becomes a non-match, or "+" for a non-match that becomes a match),
1279 use the --all flag.
1324 use the --all flag.
1325
1326 Returns 0 if a match is found, 1 otherwise.
1280 """
1327 """
1281 reflags = 0
1328 reflags = 0
1282 if opts.get('ignore_case'):
1329 if opts.get('ignore_case'):
1283 reflags |= re.I
1330 reflags |= re.I
1284 try:
1331 try:
1285 regexp = re.compile(pattern, reflags)
1332 regexp = re.compile(pattern, reflags)
1286 except Exception, inst:
1333 except Exception, inst:
1287 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1334 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1288 return None
1335 return 1
1289 sep, eol = ':', '\n'
1336 sep, eol = ':', '\n'
1290 if opts.get('print0'):
1337 if opts.get('print0'):
1291 sep = eol = '\0'
1338 sep = eol = '\0'
1292
1339
1293 getfile = util.lrucachefunc(repo.file)
1340 getfile = util.lrucachefunc(repo.file)
1294
1341
1295 def matchlines(body):
1342 def matchlines(body):
1296 begin = 0
1343 begin = 0
1297 linenum = 0
1344 linenum = 0
1298 while True:
1345 while True:
1299 match = regexp.search(body, begin)
1346 match = regexp.search(body, begin)
1300 if not match:
1347 if not match:
1301 break
1348 break
1302 mstart, mend = match.span()
1349 mstart, mend = match.span()
1303 linenum += body.count('\n', begin, mstart) + 1
1350 linenum += body.count('\n', begin, mstart) + 1
1304 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1351 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1305 begin = body.find('\n', mend) + 1 or len(body)
1352 begin = body.find('\n', mend) + 1 or len(body)
1306 lend = begin - 1
1353 lend = begin - 1
1307 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1354 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1308
1355
1309 class linestate(object):
1356 class linestate(object):
1310 def __init__(self, line, linenum, colstart, colend):
1357 def __init__(self, line, linenum, colstart, colend):
1311 self.line = line
1358 self.line = line
1312 self.linenum = linenum
1359 self.linenum = linenum
1313 self.colstart = colstart
1360 self.colstart = colstart
1314 self.colend = colend
1361 self.colend = colend
1315
1362
1316 def __hash__(self):
1363 def __hash__(self):
1317 return hash((self.linenum, self.line))
1364 return hash((self.linenum, self.line))
1318
1365
1319 def __eq__(self, other):
1366 def __eq__(self, other):
1320 return self.line == other.line
1367 return self.line == other.line
1321
1368
1322 matches = {}
1369 matches = {}
1323 copies = {}
1370 copies = {}
1324 def grepbody(fn, rev, body):
1371 def grepbody(fn, rev, body):
1325 matches[rev].setdefault(fn, [])
1372 matches[rev].setdefault(fn, [])
1326 m = matches[rev][fn]
1373 m = matches[rev][fn]
1327 for lnum, cstart, cend, line in matchlines(body):
1374 for lnum, cstart, cend, line in matchlines(body):
1328 s = linestate(line, lnum, cstart, cend)
1375 s = linestate(line, lnum, cstart, cend)
1329 m.append(s)
1376 m.append(s)
1330
1377
1331 def difflinestates(a, b):
1378 def difflinestates(a, b):
1332 sm = difflib.SequenceMatcher(None, a, b)
1379 sm = difflib.SequenceMatcher(None, a, b)
1333 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1380 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1334 if tag == 'insert':
1381 if tag == 'insert':
1335 for i in xrange(blo, bhi):
1382 for i in xrange(blo, bhi):
1336 yield ('+', b[i])
1383 yield ('+', b[i])
1337 elif tag == 'delete':
1384 elif tag == 'delete':
1338 for i in xrange(alo, ahi):
1385 for i in xrange(alo, ahi):
1339 yield ('-', a[i])
1386 yield ('-', a[i])
1340 elif tag == 'replace':
1387 elif tag == 'replace':
1341 for i in xrange(alo, ahi):
1388 for i in xrange(alo, ahi):
1342 yield ('-', a[i])
1389 yield ('-', a[i])
1343 for i in xrange(blo, bhi):
1390 for i in xrange(blo, bhi):
1344 yield ('+', b[i])
1391 yield ('+', b[i])
1345
1392
1346 def display(fn, ctx, pstates, states):
1393 def display(fn, ctx, pstates, states):
1347 rev = ctx.rev()
1394 rev = ctx.rev()
1348 datefunc = ui.quiet and util.shortdate or util.datestr
1395 datefunc = ui.quiet and util.shortdate or util.datestr
1349 found = False
1396 found = False
1350 filerevmatches = {}
1397 filerevmatches = {}
1351 if opts.get('all'):
1398 if opts.get('all'):
1352 iter = difflinestates(pstates, states)
1399 iter = difflinestates(pstates, states)
1353 else:
1400 else:
1354 iter = [('', l) for l in states]
1401 iter = [('', l) for l in states]
1355 for change, l in iter:
1402 for change, l in iter:
1356 cols = [fn, str(rev)]
1403 cols = [fn, str(rev)]
1357 before, match, after = None, None, None
1404 before, match, after = None, None, None
1358 if opts.get('line_number'):
1405 if opts.get('line_number'):
1359 cols.append(str(l.linenum))
1406 cols.append(str(l.linenum))
1360 if opts.get('all'):
1407 if opts.get('all'):
1361 cols.append(change)
1408 cols.append(change)
1362 if opts.get('user'):
1409 if opts.get('user'):
1363 cols.append(ui.shortuser(ctx.user()))
1410 cols.append(ui.shortuser(ctx.user()))
1364 if opts.get('date'):
1411 if opts.get('date'):
1365 cols.append(datefunc(ctx.date()))
1412 cols.append(datefunc(ctx.date()))
1366 if opts.get('files_with_matches'):
1413 if opts.get('files_with_matches'):
1367 c = (fn, rev)
1414 c = (fn, rev)
1368 if c in filerevmatches:
1415 if c in filerevmatches:
1369 continue
1416 continue
1370 filerevmatches[c] = 1
1417 filerevmatches[c] = 1
1371 else:
1418 else:
1372 before = l.line[:l.colstart]
1419 before = l.line[:l.colstart]
1373 match = l.line[l.colstart:l.colend]
1420 match = l.line[l.colstart:l.colend]
1374 after = l.line[l.colend:]
1421 after = l.line[l.colend:]
1375 ui.write(sep.join(cols))
1422 ui.write(sep.join(cols))
1376 if before is not None:
1423 if before is not None:
1377 ui.write(sep + before)
1424 ui.write(sep + before)
1378 ui.write(match, label='grep.match')
1425 ui.write(match, label='grep.match')
1379 ui.write(after)
1426 ui.write(after)
1380 ui.write(eol)
1427 ui.write(eol)
1381 found = True
1428 found = True
1382 return found
1429 return found
1383
1430
1384 skip = {}
1431 skip = {}
1385 revfiles = {}
1432 revfiles = {}
1386 matchfn = cmdutil.match(repo, pats, opts)
1433 matchfn = cmdutil.match(repo, pats, opts)
1387 found = False
1434 found = False
1388 follow = opts.get('follow')
1435 follow = opts.get('follow')
1389
1436
1390 def prep(ctx, fns):
1437 def prep(ctx, fns):
1391 rev = ctx.rev()
1438 rev = ctx.rev()
1392 pctx = ctx.parents()[0]
1439 pctx = ctx.parents()[0]
1393 parent = pctx.rev()
1440 parent = pctx.rev()
1394 matches.setdefault(rev, {})
1441 matches.setdefault(rev, {})
1395 matches.setdefault(parent, {})
1442 matches.setdefault(parent, {})
1396 files = revfiles.setdefault(rev, [])
1443 files = revfiles.setdefault(rev, [])
1397 for fn in fns:
1444 for fn in fns:
1398 flog = getfile(fn)
1445 flog = getfile(fn)
1399 try:
1446 try:
1400 fnode = ctx.filenode(fn)
1447 fnode = ctx.filenode(fn)
1401 except error.LookupError:
1448 except error.LookupError:
1402 continue
1449 continue
1403
1450
1404 copied = flog.renamed(fnode)
1451 copied = flog.renamed(fnode)
1405 copy = follow and copied and copied[0]
1452 copy = follow and copied and copied[0]
1406 if copy:
1453 if copy:
1407 copies.setdefault(rev, {})[fn] = copy
1454 copies.setdefault(rev, {})[fn] = copy
1408 if fn in skip:
1455 if fn in skip:
1409 if copy:
1456 if copy:
1410 skip[copy] = True
1457 skip[copy] = True
1411 continue
1458 continue
1412 files.append(fn)
1459 files.append(fn)
1413
1460
1414 if fn not in matches[rev]:
1461 if fn not in matches[rev]:
1415 grepbody(fn, rev, flog.read(fnode))
1462 grepbody(fn, rev, flog.read(fnode))
1416
1463
1417 pfn = copy or fn
1464 pfn = copy or fn
1418 if pfn not in matches[parent]:
1465 if pfn not in matches[parent]:
1419 try:
1466 try:
1420 fnode = pctx.filenode(pfn)
1467 fnode = pctx.filenode(pfn)
1421 grepbody(pfn, parent, flog.read(fnode))
1468 grepbody(pfn, parent, flog.read(fnode))
1422 except error.LookupError:
1469 except error.LookupError:
1423 pass
1470 pass
1424
1471
1425 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1472 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1426 rev = ctx.rev()
1473 rev = ctx.rev()
1427 parent = ctx.parents()[0].rev()
1474 parent = ctx.parents()[0].rev()
1428 for fn in sorted(revfiles.get(rev, [])):
1475 for fn in sorted(revfiles.get(rev, [])):
1429 states = matches[rev][fn]
1476 states = matches[rev][fn]
1430 copy = copies.get(rev, {}).get(fn)
1477 copy = copies.get(rev, {}).get(fn)
1431 if fn in skip:
1478 if fn in skip:
1432 if copy:
1479 if copy:
1433 skip[copy] = True
1480 skip[copy] = True
1434 continue
1481 continue
1435 pstates = matches.get(parent, {}).get(copy or fn, [])
1482 pstates = matches.get(parent, {}).get(copy or fn, [])
1436 if pstates or states:
1483 if pstates or states:
1437 r = display(fn, ctx, pstates, states)
1484 r = display(fn, ctx, pstates, states)
1438 found = found or r
1485 found = found or r
1439 if r and not opts.get('all'):
1486 if r and not opts.get('all'):
1440 skip[fn] = True
1487 skip[fn] = True
1441 if copy:
1488 if copy:
1442 skip[copy] = True
1489 skip[copy] = True
1443 del matches[rev]
1490 del matches[rev]
1444 del revfiles[rev]
1491 del revfiles[rev]
1445
1492
1493 return not found
1494
1446 def heads(ui, repo, *branchrevs, **opts):
1495 def heads(ui, repo, *branchrevs, **opts):
1447 """show current repository heads or show branch heads
1496 """show current repository heads or show branch heads
1448
1497
1449 With no arguments, show all repository branch heads.
1498 With no arguments, show all repository branch heads.
1450
1499
1451 Repository "heads" are changesets with no child changesets. They are
1500 Repository "heads" are changesets with no child changesets. They are
1452 where development generally takes place and are the usual targets
1501 where development generally takes place and are the usual targets
1453 for update and merge operations. Branch heads are changesets that have
1502 for update and merge operations. Branch heads are changesets that have
1454 no child changeset on the same branch.
1503 no child changeset on the same branch.
1455
1504
1456 If one or more REVs are given, only branch heads on the branches
1505 If one or more REVs are given, only branch heads on the branches
1457 associated with the specified changesets are shown.
1506 associated with the specified changesets are shown.
1458
1507
1459 If -c/--closed is specified, also show branch heads marked closed
1508 If -c/--closed is specified, also show branch heads marked closed
1460 (see hg commit --close-branch).
1509 (see hg commit --close-branch).
1461
1510
1462 If STARTREV is specified, only those heads that are descendants of
1511 If STARTREV is specified, only those heads that are descendants of
1463 STARTREV will be displayed.
1512 STARTREV will be displayed.
1464
1513
1465 If -t/--topo is specified, named branch mechanics will be ignored and only
1514 If -t/--topo is specified, named branch mechanics will be ignored and only
1466 changesets without children will be shown.
1515 changesets without children will be shown.
1516
1517 Returns 0 if matching heads are found, 1 if not.
1467 """
1518 """
1468
1519
1469 if opts.get('rev'):
1520 if opts.get('rev'):
1470 start = repo.lookup(opts['rev'])
1521 start = repo.lookup(opts['rev'])
1471 else:
1522 else:
1472 start = None
1523 start = None
1473
1524
1474 if opts.get('topo'):
1525 if opts.get('topo'):
1475 heads = [repo[h] for h in repo.heads(start)]
1526 heads = [repo[h] for h in repo.heads(start)]
1476 else:
1527 else:
1477 heads = []
1528 heads = []
1478 for b, ls in repo.branchmap().iteritems():
1529 for b, ls in repo.branchmap().iteritems():
1479 if start is None:
1530 if start is None:
1480 heads += [repo[h] for h in ls]
1531 heads += [repo[h] for h in ls]
1481 continue
1532 continue
1482 startrev = repo.changelog.rev(start)
1533 startrev = repo.changelog.rev(start)
1483 descendants = set(repo.changelog.descendants(startrev))
1534 descendants = set(repo.changelog.descendants(startrev))
1484 descendants.add(startrev)
1535 descendants.add(startrev)
1485 rev = repo.changelog.rev
1536 rev = repo.changelog.rev
1486 heads += [repo[h] for h in ls if rev(h) in descendants]
1537 heads += [repo[h] for h in ls if rev(h) in descendants]
1487
1538
1488 if branchrevs:
1539 if branchrevs:
1489 decode, encode = encoding.fromlocal, encoding.tolocal
1540 decode, encode = encoding.fromlocal, encoding.tolocal
1490 branches = set(repo[decode(br)].branch() for br in branchrevs)
1541 branches = set(repo[decode(br)].branch() for br in branchrevs)
1491 heads = [h for h in heads if h.branch() in branches]
1542 heads = [h for h in heads if h.branch() in branches]
1492
1543
1493 if not opts.get('closed'):
1544 if not opts.get('closed'):
1494 heads = [h for h in heads if not h.extra().get('close')]
1545 heads = [h for h in heads if not h.extra().get('close')]
1495
1546
1496 if opts.get('active') and branchrevs:
1547 if opts.get('active') and branchrevs:
1497 dagheads = repo.heads(start)
1548 dagheads = repo.heads(start)
1498 heads = [h for h in heads if h.node() in dagheads]
1549 heads = [h for h in heads if h.node() in dagheads]
1499
1550
1500 if branchrevs:
1551 if branchrevs:
1501 haveheads = set(h.branch() for h in heads)
1552 haveheads = set(h.branch() for h in heads)
1502 if branches - haveheads:
1553 if branches - haveheads:
1503 headless = ', '.join(encode(b) for b in branches - haveheads)
1554 headless = ', '.join(encode(b) for b in branches - haveheads)
1504 msg = _('no open branch heads found on branches %s')
1555 msg = _('no open branch heads found on branches %s')
1505 if opts.get('rev'):
1556 if opts.get('rev'):
1506 msg += _(' (started at %s)' % opts['rev'])
1557 msg += _(' (started at %s)' % opts['rev'])
1507 ui.warn((msg + '\n') % headless)
1558 ui.warn((msg + '\n') % headless)
1508
1559
1509 if not heads:
1560 if not heads:
1510 return 1
1561 return 1
1511
1562
1512 heads = sorted(heads, key=lambda x: -x.rev())
1563 heads = sorted(heads, key=lambda x: -x.rev())
1513 displayer = cmdutil.show_changeset(ui, repo, opts)
1564 displayer = cmdutil.show_changeset(ui, repo, opts)
1514 for ctx in heads:
1565 for ctx in heads:
1515 displayer.show(ctx)
1566 displayer.show(ctx)
1516 displayer.close()
1567 displayer.close()
1517
1568
1518 def help_(ui, name=None, with_version=False, unknowncmd=False):
1569 def help_(ui, name=None, with_version=False, unknowncmd=False):
1519 """show help for a given topic or a help overview
1570 """show help for a given topic or a help overview
1520
1571
1521 With no arguments, print a list of commands with short help messages.
1572 With no arguments, print a list of commands with short help messages.
1522
1573
1523 Given a topic, extension, or command name, print help for that
1574 Given a topic, extension, or command name, print help for that
1524 topic."""
1575 topic.
1576
1577 Returns 0 if successful.
1578 """
1525 option_lists = []
1579 option_lists = []
1526 textwidth = util.termwidth() - 2
1580 textwidth = util.termwidth() - 2
1527
1581
1528 def addglobalopts(aliases):
1582 def addglobalopts(aliases):
1529 if ui.verbose:
1583 if ui.verbose:
1530 option_lists.append((_("global options:"), globalopts))
1584 option_lists.append((_("global options:"), globalopts))
1531 if name == 'shortlist':
1585 if name == 'shortlist':
1532 option_lists.append((_('use "hg help" for the full list '
1586 option_lists.append((_('use "hg help" for the full list '
1533 'of commands'), ()))
1587 'of commands'), ()))
1534 else:
1588 else:
1535 if name == 'shortlist':
1589 if name == 'shortlist':
1536 msg = _('use "hg help" for the full list of commands '
1590 msg = _('use "hg help" for the full list of commands '
1537 'or "hg -v" for details')
1591 'or "hg -v" for details')
1538 elif aliases:
1592 elif aliases:
1539 msg = _('use "hg -v help%s" to show aliases and '
1593 msg = _('use "hg -v help%s" to show aliases and '
1540 'global options') % (name and " " + name or "")
1594 'global options') % (name and " " + name or "")
1541 else:
1595 else:
1542 msg = _('use "hg -v help %s" to show global options') % name
1596 msg = _('use "hg -v help %s" to show global options') % name
1543 option_lists.append((msg, ()))
1597 option_lists.append((msg, ()))
1544
1598
1545 def helpcmd(name):
1599 def helpcmd(name):
1546 if with_version:
1600 if with_version:
1547 version_(ui)
1601 version_(ui)
1548 ui.write('\n')
1602 ui.write('\n')
1549
1603
1550 try:
1604 try:
1551 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1605 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1552 except error.AmbiguousCommand, inst:
1606 except error.AmbiguousCommand, inst:
1553 # py3k fix: except vars can't be used outside the scope of the
1607 # py3k fix: except vars can't be used outside the scope of the
1554 # except block, nor can be used inside a lambda. python issue4617
1608 # except block, nor can be used inside a lambda. python issue4617
1555 prefix = inst.args[0]
1609 prefix = inst.args[0]
1556 select = lambda c: c.lstrip('^').startswith(prefix)
1610 select = lambda c: c.lstrip('^').startswith(prefix)
1557 helplist(_('list of commands:\n\n'), select)
1611 helplist(_('list of commands:\n\n'), select)
1558 return
1612 return
1559
1613
1560 # check if it's an invalid alias and display its error if it is
1614 # check if it's an invalid alias and display its error if it is
1561 if getattr(entry[0], 'badalias', False):
1615 if getattr(entry[0], 'badalias', False):
1562 if not unknowncmd:
1616 if not unknowncmd:
1563 entry[0](ui)
1617 entry[0](ui)
1564 return
1618 return
1565
1619
1566 # synopsis
1620 # synopsis
1567 if len(entry) > 2:
1621 if len(entry) > 2:
1568 if entry[2].startswith('hg'):
1622 if entry[2].startswith('hg'):
1569 ui.write("%s\n" % entry[2])
1623 ui.write("%s\n" % entry[2])
1570 else:
1624 else:
1571 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1625 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1572 else:
1626 else:
1573 ui.write('hg %s\n' % aliases[0])
1627 ui.write('hg %s\n' % aliases[0])
1574
1628
1575 # aliases
1629 # aliases
1576 if not ui.quiet and len(aliases) > 1:
1630 if not ui.quiet and len(aliases) > 1:
1577 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1631 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1578
1632
1579 # description
1633 # description
1580 doc = gettext(entry[0].__doc__)
1634 doc = gettext(entry[0].__doc__)
1581 if not doc:
1635 if not doc:
1582 doc = _("(no help text available)")
1636 doc = _("(no help text available)")
1583 if hasattr(entry[0], 'definition'): # aliased command
1637 if hasattr(entry[0], 'definition'): # aliased command
1584 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1638 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1585 if ui.quiet:
1639 if ui.quiet:
1586 doc = doc.splitlines()[0]
1640 doc = doc.splitlines()[0]
1587 keep = ui.verbose and ['verbose'] or []
1641 keep = ui.verbose and ['verbose'] or []
1588 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1642 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1589 ui.write("\n%s\n" % formatted)
1643 ui.write("\n%s\n" % formatted)
1590 if pruned:
1644 if pruned:
1591 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1645 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1592
1646
1593 if not ui.quiet:
1647 if not ui.quiet:
1594 # options
1648 # options
1595 if entry[1]:
1649 if entry[1]:
1596 option_lists.append((_("options:\n"), entry[1]))
1650 option_lists.append((_("options:\n"), entry[1]))
1597
1651
1598 addglobalopts(False)
1652 addglobalopts(False)
1599
1653
1600 def helplist(header, select=None):
1654 def helplist(header, select=None):
1601 h = {}
1655 h = {}
1602 cmds = {}
1656 cmds = {}
1603 for c, e in table.iteritems():
1657 for c, e in table.iteritems():
1604 f = c.split("|", 1)[0]
1658 f = c.split("|", 1)[0]
1605 if select and not select(f):
1659 if select and not select(f):
1606 continue
1660 continue
1607 if (not select and name != 'shortlist' and
1661 if (not select and name != 'shortlist' and
1608 e[0].__module__ != __name__):
1662 e[0].__module__ != __name__):
1609 continue
1663 continue
1610 if name == "shortlist" and not f.startswith("^"):
1664 if name == "shortlist" and not f.startswith("^"):
1611 continue
1665 continue
1612 f = f.lstrip("^")
1666 f = f.lstrip("^")
1613 if not ui.debugflag and f.startswith("debug"):
1667 if not ui.debugflag and f.startswith("debug"):
1614 continue
1668 continue
1615 doc = e[0].__doc__
1669 doc = e[0].__doc__
1616 if doc and 'DEPRECATED' in doc and not ui.verbose:
1670 if doc and 'DEPRECATED' in doc and not ui.verbose:
1617 continue
1671 continue
1618 doc = gettext(doc)
1672 doc = gettext(doc)
1619 if not doc:
1673 if not doc:
1620 doc = _("(no help text available)")
1674 doc = _("(no help text available)")
1621 h[f] = doc.splitlines()[0].rstrip()
1675 h[f] = doc.splitlines()[0].rstrip()
1622 cmds[f] = c.lstrip("^")
1676 cmds[f] = c.lstrip("^")
1623
1677
1624 if not h:
1678 if not h:
1625 ui.status(_('no commands defined\n'))
1679 ui.status(_('no commands defined\n'))
1626 return
1680 return
1627
1681
1628 ui.status(header)
1682 ui.status(header)
1629 fns = sorted(h)
1683 fns = sorted(h)
1630 m = max(map(len, fns))
1684 m = max(map(len, fns))
1631 for f in fns:
1685 for f in fns:
1632 if ui.verbose:
1686 if ui.verbose:
1633 commands = cmds[f].replace("|",", ")
1687 commands = cmds[f].replace("|",", ")
1634 ui.write(" %s:\n %s\n"%(commands, h[f]))
1688 ui.write(" %s:\n %s\n"%(commands, h[f]))
1635 else:
1689 else:
1636 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1690 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1637
1691
1638 if not ui.quiet:
1692 if not ui.quiet:
1639 addglobalopts(True)
1693 addglobalopts(True)
1640
1694
1641 def helptopic(name):
1695 def helptopic(name):
1642 for names, header, doc in help.helptable:
1696 for names, header, doc in help.helptable:
1643 if name in names:
1697 if name in names:
1644 break
1698 break
1645 else:
1699 else:
1646 raise error.UnknownCommand(name)
1700 raise error.UnknownCommand(name)
1647
1701
1648 # description
1702 # description
1649 if not doc:
1703 if not doc:
1650 doc = _("(no help text available)")
1704 doc = _("(no help text available)")
1651 if hasattr(doc, '__call__'):
1705 if hasattr(doc, '__call__'):
1652 doc = doc()
1706 doc = doc()
1653
1707
1654 ui.write("%s\n\n" % header)
1708 ui.write("%s\n\n" % header)
1655 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1709 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1656
1710
1657 def helpext(name):
1711 def helpext(name):
1658 try:
1712 try:
1659 mod = extensions.find(name)
1713 mod = extensions.find(name)
1660 doc = gettext(mod.__doc__) or _('no help text available')
1714 doc = gettext(mod.__doc__) or _('no help text available')
1661 except KeyError:
1715 except KeyError:
1662 mod = None
1716 mod = None
1663 doc = extensions.disabledext(name)
1717 doc = extensions.disabledext(name)
1664 if not doc:
1718 if not doc:
1665 raise error.UnknownCommand(name)
1719 raise error.UnknownCommand(name)
1666
1720
1667 if '\n' not in doc:
1721 if '\n' not in doc:
1668 head, tail = doc, ""
1722 head, tail = doc, ""
1669 else:
1723 else:
1670 head, tail = doc.split('\n', 1)
1724 head, tail = doc.split('\n', 1)
1671 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1725 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1672 if tail:
1726 if tail:
1673 ui.write(minirst.format(tail, textwidth))
1727 ui.write(minirst.format(tail, textwidth))
1674 ui.status('\n\n')
1728 ui.status('\n\n')
1675
1729
1676 if mod:
1730 if mod:
1677 try:
1731 try:
1678 ct = mod.cmdtable
1732 ct = mod.cmdtable
1679 except AttributeError:
1733 except AttributeError:
1680 ct = {}
1734 ct = {}
1681 modcmds = set([c.split('|', 1)[0] for c in ct])
1735 modcmds = set([c.split('|', 1)[0] for c in ct])
1682 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1736 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1683 else:
1737 else:
1684 ui.write(_('use "hg help extensions" for information on enabling '
1738 ui.write(_('use "hg help extensions" for information on enabling '
1685 'extensions\n'))
1739 'extensions\n'))
1686
1740
1687 def helpextcmd(name):
1741 def helpextcmd(name):
1688 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1742 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1689 doc = gettext(mod.__doc__).splitlines()[0]
1743 doc = gettext(mod.__doc__).splitlines()[0]
1690
1744
1691 msg = help.listexts(_("'%s' is provided by the following "
1745 msg = help.listexts(_("'%s' is provided by the following "
1692 "extension:") % cmd, {ext: doc}, len(ext),
1746 "extension:") % cmd, {ext: doc}, len(ext),
1693 indent=4)
1747 indent=4)
1694 ui.write(minirst.format(msg, textwidth))
1748 ui.write(minirst.format(msg, textwidth))
1695 ui.write('\n\n')
1749 ui.write('\n\n')
1696 ui.write(_('use "hg help extensions" for information on enabling '
1750 ui.write(_('use "hg help extensions" for information on enabling '
1697 'extensions\n'))
1751 'extensions\n'))
1698
1752
1699 if name and name != 'shortlist':
1753 if name and name != 'shortlist':
1700 i = None
1754 i = None
1701 if unknowncmd:
1755 if unknowncmd:
1702 queries = (helpextcmd,)
1756 queries = (helpextcmd,)
1703 else:
1757 else:
1704 queries = (helptopic, helpcmd, helpext, helpextcmd)
1758 queries = (helptopic, helpcmd, helpext, helpextcmd)
1705 for f in queries:
1759 for f in queries:
1706 try:
1760 try:
1707 f(name)
1761 f(name)
1708 i = None
1762 i = None
1709 break
1763 break
1710 except error.UnknownCommand, inst:
1764 except error.UnknownCommand, inst:
1711 i = inst
1765 i = inst
1712 if i:
1766 if i:
1713 raise i
1767 raise i
1714
1768
1715 else:
1769 else:
1716 # program name
1770 # program name
1717 if ui.verbose or with_version:
1771 if ui.verbose or with_version:
1718 version_(ui)
1772 version_(ui)
1719 else:
1773 else:
1720 ui.status(_("Mercurial Distributed SCM\n"))
1774 ui.status(_("Mercurial Distributed SCM\n"))
1721 ui.status('\n')
1775 ui.status('\n')
1722
1776
1723 # list of commands
1777 # list of commands
1724 if name == "shortlist":
1778 if name == "shortlist":
1725 header = _('basic commands:\n\n')
1779 header = _('basic commands:\n\n')
1726 else:
1780 else:
1727 header = _('list of commands:\n\n')
1781 header = _('list of commands:\n\n')
1728
1782
1729 helplist(header)
1783 helplist(header)
1730 if name != 'shortlist':
1784 if name != 'shortlist':
1731 exts, maxlength = extensions.enabled()
1785 exts, maxlength = extensions.enabled()
1732 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1786 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1733 if text:
1787 if text:
1734 ui.write("\n%s\n" % minirst.format(text, textwidth))
1788 ui.write("\n%s\n" % minirst.format(text, textwidth))
1735
1789
1736 # list all option lists
1790 # list all option lists
1737 opt_output = []
1791 opt_output = []
1738 for title, options in option_lists:
1792 for title, options in option_lists:
1739 opt_output.append(("\n%s" % title, None))
1793 opt_output.append(("\n%s" % title, None))
1740 for shortopt, longopt, default, desc in options:
1794 for shortopt, longopt, default, desc in options:
1741 if _("DEPRECATED") in desc and not ui.verbose:
1795 if _("DEPRECATED") in desc and not ui.verbose:
1742 continue
1796 continue
1743 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1797 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1744 longopt and " --%s" % longopt),
1798 longopt and " --%s" % longopt),
1745 "%s%s" % (desc,
1799 "%s%s" % (desc,
1746 default
1800 default
1747 and _(" (default: %s)") % default
1801 and _(" (default: %s)") % default
1748 or "")))
1802 or "")))
1749
1803
1750 if not name:
1804 if not name:
1751 ui.write(_("\nadditional help topics:\n\n"))
1805 ui.write(_("\nadditional help topics:\n\n"))
1752 topics = []
1806 topics = []
1753 for names, header, doc in help.helptable:
1807 for names, header, doc in help.helptable:
1754 topics.append((sorted(names, key=len, reverse=True)[0], header))
1808 topics.append((sorted(names, key=len, reverse=True)[0], header))
1755 topics_len = max([len(s[0]) for s in topics])
1809 topics_len = max([len(s[0]) for s in topics])
1756 for t, desc in topics:
1810 for t, desc in topics:
1757 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1811 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1758
1812
1759 if opt_output:
1813 if opt_output:
1760 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1814 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1761 for first, second in opt_output:
1815 for first, second in opt_output:
1762 if second:
1816 if second:
1763 second = util.wrap(second, opts_len + 3)
1817 second = util.wrap(second, opts_len + 3)
1764 ui.write(" %-*s %s\n" % (opts_len, first, second))
1818 ui.write(" %-*s %s\n" % (opts_len, first, second))
1765 else:
1819 else:
1766 ui.write("%s\n" % first)
1820 ui.write("%s\n" % first)
1767
1821
1768 def identify(ui, repo, source=None,
1822 def identify(ui, repo, source=None,
1769 rev=None, num=None, id=None, branch=None, tags=None):
1823 rev=None, num=None, id=None, branch=None, tags=None):
1770 """identify the working copy or specified revision
1824 """identify the working copy or specified revision
1771
1825
1772 With no revision, print a summary of the current state of the
1826 With no revision, print a summary of the current state of the
1773 repository.
1827 repository.
1774
1828
1775 Specifying a path to a repository root or Mercurial bundle will
1829 Specifying a path to a repository root or Mercurial bundle will
1776 cause lookup to operate on that repository/bundle.
1830 cause lookup to operate on that repository/bundle.
1777
1831
1778 This summary identifies the repository state using one or two
1832 This summary identifies the repository state using one or two
1779 parent hash identifiers, followed by a "+" if there are
1833 parent hash identifiers, followed by a "+" if there are
1780 uncommitted changes in the working directory, a list of tags for
1834 uncommitted changes in the working directory, a list of tags for
1781 this revision and a branch name for non-default branches.
1835 this revision and a branch name for non-default branches.
1836
1837 Returns 0 if successful.
1782 """
1838 """
1783
1839
1784 if not repo and not source:
1840 if not repo and not source:
1785 raise util.Abort(_("There is no Mercurial repository here "
1841 raise util.Abort(_("There is no Mercurial repository here "
1786 "(.hg not found)"))
1842 "(.hg not found)"))
1787
1843
1788 hexfunc = ui.debugflag and hex or short
1844 hexfunc = ui.debugflag and hex or short
1789 default = not (num or id or branch or tags)
1845 default = not (num or id or branch or tags)
1790 output = []
1846 output = []
1791
1847
1792 revs = []
1848 revs = []
1793 if source:
1849 if source:
1794 source, branches = hg.parseurl(ui.expandpath(source))
1850 source, branches = hg.parseurl(ui.expandpath(source))
1795 repo = hg.repository(ui, source)
1851 repo = hg.repository(ui, source)
1796 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1852 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1797
1853
1798 if not repo.local():
1854 if not repo.local():
1799 if not rev and revs:
1855 if not rev and revs:
1800 rev = revs[0]
1856 rev = revs[0]
1801 if not rev:
1857 if not rev:
1802 rev = "tip"
1858 rev = "tip"
1803 if num or branch or tags:
1859 if num or branch or tags:
1804 raise util.Abort(
1860 raise util.Abort(
1805 "can't query remote revision number, branch, or tags")
1861 "can't query remote revision number, branch, or tags")
1806 output = [hexfunc(repo.lookup(rev))]
1862 output = [hexfunc(repo.lookup(rev))]
1807 elif not rev:
1863 elif not rev:
1808 ctx = repo[None]
1864 ctx = repo[None]
1809 parents = ctx.parents()
1865 parents = ctx.parents()
1810 changed = False
1866 changed = False
1811 if default or id or num:
1867 if default or id or num:
1812 changed = util.any(repo.status())
1868 changed = util.any(repo.status())
1813 if default or id:
1869 if default or id:
1814 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1870 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1815 (changed) and "+" or "")]
1871 (changed) and "+" or "")]
1816 if num:
1872 if num:
1817 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1873 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1818 (changed) and "+" or ""))
1874 (changed) and "+" or ""))
1819 else:
1875 else:
1820 ctx = repo[rev]
1876 ctx = repo[rev]
1821 if default or id:
1877 if default or id:
1822 output = [hexfunc(ctx.node())]
1878 output = [hexfunc(ctx.node())]
1823 if num:
1879 if num:
1824 output.append(str(ctx.rev()))
1880 output.append(str(ctx.rev()))
1825
1881
1826 if repo.local() and default and not ui.quiet:
1882 if repo.local() and default and not ui.quiet:
1827 b = encoding.tolocal(ctx.branch())
1883 b = encoding.tolocal(ctx.branch())
1828 if b != 'default':
1884 if b != 'default':
1829 output.append("(%s)" % b)
1885 output.append("(%s)" % b)
1830
1886
1831 # multiple tags for a single parent separated by '/'
1887 # multiple tags for a single parent separated by '/'
1832 t = "/".join(ctx.tags())
1888 t = "/".join(ctx.tags())
1833 if t:
1889 if t:
1834 output.append(t)
1890 output.append(t)
1835
1891
1836 if branch:
1892 if branch:
1837 output.append(encoding.tolocal(ctx.branch()))
1893 output.append(encoding.tolocal(ctx.branch()))
1838
1894
1839 if tags:
1895 if tags:
1840 output.extend(ctx.tags())
1896 output.extend(ctx.tags())
1841
1897
1842 ui.write("%s\n" % ' '.join(output))
1898 ui.write("%s\n" % ' '.join(output))
1843
1899
1844 def import_(ui, repo, patch1, *patches, **opts):
1900 def import_(ui, repo, patch1, *patches, **opts):
1845 """import an ordered set of patches
1901 """import an ordered set of patches
1846
1902
1847 Import a list of patches and commit them individually (unless
1903 Import a list of patches and commit them individually (unless
1848 --no-commit is specified).
1904 --no-commit is specified).
1849
1905
1850 If there are outstanding changes in the working directory, import
1906 If there are outstanding changes in the working directory, import
1851 will abort unless given the -f/--force flag.
1907 will abort unless given the -f/--force flag.
1852
1908
1853 You can import a patch straight from a mail message. Even patches
1909 You can import a patch straight from a mail message. Even patches
1854 as attachments work (to use the body part, it must have type
1910 as attachments work (to use the body part, it must have type
1855 text/plain or text/x-patch). From and Subject headers of email
1911 text/plain or text/x-patch). From and Subject headers of email
1856 message are used as default committer and commit message. All
1912 message are used as default committer and commit message. All
1857 text/plain body parts before first diff are added to commit
1913 text/plain body parts before first diff are added to commit
1858 message.
1914 message.
1859
1915
1860 If the imported patch was generated by hg export, user and
1916 If the imported patch was generated by hg export, user and
1861 description from patch override values from message headers and
1917 description from patch override values from message headers and
1862 body. Values given on command line with -m/--message and -u/--user
1918 body. Values given on command line with -m/--message and -u/--user
1863 override these.
1919 override these.
1864
1920
1865 If --exact is specified, import will set the working directory to
1921 If --exact is specified, import will set the working directory to
1866 the parent of each patch before applying it, and will abort if the
1922 the parent of each patch before applying it, and will abort if the
1867 resulting changeset has a different ID than the one recorded in
1923 resulting changeset has a different ID than the one recorded in
1868 the patch. This may happen due to character set problems or other
1924 the patch. This may happen due to character set problems or other
1869 deficiencies in the text patch format.
1925 deficiencies in the text patch format.
1870
1926
1871 With -s/--similarity, hg will attempt to discover renames and
1927 With -s/--similarity, hg will attempt to discover renames and
1872 copies in the patch in the same way as 'addremove'.
1928 copies in the patch in the same way as 'addremove'.
1873
1929
1874 To read a patch from standard input, use "-" as the patch name. If
1930 To read a patch from standard input, use "-" as the patch name. If
1875 a URL is specified, the patch will be downloaded from it.
1931 a URL is specified, the patch will be downloaded from it.
1876 See :hg:`help dates` for a list of formats valid for -d/--date.
1932 See :hg:`help dates` for a list of formats valid for -d/--date.
1933
1934 Returns 0 on success.
1877 """
1935 """
1878 patches = (patch1,) + patches
1936 patches = (patch1,) + patches
1879
1937
1880 date = opts.get('date')
1938 date = opts.get('date')
1881 if date:
1939 if date:
1882 opts['date'] = util.parsedate(date)
1940 opts['date'] = util.parsedate(date)
1883
1941
1884 try:
1942 try:
1885 sim = float(opts.get('similarity') or 0)
1943 sim = float(opts.get('similarity') or 0)
1886 except ValueError:
1944 except ValueError:
1887 raise util.Abort(_('similarity must be a number'))
1945 raise util.Abort(_('similarity must be a number'))
1888 if sim < 0 or sim > 100:
1946 if sim < 0 or sim > 100:
1889 raise util.Abort(_('similarity must be between 0 and 100'))
1947 raise util.Abort(_('similarity must be between 0 and 100'))
1890
1948
1891 if opts.get('exact') or not opts.get('force'):
1949 if opts.get('exact') or not opts.get('force'):
1892 cmdutil.bail_if_changed(repo)
1950 cmdutil.bail_if_changed(repo)
1893
1951
1894 d = opts["base"]
1952 d = opts["base"]
1895 strip = opts["strip"]
1953 strip = opts["strip"]
1896 wlock = lock = None
1954 wlock = lock = None
1897
1955
1898 def tryone(ui, hunk):
1956 def tryone(ui, hunk):
1899 tmpname, message, user, date, branch, nodeid, p1, p2 = \
1957 tmpname, message, user, date, branch, nodeid, p1, p2 = \
1900 patch.extract(ui, hunk)
1958 patch.extract(ui, hunk)
1901
1959
1902 if not tmpname:
1960 if not tmpname:
1903 return None
1961 return None
1904 commitid = _('to working directory')
1962 commitid = _('to working directory')
1905
1963
1906 try:
1964 try:
1907 cmdline_message = cmdutil.logmessage(opts)
1965 cmdline_message = cmdutil.logmessage(opts)
1908 if cmdline_message:
1966 if cmdline_message:
1909 # pickup the cmdline msg
1967 # pickup the cmdline msg
1910 message = cmdline_message
1968 message = cmdline_message
1911 elif message:
1969 elif message:
1912 # pickup the patch msg
1970 # pickup the patch msg
1913 message = message.strip()
1971 message = message.strip()
1914 else:
1972 else:
1915 # launch the editor
1973 # launch the editor
1916 message = None
1974 message = None
1917 ui.debug('message:\n%s\n' % message)
1975 ui.debug('message:\n%s\n' % message)
1918
1976
1919 wp = repo.parents()
1977 wp = repo.parents()
1920 if opts.get('exact'):
1978 if opts.get('exact'):
1921 if not nodeid or not p1:
1979 if not nodeid or not p1:
1922 raise util.Abort(_('not a Mercurial patch'))
1980 raise util.Abort(_('not a Mercurial patch'))
1923 p1 = repo.lookup(p1)
1981 p1 = repo.lookup(p1)
1924 p2 = repo.lookup(p2 or hex(nullid))
1982 p2 = repo.lookup(p2 or hex(nullid))
1925
1983
1926 if p1 != wp[0].node():
1984 if p1 != wp[0].node():
1927 hg.clean(repo, p1)
1985 hg.clean(repo, p1)
1928 repo.dirstate.setparents(p1, p2)
1986 repo.dirstate.setparents(p1, p2)
1929 elif p2:
1987 elif p2:
1930 try:
1988 try:
1931 p1 = repo.lookup(p1)
1989 p1 = repo.lookup(p1)
1932 p2 = repo.lookup(p2)
1990 p2 = repo.lookup(p2)
1933 if p1 == wp[0].node():
1991 if p1 == wp[0].node():
1934 repo.dirstate.setparents(p1, p2)
1992 repo.dirstate.setparents(p1, p2)
1935 except error.RepoError:
1993 except error.RepoError:
1936 pass
1994 pass
1937 if opts.get('exact') or opts.get('import_branch'):
1995 if opts.get('exact') or opts.get('import_branch'):
1938 repo.dirstate.setbranch(branch or 'default')
1996 repo.dirstate.setbranch(branch or 'default')
1939
1997
1940 files = {}
1998 files = {}
1941 try:
1999 try:
1942 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2000 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1943 files=files, eolmode=None)
2001 files=files, eolmode=None)
1944 finally:
2002 finally:
1945 files = patch.updatedir(ui, repo, files,
2003 files = patch.updatedir(ui, repo, files,
1946 similarity=sim / 100.0)
2004 similarity=sim / 100.0)
1947 if not opts.get('no_commit'):
2005 if not opts.get('no_commit'):
1948 if opts.get('exact'):
2006 if opts.get('exact'):
1949 m = None
2007 m = None
1950 else:
2008 else:
1951 m = cmdutil.matchfiles(repo, files or [])
2009 m = cmdutil.matchfiles(repo, files or [])
1952 n = repo.commit(message, opts.get('user') or user,
2010 n = repo.commit(message, opts.get('user') or user,
1953 opts.get('date') or date, match=m,
2011 opts.get('date') or date, match=m,
1954 editor=cmdutil.commiteditor)
2012 editor=cmdutil.commiteditor)
1955 if opts.get('exact'):
2013 if opts.get('exact'):
1956 if hex(n) != nodeid:
2014 if hex(n) != nodeid:
1957 repo.rollback()
2015 repo.rollback()
1958 raise util.Abort(_('patch is damaged'
2016 raise util.Abort(_('patch is damaged'
1959 ' or loses information'))
2017 ' or loses information'))
1960 # Force a dirstate write so that the next transaction
2018 # Force a dirstate write so that the next transaction
1961 # backups an up-do-date file.
2019 # backups an up-do-date file.
1962 repo.dirstate.write()
2020 repo.dirstate.write()
1963 if n:
2021 if n:
1964 commitid = short(n)
2022 commitid = short(n)
1965
2023
1966 return commitid
2024 return commitid
1967 finally:
2025 finally:
1968 os.unlink(tmpname)
2026 os.unlink(tmpname)
1969
2027
1970 try:
2028 try:
1971 wlock = repo.wlock()
2029 wlock = repo.wlock()
1972 lock = repo.lock()
2030 lock = repo.lock()
1973 lastcommit = None
2031 lastcommit = None
1974 for p in patches:
2032 for p in patches:
1975 pf = os.path.join(d, p)
2033 pf = os.path.join(d, p)
1976
2034
1977 if pf == '-':
2035 if pf == '-':
1978 ui.status(_("applying patch from stdin\n"))
2036 ui.status(_("applying patch from stdin\n"))
1979 pf = sys.stdin
2037 pf = sys.stdin
1980 else:
2038 else:
1981 ui.status(_("applying %s\n") % p)
2039 ui.status(_("applying %s\n") % p)
1982 pf = url.open(ui, pf)
2040 pf = url.open(ui, pf)
1983
2041
1984 haspatch = False
2042 haspatch = False
1985 for hunk in patch.split(pf):
2043 for hunk in patch.split(pf):
1986 commitid = tryone(ui, hunk)
2044 commitid = tryone(ui, hunk)
1987 if commitid:
2045 if commitid:
1988 haspatch = True
2046 haspatch = True
1989 if lastcommit:
2047 if lastcommit:
1990 ui.status(_('applied %s\n') % lastcommit)
2048 ui.status(_('applied %s\n') % lastcommit)
1991 lastcommit = commitid
2049 lastcommit = commitid
1992
2050
1993 if not haspatch:
2051 if not haspatch:
1994 raise util.Abort(_('no diffs found'))
2052 raise util.Abort(_('no diffs found'))
1995
2053
1996 finally:
2054 finally:
1997 release(lock, wlock)
2055 release(lock, wlock)
1998
2056
1999 def incoming(ui, repo, source="default", **opts):
2057 def incoming(ui, repo, source="default", **opts):
2000 """show new changesets found in source
2058 """show new changesets found in source
2001
2059
2002 Show new changesets found in the specified path/URL or the default
2060 Show new changesets found in the specified path/URL or the default
2003 pull location. These are the changesets that would have been pulled
2061 pull location. These are the changesets that would have been pulled
2004 if a pull at the time you issued this command.
2062 if a pull at the time you issued this command.
2005
2063
2006 For remote repository, using --bundle avoids downloading the
2064 For remote repository, using --bundle avoids downloading the
2007 changesets twice if the incoming is followed by a pull.
2065 changesets twice if the incoming is followed by a pull.
2008
2066
2009 See pull for valid source format details.
2067 See pull for valid source format details.
2068
2069 Returns 0 if there are incoming changes, 1 otherwise.
2010 """
2070 """
2011 limit = cmdutil.loglimit(opts)
2071 limit = cmdutil.loglimit(opts)
2012 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2072 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2013 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2073 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2014 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2074 ui.status(_('comparing with %s\n') % url.hidepassword(source))
2015 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2075 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2016 if revs:
2076 if revs:
2017 revs = [other.lookup(rev) for rev in revs]
2077 revs = [other.lookup(rev) for rev in revs]
2018 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
2078 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
2019 force=opts["force"])
2079 force=opts["force"])
2020 if not incoming:
2080 if not incoming:
2021 try:
2081 try:
2022 os.unlink(opts["bundle"])
2082 os.unlink(opts["bundle"])
2023 except:
2083 except:
2024 pass
2084 pass
2025 ui.status(_("no changes found\n"))
2085 ui.status(_("no changes found\n"))
2026 return 1
2086 return 1
2027
2087
2028 cleanup = None
2088 cleanup = None
2029 try:
2089 try:
2030 fname = opts["bundle"]
2090 fname = opts["bundle"]
2031 if fname or not other.local():
2091 if fname or not other.local():
2032 # create a bundle (uncompressed if other repo is not local)
2092 # create a bundle (uncompressed if other repo is not local)
2033
2093
2034 if revs is None and other.capable('changegroupsubset'):
2094 if revs is None and other.capable('changegroupsubset'):
2035 revs = rheads
2095 revs = rheads
2036
2096
2037 if revs is None:
2097 if revs is None:
2038 cg = other.changegroup(incoming, "incoming")
2098 cg = other.changegroup(incoming, "incoming")
2039 else:
2099 else:
2040 cg = other.changegroupsubset(incoming, revs, 'incoming')
2100 cg = other.changegroupsubset(incoming, revs, 'incoming')
2041 bundletype = other.local() and "HG10BZ" or "HG10UN"
2101 bundletype = other.local() and "HG10BZ" or "HG10UN"
2042 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2102 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
2043 # keep written bundle?
2103 # keep written bundle?
2044 if opts["bundle"]:
2104 if opts["bundle"]:
2045 cleanup = None
2105 cleanup = None
2046 if not other.local():
2106 if not other.local():
2047 # use the created uncompressed bundlerepo
2107 # use the created uncompressed bundlerepo
2048 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2108 other = bundlerepo.bundlerepository(ui, repo.root, fname)
2049
2109
2050 o = other.changelog.nodesbetween(incoming, revs)[0]
2110 o = other.changelog.nodesbetween(incoming, revs)[0]
2051 if opts.get('newest_first'):
2111 if opts.get('newest_first'):
2052 o.reverse()
2112 o.reverse()
2053 displayer = cmdutil.show_changeset(ui, other, opts)
2113 displayer = cmdutil.show_changeset(ui, other, opts)
2054 count = 0
2114 count = 0
2055 for n in o:
2115 for n in o:
2056 if limit is not None and count >= limit:
2116 if limit is not None and count >= limit:
2057 break
2117 break
2058 parents = [p for p in other.changelog.parents(n) if p != nullid]
2118 parents = [p for p in other.changelog.parents(n) if p != nullid]
2059 if opts.get('no_merges') and len(parents) == 2:
2119 if opts.get('no_merges') and len(parents) == 2:
2060 continue
2120 continue
2061 count += 1
2121 count += 1
2062 displayer.show(other[n])
2122 displayer.show(other[n])
2063 displayer.close()
2123 displayer.close()
2064 finally:
2124 finally:
2065 if hasattr(other, 'close'):
2125 if hasattr(other, 'close'):
2066 other.close()
2126 other.close()
2067 if cleanup:
2127 if cleanup:
2068 os.unlink(cleanup)
2128 os.unlink(cleanup)
2069
2129
2070 def init(ui, dest=".", **opts):
2130 def init(ui, dest=".", **opts):
2071 """create a new repository in the given directory
2131 """create a new repository in the given directory
2072
2132
2073 Initialize a new repository in the given directory. If the given
2133 Initialize a new repository in the given directory. If the given
2074 directory does not exist, it will be created.
2134 directory does not exist, it will be created.
2075
2135
2076 If no directory is given, the current directory is used.
2136 If no directory is given, the current directory is used.
2077
2137
2078 It is possible to specify an ``ssh://`` URL as the destination.
2138 It is possible to specify an ``ssh://`` URL as the destination.
2079 See :hg:`help urls` for more information.
2139 See :hg:`help urls` for more information.
2140
2141 Returns 0 on success.
2080 """
2142 """
2081 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
2143 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
2082
2144
2083 def locate(ui, repo, *pats, **opts):
2145 def locate(ui, repo, *pats, **opts):
2084 """locate files matching specific patterns
2146 """locate files matching specific patterns
2085
2147
2086 Print files under Mercurial control in the working directory whose
2148 Print files under Mercurial control in the working directory whose
2087 names match the given patterns.
2149 names match the given patterns.
2088
2150
2089 By default, this command searches all directories in the working
2151 By default, this command searches all directories in the working
2090 directory. To search just the current directory and its
2152 directory. To search just the current directory and its
2091 subdirectories, use "--include .".
2153 subdirectories, use "--include .".
2092
2154
2093 If no patterns are given to match, this command prints the names
2155 If no patterns are given to match, this command prints the names
2094 of all files under Mercurial control in the working directory.
2156 of all files under Mercurial control in the working directory.
2095
2157
2096 If you want to feed the output of this command into the "xargs"
2158 If you want to feed the output of this command into the "xargs"
2097 command, use the -0 option to both this command and "xargs". This
2159 command, use the -0 option to both this command and "xargs". This
2098 will avoid the problem of "xargs" treating single filenames that
2160 will avoid the problem of "xargs" treating single filenames that
2099 contain whitespace as multiple filenames.
2161 contain whitespace as multiple filenames.
2162
2163 Returns 0 if a match is found, 1 otherwise.
2100 """
2164 """
2101 end = opts.get('print0') and '\0' or '\n'
2165 end = opts.get('print0') and '\0' or '\n'
2102 rev = opts.get('rev') or None
2166 rev = opts.get('rev') or None
2103
2167
2104 ret = 1
2168 ret = 1
2105 m = cmdutil.match(repo, pats, opts, default='relglob')
2169 m = cmdutil.match(repo, pats, opts, default='relglob')
2106 m.bad = lambda x, y: False
2170 m.bad = lambda x, y: False
2107 for abs in repo[rev].walk(m):
2171 for abs in repo[rev].walk(m):
2108 if not rev and abs not in repo.dirstate:
2172 if not rev and abs not in repo.dirstate:
2109 continue
2173 continue
2110 if opts.get('fullpath'):
2174 if opts.get('fullpath'):
2111 ui.write(repo.wjoin(abs), end)
2175 ui.write(repo.wjoin(abs), end)
2112 else:
2176 else:
2113 ui.write(((pats and m.rel(abs)) or abs), end)
2177 ui.write(((pats and m.rel(abs)) or abs), end)
2114 ret = 0
2178 ret = 0
2115
2179
2116 return ret
2180 return ret
2117
2181
2118 def log(ui, repo, *pats, **opts):
2182 def log(ui, repo, *pats, **opts):
2119 """show revision history of entire repository or files
2183 """show revision history of entire repository or files
2120
2184
2121 Print the revision history of the specified files or the entire
2185 Print the revision history of the specified files or the entire
2122 project.
2186 project.
2123
2187
2124 File history is shown without following rename or copy history of
2188 File history is shown without following rename or copy history of
2125 files. Use -f/--follow with a filename to follow history across
2189 files. Use -f/--follow with a filename to follow history across
2126 renames and copies. --follow without a filename will only show
2190 renames and copies. --follow without a filename will only show
2127 ancestors or descendants of the starting revision. --follow-first
2191 ancestors or descendants of the starting revision. --follow-first
2128 only follows the first parent of merge revisions.
2192 only follows the first parent of merge revisions.
2129
2193
2130 If no revision range is specified, the default is tip:0 unless
2194 If no revision range is specified, the default is tip:0 unless
2131 --follow is set, in which case the working directory parent is
2195 --follow is set, in which case the working directory parent is
2132 used as the starting revision.
2196 used as the starting revision.
2133
2197
2134 See :hg:`help dates` for a list of formats valid for -d/--date.
2198 See :hg:`help dates` for a list of formats valid for -d/--date.
2135
2199
2136 By default this command prints revision number and changeset id,
2200 By default this command prints revision number and changeset id,
2137 tags, non-trivial parents, user, date and time, and a summary for
2201 tags, non-trivial parents, user, date and time, and a summary for
2138 each commit. When the -v/--verbose switch is used, the list of
2202 each commit. When the -v/--verbose switch is used, the list of
2139 changed files and full commit message are shown.
2203 changed files and full commit message are shown.
2140
2204
2141 NOTE: log -p/--patch may generate unexpected diff output for merge
2205 NOTE: log -p/--patch may generate unexpected diff output for merge
2142 changesets, as it will only compare the merge changeset against
2206 changesets, as it will only compare the merge changeset against
2143 its first parent. Also, only files different from BOTH parents
2207 its first parent. Also, only files different from BOTH parents
2144 will appear in files:.
2208 will appear in files:.
2209
2210 Returns 0 on success.
2145 """
2211 """
2146
2212
2147 matchfn = cmdutil.match(repo, pats, opts)
2213 matchfn = cmdutil.match(repo, pats, opts)
2148 limit = cmdutil.loglimit(opts)
2214 limit = cmdutil.loglimit(opts)
2149 count = 0
2215 count = 0
2150
2216
2151 endrev = None
2217 endrev = None
2152 if opts.get('copies') and opts.get('rev'):
2218 if opts.get('copies') and opts.get('rev'):
2153 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2219 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2154
2220
2155 df = False
2221 df = False
2156 if opts["date"]:
2222 if opts["date"]:
2157 df = util.matchdate(opts["date"])
2223 df = util.matchdate(opts["date"])
2158
2224
2159 branches = opts.get('branch', []) + opts.get('only_branch', [])
2225 branches = opts.get('branch', []) + opts.get('only_branch', [])
2160 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2226 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2161
2227
2162 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2228 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2163 def prep(ctx, fns):
2229 def prep(ctx, fns):
2164 rev = ctx.rev()
2230 rev = ctx.rev()
2165 parents = [p for p in repo.changelog.parentrevs(rev)
2231 parents = [p for p in repo.changelog.parentrevs(rev)
2166 if p != nullrev]
2232 if p != nullrev]
2167 if opts.get('no_merges') and len(parents) == 2:
2233 if opts.get('no_merges') and len(parents) == 2:
2168 return
2234 return
2169 if opts.get('only_merges') and len(parents) != 2:
2235 if opts.get('only_merges') and len(parents) != 2:
2170 return
2236 return
2171 if opts.get('branch') and ctx.branch() not in opts['branch']:
2237 if opts.get('branch') and ctx.branch() not in opts['branch']:
2172 return
2238 return
2173 if df and not df(ctx.date()[0]):
2239 if df and not df(ctx.date()[0]):
2174 return
2240 return
2175 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2241 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2176 return
2242 return
2177 if opts.get('keyword'):
2243 if opts.get('keyword'):
2178 for k in [kw.lower() for kw in opts['keyword']]:
2244 for k in [kw.lower() for kw in opts['keyword']]:
2179 if (k in ctx.user().lower() or
2245 if (k in ctx.user().lower() or
2180 k in ctx.description().lower() or
2246 k in ctx.description().lower() or
2181 k in " ".join(ctx.files()).lower()):
2247 k in " ".join(ctx.files()).lower()):
2182 break
2248 break
2183 else:
2249 else:
2184 return
2250 return
2185
2251
2186 copies = None
2252 copies = None
2187 if opts.get('copies') and rev:
2253 if opts.get('copies') and rev:
2188 copies = []
2254 copies = []
2189 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2255 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2190 for fn in ctx.files():
2256 for fn in ctx.files():
2191 rename = getrenamed(fn, rev)
2257 rename = getrenamed(fn, rev)
2192 if rename:
2258 if rename:
2193 copies.append((fn, rename[0]))
2259 copies.append((fn, rename[0]))
2194
2260
2195 displayer.show(ctx, copies=copies)
2261 displayer.show(ctx, copies=copies)
2196
2262
2197 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2263 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2198 if count == limit:
2264 if count == limit:
2199 break
2265 break
2200 if displayer.flush(ctx.rev()):
2266 if displayer.flush(ctx.rev()):
2201 count += 1
2267 count += 1
2202 displayer.close()
2268 displayer.close()
2203
2269
2204 def manifest(ui, repo, node=None, rev=None):
2270 def manifest(ui, repo, node=None, rev=None):
2205 """output the current or given revision of the project manifest
2271 """output the current or given revision of the project manifest
2206
2272
2207 Print a list of version controlled files for the given revision.
2273 Print a list of version controlled files for the given revision.
2208 If no revision is given, the first parent of the working directory
2274 If no revision is given, the first parent of the working directory
2209 is used, or the null revision if no revision is checked out.
2275 is used, or the null revision if no revision is checked out.
2210
2276
2211 With -v, print file permissions, symlink and executable bits.
2277 With -v, print file permissions, symlink and executable bits.
2212 With --debug, print file revision hashes.
2278 With --debug, print file revision hashes.
2279
2280 Returns 0 on success.
2213 """
2281 """
2214
2282
2215 if rev and node:
2283 if rev and node:
2216 raise util.Abort(_("please specify just one revision"))
2284 raise util.Abort(_("please specify just one revision"))
2217
2285
2218 if not node:
2286 if not node:
2219 node = rev
2287 node = rev
2220
2288
2221 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2289 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2222 ctx = repo[node]
2290 ctx = repo[node]
2223 for f in ctx:
2291 for f in ctx:
2224 if ui.debugflag:
2292 if ui.debugflag:
2225 ui.write("%40s " % hex(ctx.manifest()[f]))
2293 ui.write("%40s " % hex(ctx.manifest()[f]))
2226 if ui.verbose:
2294 if ui.verbose:
2227 ui.write(decor[ctx.flags(f)])
2295 ui.write(decor[ctx.flags(f)])
2228 ui.write("%s\n" % f)
2296 ui.write("%s\n" % f)
2229
2297
2230 def merge(ui, repo, node=None, **opts):
2298 def merge(ui, repo, node=None, **opts):
2231 """merge working directory with another revision
2299 """merge working directory with another revision
2232
2300
2233 The current working directory is updated with all changes made in
2301 The current working directory is updated with all changes made in
2234 the requested revision since the last common predecessor revision.
2302 the requested revision since the last common predecessor revision.
2235
2303
2236 Files that changed between either parent are marked as changed for
2304 Files that changed between either parent are marked as changed for
2237 the next commit and a commit must be performed before any further
2305 the next commit and a commit must be performed before any further
2238 updates to the repository are allowed. The next commit will have
2306 updates to the repository are allowed. The next commit will have
2239 two parents.
2307 two parents.
2240
2308
2241 If no revision is specified, the working directory's parent is a
2309 If no revision is specified, the working directory's parent is a
2242 head revision, and the current branch contains exactly one other
2310 head revision, and the current branch contains exactly one other
2243 head, the other head is merged with by default. Otherwise, an
2311 head, the other head is merged with by default. Otherwise, an
2244 explicit revision with which to merge with must be provided.
2312 explicit revision with which to merge with must be provided.
2313
2314 Returns 0 on success, 1 if there are unresolved files.
2245 """
2315 """
2246
2316
2247 if opts.get('rev') and node:
2317 if opts.get('rev') and node:
2248 raise util.Abort(_("please specify just one revision"))
2318 raise util.Abort(_("please specify just one revision"))
2249 if not node:
2319 if not node:
2250 node = opts.get('rev')
2320 node = opts.get('rev')
2251
2321
2252 if not node:
2322 if not node:
2253 branch = repo.changectx(None).branch()
2323 branch = repo.changectx(None).branch()
2254 bheads = repo.branchheads(branch)
2324 bheads = repo.branchheads(branch)
2255 if len(bheads) > 2:
2325 if len(bheads) > 2:
2256 ui.warn(_("abort: branch '%s' has %d heads - "
2326 ui.warn(_("abort: branch '%s' has %d heads - "
2257 "please merge with an explicit rev\n")
2327 "please merge with an explicit rev\n")
2258 % (branch, len(bheads)))
2328 % (branch, len(bheads)))
2259 ui.status(_("(run 'hg heads .' to see heads)\n"))
2329 ui.status(_("(run 'hg heads .' to see heads)\n"))
2260 return False
2330 return False
2261
2331
2262 parent = repo.dirstate.parents()[0]
2332 parent = repo.dirstate.parents()[0]
2263 if len(bheads) == 1:
2333 if len(bheads) == 1:
2264 if len(repo.heads()) > 1:
2334 if len(repo.heads()) > 1:
2265 ui.warn(_("abort: branch '%s' has one head - "
2335 ui.warn(_("abort: branch '%s' has one head - "
2266 "please merge with an explicit rev\n" % branch))
2336 "please merge with an explicit rev\n" % branch))
2267 ui.status(_("(run 'hg heads' to see all heads)\n"))
2337 ui.status(_("(run 'hg heads' to see all heads)\n"))
2268 return False
2338 return False
2269 msg = _('there is nothing to merge')
2339 msg = _('there is nothing to merge')
2270 if parent != repo.lookup(repo[None].branch()):
2340 if parent != repo.lookup(repo[None].branch()):
2271 msg = _('%s - use "hg update" instead') % msg
2341 msg = _('%s - use "hg update" instead') % msg
2272 raise util.Abort(msg)
2342 raise util.Abort(msg)
2273
2343
2274 if parent not in bheads:
2344 if parent not in bheads:
2275 raise util.Abort(_('working dir not at a head rev - '
2345 raise util.Abort(_('working dir not at a head rev - '
2276 'use "hg update" or merge with an explicit rev'))
2346 'use "hg update" or merge with an explicit rev'))
2277 node = parent == bheads[0] and bheads[-1] or bheads[0]
2347 node = parent == bheads[0] and bheads[-1] or bheads[0]
2278
2348
2279 if opts.get('preview'):
2349 if opts.get('preview'):
2280 # find nodes that are ancestors of p2 but not of p1
2350 # find nodes that are ancestors of p2 but not of p1
2281 p1 = repo.lookup('.')
2351 p1 = repo.lookup('.')
2282 p2 = repo.lookup(node)
2352 p2 = repo.lookup(node)
2283 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2353 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2284
2354
2285 displayer = cmdutil.show_changeset(ui, repo, opts)
2355 displayer = cmdutil.show_changeset(ui, repo, opts)
2286 for node in nodes:
2356 for node in nodes:
2287 displayer.show(repo[node])
2357 displayer.show(repo[node])
2288 displayer.close()
2358 displayer.close()
2289 return 0
2359 return 0
2290
2360
2291 return hg.merge(repo, node, force=opts.get('force'))
2361 return hg.merge(repo, node, force=opts.get('force'))
2292
2362
2293 def outgoing(ui, repo, dest=None, **opts):
2363 def outgoing(ui, repo, dest=None, **opts):
2294 """show changesets not found in the destination
2364 """show changesets not found in the destination
2295
2365
2296 Show changesets not found in the specified destination repository
2366 Show changesets not found in the specified destination repository
2297 or the default push location. These are the changesets that would
2367 or the default push location. These are the changesets that would
2298 be pushed if a push was requested.
2368 be pushed if a push was requested.
2299
2369
2300 See pull for details of valid destination formats.
2370 See pull for details of valid destination formats.
2371
2372 Returns 0 if there are outgoing changes, 1 otherwise.
2301 """
2373 """
2302 limit = cmdutil.loglimit(opts)
2374 limit = cmdutil.loglimit(opts)
2303 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2375 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2304 dest, branches = hg.parseurl(dest, opts.get('branch'))
2376 dest, branches = hg.parseurl(dest, opts.get('branch'))
2305 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2377 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2306 if revs:
2378 if revs:
2307 revs = [repo.lookup(rev) for rev in revs]
2379 revs = [repo.lookup(rev) for rev in revs]
2308
2380
2309 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2381 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2310 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2382 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2311 o = repo.findoutgoing(other, force=opts.get('force'))
2383 o = repo.findoutgoing(other, force=opts.get('force'))
2312 if not o:
2384 if not o:
2313 ui.status(_("no changes found\n"))
2385 ui.status(_("no changes found\n"))
2314 return 1
2386 return 1
2315 o = repo.changelog.nodesbetween(o, revs)[0]
2387 o = repo.changelog.nodesbetween(o, revs)[0]
2316 if opts.get('newest_first'):
2388 if opts.get('newest_first'):
2317 o.reverse()
2389 o.reverse()
2318 displayer = cmdutil.show_changeset(ui, repo, opts)
2390 displayer = cmdutil.show_changeset(ui, repo, opts)
2319 count = 0
2391 count = 0
2320 for n in o:
2392 for n in o:
2321 if limit is not None and count >= limit:
2393 if limit is not None and count >= limit:
2322 break
2394 break
2323 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2395 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2324 if opts.get('no_merges') and len(parents) == 2:
2396 if opts.get('no_merges') and len(parents) == 2:
2325 continue
2397 continue
2326 count += 1
2398 count += 1
2327 displayer.show(repo[n])
2399 displayer.show(repo[n])
2328 displayer.close()
2400 displayer.close()
2329
2401
2330 def parents(ui, repo, file_=None, **opts):
2402 def parents(ui, repo, file_=None, **opts):
2331 """show the parents of the working directory or revision
2403 """show the parents of the working directory or revision
2332
2404
2333 Print the working directory's parent revisions. If a revision is
2405 Print the working directory's parent revisions. If a revision is
2334 given via -r/--rev, the parent of that revision will be printed.
2406 given via -r/--rev, the parent of that revision will be printed.
2335 If a file argument is given, the revision in which the file was
2407 If a file argument is given, the revision in which the file was
2336 last changed (before the working directory revision or the
2408 last changed (before the working directory revision or the
2337 argument to --rev if given) is printed.
2409 argument to --rev if given) is printed.
2410
2411 Returns 0 on success.
2338 """
2412 """
2339 rev = opts.get('rev')
2413 rev = opts.get('rev')
2340 if rev:
2414 if rev:
2341 ctx = repo[rev]
2415 ctx = repo[rev]
2342 else:
2416 else:
2343 ctx = repo[None]
2417 ctx = repo[None]
2344
2418
2345 if file_:
2419 if file_:
2346 m = cmdutil.match(repo, (file_,), opts)
2420 m = cmdutil.match(repo, (file_,), opts)
2347 if m.anypats() or len(m.files()) != 1:
2421 if m.anypats() or len(m.files()) != 1:
2348 raise util.Abort(_('can only specify an explicit filename'))
2422 raise util.Abort(_('can only specify an explicit filename'))
2349 file_ = m.files()[0]
2423 file_ = m.files()[0]
2350 filenodes = []
2424 filenodes = []
2351 for cp in ctx.parents():
2425 for cp in ctx.parents():
2352 if not cp:
2426 if not cp:
2353 continue
2427 continue
2354 try:
2428 try:
2355 filenodes.append(cp.filenode(file_))
2429 filenodes.append(cp.filenode(file_))
2356 except error.LookupError:
2430 except error.LookupError:
2357 pass
2431 pass
2358 if not filenodes:
2432 if not filenodes:
2359 raise util.Abort(_("'%s' not found in manifest!") % file_)
2433 raise util.Abort(_("'%s' not found in manifest!") % file_)
2360 fl = repo.file(file_)
2434 fl = repo.file(file_)
2361 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2435 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2362 else:
2436 else:
2363 p = [cp.node() for cp in ctx.parents()]
2437 p = [cp.node() for cp in ctx.parents()]
2364
2438
2365 displayer = cmdutil.show_changeset(ui, repo, opts)
2439 displayer = cmdutil.show_changeset(ui, repo, opts)
2366 for n in p:
2440 for n in p:
2367 if n != nullid:
2441 if n != nullid:
2368 displayer.show(repo[n])
2442 displayer.show(repo[n])
2369 displayer.close()
2443 displayer.close()
2370
2444
2371 def paths(ui, repo, search=None):
2445 def paths(ui, repo, search=None):
2372 """show aliases for remote repositories
2446 """show aliases for remote repositories
2373
2447
2374 Show definition of symbolic path name NAME. If no name is given,
2448 Show definition of symbolic path name NAME. If no name is given,
2375 show definition of all available names.
2449 show definition of all available names.
2376
2450
2377 Path names are defined in the [paths] section of
2451 Path names are defined in the [paths] section of
2378 ``/etc/mercurial/hgrc`` and ``$HOME/.hgrc``. If run inside a
2452 ``/etc/mercurial/hgrc`` and ``$HOME/.hgrc``. If run inside a
2379 repository, ``.hg/hgrc`` is used, too.
2453 repository, ``.hg/hgrc`` is used, too.
2380
2454
2381 The path names ``default`` and ``default-push`` have a special
2455 The path names ``default`` and ``default-push`` have a special
2382 meaning. When performing a push or pull operation, they are used
2456 meaning. When performing a push or pull operation, they are used
2383 as fallbacks if no location is specified on the command-line.
2457 as fallbacks if no location is specified on the command-line.
2384 When ``default-push`` is set, it will be used for push and
2458 When ``default-push`` is set, it will be used for push and
2385 ``default`` will be used for pull; otherwise ``default`` is used
2459 ``default`` will be used for pull; otherwise ``default`` is used
2386 as the fallback for both. When cloning a repository, the clone
2460 as the fallback for both. When cloning a repository, the clone
2387 source is written as ``default`` in ``.hg/hgrc``. Note that
2461 source is written as ``default`` in ``.hg/hgrc``. Note that
2388 ``default`` and ``default-push`` apply to all inbound (e.g.
2462 ``default`` and ``default-push`` apply to all inbound (e.g.
2389 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2463 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2390 :hg:`bundle`) operations.
2464 :hg:`bundle`) operations.
2391
2465
2392 See :hg:`help urls` for more information.
2466 See :hg:`help urls` for more information.
2393 """
2467 """
2394 if search:
2468 if search:
2395 for name, path in ui.configitems("paths"):
2469 for name, path in ui.configitems("paths"):
2396 if name == search:
2470 if name == search:
2397 ui.write("%s\n" % url.hidepassword(path))
2471 ui.write("%s\n" % url.hidepassword(path))
2398 return
2472 return
2399 ui.warn(_("not found!\n"))
2473 ui.warn(_("not found!\n"))
2400 return 1
2474 return 1
2401 else:
2475 else:
2402 for name, path in ui.configitems("paths"):
2476 for name, path in ui.configitems("paths"):
2403 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2477 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2404
2478
2405 def postincoming(ui, repo, modheads, optupdate, checkout):
2479 def postincoming(ui, repo, modheads, optupdate, checkout):
2406 if modheads == 0:
2480 if modheads == 0:
2407 return
2481 return
2408 if optupdate:
2482 if optupdate:
2409 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2483 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2410 return hg.update(repo, checkout)
2484 return hg.update(repo, checkout)
2411 else:
2485 else:
2412 ui.status(_("not updating, since new heads added\n"))
2486 ui.status(_("not updating, since new heads added\n"))
2413 if modheads > 1:
2487 if modheads > 1:
2414 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2488 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2415 else:
2489 else:
2416 ui.status(_("(run 'hg update' to get a working copy)\n"))
2490 ui.status(_("(run 'hg update' to get a working copy)\n"))
2417
2491
2418 def pull(ui, repo, source="default", **opts):
2492 def pull(ui, repo, source="default", **opts):
2419 """pull changes from the specified source
2493 """pull changes from the specified source
2420
2494
2421 Pull changes from a remote repository to a local one.
2495 Pull changes from a remote repository to a local one.
2422
2496
2423 This finds all changes from the repository at the specified path
2497 This finds all changes from the repository at the specified path
2424 or URL and adds them to a local repository (the current one unless
2498 or URL and adds them to a local repository (the current one unless
2425 -R is specified). By default, this does not update the copy of the
2499 -R is specified). By default, this does not update the copy of the
2426 project in the working directory.
2500 project in the working directory.
2427
2501
2428 Use hg incoming if you want to see what would have been added by a
2502 Use hg incoming if you want to see what would have been added by a
2429 pull at the time you issued this command. If you then decide to
2503 pull at the time you issued this command. If you then decide to
2430 added those changes to the repository, you should use pull -r X
2504 added those changes to the repository, you should use pull -r X
2431 where X is the last changeset listed by hg incoming.
2505 where X is the last changeset listed by hg incoming.
2432
2506
2433 If SOURCE is omitted, the 'default' path will be used.
2507 If SOURCE is omitted, the 'default' path will be used.
2434 See :hg:`help urls` for more information.
2508 See :hg:`help urls` for more information.
2509
2510 Returns 0 on success, 1 if an update had unresolved files.
2435 """
2511 """
2436 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2512 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2437 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2513 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2438 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2514 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2439 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2515 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2440 if revs:
2516 if revs:
2441 try:
2517 try:
2442 revs = [other.lookup(rev) for rev in revs]
2518 revs = [other.lookup(rev) for rev in revs]
2443 except error.CapabilityError:
2519 except error.CapabilityError:
2444 err = _("Other repository doesn't support revision lookup, "
2520 err = _("Other repository doesn't support revision lookup, "
2445 "so a rev cannot be specified.")
2521 "so a rev cannot be specified.")
2446 raise util.Abort(err)
2522 raise util.Abort(err)
2447
2523
2448 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2524 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2449 if checkout:
2525 if checkout:
2450 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2526 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2451 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2527 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2452
2528
2453 def push(ui, repo, dest=None, **opts):
2529 def push(ui, repo, dest=None, **opts):
2454 """push changes to the specified destination
2530 """push changes to the specified destination
2455
2531
2456 Push changes from the local repository to the specified destination.
2532 Push changes from the local repository to the specified destination.
2457
2533
2458 This is the symmetrical operation for pull. It moves changes from
2534 This is the symmetrical operation for pull. It moves changes from
2459 the current repository to a different one. If the destination is
2535 the current repository to a different one. If the destination is
2460 local this is identical to a pull in that directory from the
2536 local this is identical to a pull in that directory from the
2461 current one.
2537 current one.
2462
2538
2463 By default, push will refuse to run if it detects the result would
2539 By default, push will refuse to run if it detects the result would
2464 increase the number of remote heads. This generally indicates the
2540 increase the number of remote heads. This generally indicates the
2465 user forgot to pull and merge before pushing.
2541 user forgot to pull and merge before pushing.
2466
2542
2467 If -r/--rev is used, the named revision and all its ancestors will
2543 If -r/--rev is used, the named revision and all its ancestors will
2468 be pushed to the remote repository.
2544 be pushed to the remote repository.
2469
2545
2470 Please see :hg:`help urls` for important details about ``ssh://``
2546 Please see :hg:`help urls` for important details about ``ssh://``
2471 URLs. If DESTINATION is omitted, a default path will be used.
2547 URLs. If DESTINATION is omitted, a default path will be used.
2548
2549 Returns 0 if push was successful, 1 if nothing to push.
2472 """
2550 """
2473 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2551 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2474 dest, branches = hg.parseurl(dest, opts.get('branch'))
2552 dest, branches = hg.parseurl(dest, opts.get('branch'))
2475 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2553 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2476 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2554 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2477 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2555 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2478 if revs:
2556 if revs:
2479 revs = [repo.lookup(rev) for rev in revs]
2557 revs = [repo.lookup(rev) for rev in revs]
2480
2558
2481 # push subrepos depth-first for coherent ordering
2559 # push subrepos depth-first for coherent ordering
2482 c = repo['']
2560 c = repo['']
2483 subs = c.substate # only repos that are committed
2561 subs = c.substate # only repos that are committed
2484 for s in sorted(subs):
2562 for s in sorted(subs):
2485 if not c.sub(s).push(opts.get('force')):
2563 if not c.sub(s).push(opts.get('force')):
2486 return False
2564 return False
2487
2565
2488 r = repo.push(other, opts.get('force'), revs=revs)
2566 r = repo.push(other, opts.get('force'), revs=revs)
2489 return r == 0
2567 return r == 0
2490
2568
2491 def recover(ui, repo):
2569 def recover(ui, repo):
2492 """roll back an interrupted transaction
2570 """roll back an interrupted transaction
2493
2571
2494 Recover from an interrupted commit or pull.
2572 Recover from an interrupted commit or pull.
2495
2573
2496 This command tries to fix the repository status after an
2574 This command tries to fix the repository status after an
2497 interrupted operation. It should only be necessary when Mercurial
2575 interrupted operation. It should only be necessary when Mercurial
2498 suggests it.
2576 suggests it.
2577
2578 Returns 0 if successful, 1 if nothing to recover or verify fails.
2499 """
2579 """
2500 if repo.recover():
2580 if repo.recover():
2501 return hg.verify(repo)
2581 return hg.verify(repo)
2502 return 1
2582 return 1
2503
2583
2504 def remove(ui, repo, *pats, **opts):
2584 def remove(ui, repo, *pats, **opts):
2505 """remove the specified files on the next commit
2585 """remove the specified files on the next commit
2506
2586
2507 Schedule the indicated files for removal from the repository.
2587 Schedule the indicated files for removal from the repository.
2508
2588
2509 This only removes files from the current branch, not from the
2589 This only removes files from the current branch, not from the
2510 entire project history. -A/--after can be used to remove only
2590 entire project history. -A/--after can be used to remove only
2511 files that have already been deleted, -f/--force can be used to
2591 files that have already been deleted, -f/--force can be used to
2512 force deletion, and -Af can be used to remove files from the next
2592 force deletion, and -Af can be used to remove files from the next
2513 revision without deleting them from the working directory.
2593 revision without deleting them from the working directory.
2514
2594
2515 The following table details the behavior of remove for different
2595 The following table details the behavior of remove for different
2516 file states (columns) and option combinations (rows). The file
2596 file states (columns) and option combinations (rows). The file
2517 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2597 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2518 reported by hg status). The actions are Warn, Remove (from branch)
2598 reported by hg status). The actions are Warn, Remove (from branch)
2519 and Delete (from disk)::
2599 and Delete (from disk)::
2520
2600
2521 A C M !
2601 A C M !
2522 none W RD W R
2602 none W RD W R
2523 -f R RD RD R
2603 -f R RD RD R
2524 -A W W W R
2604 -A W W W R
2525 -Af R R R R
2605 -Af R R R R
2526
2606
2527 This command schedules the files to be removed at the next commit.
2607 This command schedules the files to be removed at the next commit.
2528 To undo a remove before that, see hg revert.
2608 To undo a remove before that, see hg revert.
2609
2610 Returns 0 on success, 1 if any warnings encountered.
2529 """
2611 """
2530
2612
2613 ret = 0
2531 after, force = opts.get('after'), opts.get('force')
2614 after, force = opts.get('after'), opts.get('force')
2532 if not pats and not after:
2615 if not pats and not after:
2533 raise util.Abort(_('no files specified'))
2616 raise util.Abort(_('no files specified'))
2534
2617
2535 m = cmdutil.match(repo, pats, opts)
2618 m = cmdutil.match(repo, pats, opts)
2536 s = repo.status(match=m, clean=True)
2619 s = repo.status(match=m, clean=True)
2537 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2620 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2538
2621
2539 for f in m.files():
2622 for f in m.files():
2540 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2623 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2541 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2624 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2625 ret = 1
2542
2626
2543 def warn(files, reason):
2627 def warn(files, reason):
2544 for f in files:
2628 for f in files:
2545 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2629 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2546 % (m.rel(f), reason))
2630 % (m.rel(f), reason))
2631 ret = 1
2547
2632
2548 if force:
2633 if force:
2549 remove, forget = modified + deleted + clean, added
2634 remove, forget = modified + deleted + clean, added
2550 elif after:
2635 elif after:
2551 remove, forget = deleted, []
2636 remove, forget = deleted, []
2552 warn(modified + added + clean, _('still exists'))
2637 warn(modified + added + clean, _('still exists'))
2553 else:
2638 else:
2554 remove, forget = deleted + clean, []
2639 remove, forget = deleted + clean, []
2555 warn(modified, _('is modified'))
2640 warn(modified, _('is modified'))
2556 warn(added, _('has been marked for add'))
2641 warn(added, _('has been marked for add'))
2557
2642
2558 for f in sorted(remove + forget):
2643 for f in sorted(remove + forget):
2559 if ui.verbose or not m.exact(f):
2644 if ui.verbose or not m.exact(f):
2560 ui.status(_('removing %s\n') % m.rel(f))
2645 ui.status(_('removing %s\n') % m.rel(f))
2561
2646
2562 repo.forget(forget)
2647 repo.forget(forget)
2563 repo.remove(remove, unlink=not after)
2648 repo.remove(remove, unlink=not after)
2649 return ret
2564
2650
2565 def rename(ui, repo, *pats, **opts):
2651 def rename(ui, repo, *pats, **opts):
2566 """rename files; equivalent of copy + remove
2652 """rename files; equivalent of copy + remove
2567
2653
2568 Mark dest as copies of sources; mark sources for deletion. If dest
2654 Mark dest as copies of sources; mark sources for deletion. If dest
2569 is a directory, copies are put in that directory. If dest is a
2655 is a directory, copies are put in that directory. If dest is a
2570 file, there can only be one source.
2656 file, there can only be one source.
2571
2657
2572 By default, this command copies the contents of files as they
2658 By default, this command copies the contents of files as they
2573 exist in the working directory. If invoked with -A/--after, the
2659 exist in the working directory. If invoked with -A/--after, the
2574 operation is recorded, but no copying is performed.
2660 operation is recorded, but no copying is performed.
2575
2661
2576 This command takes effect at the next commit. To undo a rename
2662 This command takes effect at the next commit. To undo a rename
2577 before that, see hg revert.
2663 before that, see hg revert.
2664
2665 Returns 0 on success, 1 if errors are encountered.
2578 """
2666 """
2579 wlock = repo.wlock(False)
2667 wlock = repo.wlock(False)
2580 try:
2668 try:
2581 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2669 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2582 finally:
2670 finally:
2583 wlock.release()
2671 wlock.release()
2584
2672
2585 def resolve(ui, repo, *pats, **opts):
2673 def resolve(ui, repo, *pats, **opts):
2586 """various operations to help finish a merge
2674 """various operations to help finish a merge
2587
2675
2588 This command includes several actions that are often useful while
2676 This command includes several actions that are often useful while
2589 performing a merge, after running ``merge`` but before running
2677 performing a merge, after running ``merge`` but before running
2590 ``commit``. (It is only meaningful if your working directory has
2678 ``commit``. (It is only meaningful if your working directory has
2591 two parents.) It is most relevant for merges with unresolved
2679 two parents.) It is most relevant for merges with unresolved
2592 conflicts, which are typically a result of non-interactive merging with
2680 conflicts, which are typically a result of non-interactive merging with
2593 ``internal:merge`` or a command-line merge tool like ``diff3``.
2681 ``internal:merge`` or a command-line merge tool like ``diff3``.
2594
2682
2595 The available actions are:
2683 The available actions are:
2596
2684
2597 1) list files that were merged with conflicts (U, for unresolved)
2685 1) list files that were merged with conflicts (U, for unresolved)
2598 and without conflicts (R, for resolved): ``hg resolve -l``
2686 and without conflicts (R, for resolved): ``hg resolve -l``
2599 (this is like ``status`` for merges)
2687 (this is like ``status`` for merges)
2600 2) record that you have resolved conflicts in certain files:
2688 2) record that you have resolved conflicts in certain files:
2601 ``hg resolve -m [file ...]`` (default: mark all unresolved files)
2689 ``hg resolve -m [file ...]`` (default: mark all unresolved files)
2602 3) forget that you have resolved conflicts in certain files:
2690 3) forget that you have resolved conflicts in certain files:
2603 ``hg resolve -u [file ...]`` (default: unmark all resolved files)
2691 ``hg resolve -u [file ...]`` (default: unmark all resolved files)
2604 4) discard your current attempt(s) at resolving conflicts and
2692 4) discard your current attempt(s) at resolving conflicts and
2605 restart the merge from scratch: ``hg resolve file...``
2693 restart the merge from scratch: ``hg resolve file...``
2606 (or ``-a`` for all unresolved files)
2694 (or ``-a`` for all unresolved files)
2607
2695
2608 Note that Mercurial will not let you commit files with unresolved merge
2696 Note that Mercurial will not let you commit files with unresolved merge
2609 conflicts. You must use ``hg resolve -m ...`` before you can commit
2697 conflicts. You must use ``hg resolve -m ...`` before you can commit
2610 after a conflicting merge.
2698 after a conflicting merge.
2699
2700 Returns 0 on success, 1 if any files fail a resolve attempt.
2611 """
2701 """
2612
2702
2613 all, mark, unmark, show, nostatus = \
2703 all, mark, unmark, show, nostatus = \
2614 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2704 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2615
2705
2616 if (show and (mark or unmark)) or (mark and unmark):
2706 if (show and (mark or unmark)) or (mark and unmark):
2617 raise util.Abort(_("too many options specified"))
2707 raise util.Abort(_("too many options specified"))
2618 if pats and all:
2708 if pats and all:
2619 raise util.Abort(_("can't specify --all and patterns"))
2709 raise util.Abort(_("can't specify --all and patterns"))
2620 if not (all or pats or show or mark or unmark):
2710 if not (all or pats or show or mark or unmark):
2621 raise util.Abort(_('no files or directories specified; '
2711 raise util.Abort(_('no files or directories specified; '
2622 'use --all to remerge all files'))
2712 'use --all to remerge all files'))
2623
2713
2624 ms = mergemod.mergestate(repo)
2714 ms = mergemod.mergestate(repo)
2625 m = cmdutil.match(repo, pats, opts)
2715 m = cmdutil.match(repo, pats, opts)
2716 ret = 0
2626
2717
2627 for f in ms:
2718 for f in ms:
2628 if m(f):
2719 if m(f):
2629 if show:
2720 if show:
2630 if nostatus:
2721 if nostatus:
2631 ui.write("%s\n" % f)
2722 ui.write("%s\n" % f)
2632 else:
2723 else:
2633 ui.write("%s %s\n" % (ms[f].upper(), f),
2724 ui.write("%s %s\n" % (ms[f].upper(), f),
2634 label='resolve.' +
2725 label='resolve.' +
2635 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
2726 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
2636 elif mark:
2727 elif mark:
2637 ms.mark(f, "r")
2728 ms.mark(f, "r")
2638 elif unmark:
2729 elif unmark:
2639 ms.mark(f, "u")
2730 ms.mark(f, "u")
2640 else:
2731 else:
2641 wctx = repo[None]
2732 wctx = repo[None]
2642 mctx = wctx.parents()[-1]
2733 mctx = wctx.parents()[-1]
2643
2734
2644 # backup pre-resolve (merge uses .orig for its own purposes)
2735 # backup pre-resolve (merge uses .orig for its own purposes)
2645 a = repo.wjoin(f)
2736 a = repo.wjoin(f)
2646 util.copyfile(a, a + ".resolve")
2737 util.copyfile(a, a + ".resolve")
2647
2738
2648 # resolve file
2739 # resolve file
2649 ms.resolve(f, wctx, mctx)
2740 if ms.resolve(f, wctx, mctx):
2741 ret = 1
2650
2742
2651 # replace filemerge's .orig file with our resolve file
2743 # replace filemerge's .orig file with our resolve file
2652 util.rename(a + ".resolve", a + ".orig")
2744 util.rename(a + ".resolve", a + ".orig")
2745 return ret
2653
2746
2654 def revert(ui, repo, *pats, **opts):
2747 def revert(ui, repo, *pats, **opts):
2655 """restore individual files or directories to an earlier state
2748 """restore individual files or directories to an earlier state
2656
2749
2657 (Use update -r to check out earlier revisions, revert does not
2750 (Use update -r to check out earlier revisions, revert does not
2658 change the working directory parents.)
2751 change the working directory parents.)
2659
2752
2660 With no revision specified, revert the named files or directories
2753 With no revision specified, revert the named files or directories
2661 to the contents they had in the parent of the working directory.
2754 to the contents they had in the parent of the working directory.
2662 This restores the contents of the affected files to an unmodified
2755 This restores the contents of the affected files to an unmodified
2663 state and unschedules adds, removes, copies, and renames. If the
2756 state and unschedules adds, removes, copies, and renames. If the
2664 working directory has two parents, you must explicitly specify a
2757 working directory has two parents, you must explicitly specify a
2665 revision.
2758 revision.
2666
2759
2667 Using the -r/--rev option, revert the given files or directories
2760 Using the -r/--rev option, revert the given files or directories
2668 to their contents as of a specific revision. This can be helpful
2761 to their contents as of a specific revision. This can be helpful
2669 to "roll back" some or all of an earlier change. See :hg:`help
2762 to "roll back" some or all of an earlier change. See :hg:`help
2670 dates` for a list of formats valid for -d/--date.
2763 dates` for a list of formats valid for -d/--date.
2671
2764
2672 Revert modifies the working directory. It does not commit any
2765 Revert modifies the working directory. It does not commit any
2673 changes, or change the parent of the working directory. If you
2766 changes, or change the parent of the working directory. If you
2674 revert to a revision other than the parent of the working
2767 revert to a revision other than the parent of the working
2675 directory, the reverted files will thus appear modified
2768 directory, the reverted files will thus appear modified
2676 afterwards.
2769 afterwards.
2677
2770
2678 If a file has been deleted, it is restored. If the executable mode
2771 If a file has been deleted, it is restored. If the executable mode
2679 of a file was changed, it is reset.
2772 of a file was changed, it is reset.
2680
2773
2681 If names are given, all files matching the names are reverted.
2774 If names are given, all files matching the names are reverted.
2682 If no arguments are given, no files are reverted.
2775 If no arguments are given, no files are reverted.
2683
2776
2684 Modified files are saved with a .orig suffix before reverting.
2777 Modified files are saved with a .orig suffix before reverting.
2685 To disable these backups, use --no-backup.
2778 To disable these backups, use --no-backup.
2779
2780 Returns 0 on success.
2686 """
2781 """
2687
2782
2688 if opts["date"]:
2783 if opts["date"]:
2689 if opts["rev"]:
2784 if opts["rev"]:
2690 raise util.Abort(_("you can't specify a revision and a date"))
2785 raise util.Abort(_("you can't specify a revision and a date"))
2691 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2786 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2692
2787
2693 if not pats and not opts.get('all'):
2788 if not pats and not opts.get('all'):
2694 raise util.Abort(_('no files or directories specified; '
2789 raise util.Abort(_('no files or directories specified; '
2695 'use --all to revert the whole repo'))
2790 'use --all to revert the whole repo'))
2696
2791
2697 parent, p2 = repo.dirstate.parents()
2792 parent, p2 = repo.dirstate.parents()
2698 if not opts.get('rev') and p2 != nullid:
2793 if not opts.get('rev') and p2 != nullid:
2699 raise util.Abort(_('uncommitted merge - please provide a '
2794 raise util.Abort(_('uncommitted merge - please provide a '
2700 'specific revision'))
2795 'specific revision'))
2701 ctx = repo[opts.get('rev')]
2796 ctx = repo[opts.get('rev')]
2702 node = ctx.node()
2797 node = ctx.node()
2703 mf = ctx.manifest()
2798 mf = ctx.manifest()
2704 if node == parent:
2799 if node == parent:
2705 pmf = mf
2800 pmf = mf
2706 else:
2801 else:
2707 pmf = None
2802 pmf = None
2708
2803
2709 # need all matching names in dirstate and manifest of target rev,
2804 # need all matching names in dirstate and manifest of target rev,
2710 # so have to walk both. do not print errors if files exist in one
2805 # so have to walk both. do not print errors if files exist in one
2711 # but not other.
2806 # but not other.
2712
2807
2713 names = {}
2808 names = {}
2714
2809
2715 wlock = repo.wlock()
2810 wlock = repo.wlock()
2716 try:
2811 try:
2717 # walk dirstate.
2812 # walk dirstate.
2718
2813
2719 m = cmdutil.match(repo, pats, opts)
2814 m = cmdutil.match(repo, pats, opts)
2720 m.bad = lambda x, y: False
2815 m.bad = lambda x, y: False
2721 for abs in repo.walk(m):
2816 for abs in repo.walk(m):
2722 names[abs] = m.rel(abs), m.exact(abs)
2817 names[abs] = m.rel(abs), m.exact(abs)
2723
2818
2724 # walk target manifest.
2819 # walk target manifest.
2725
2820
2726 def badfn(path, msg):
2821 def badfn(path, msg):
2727 if path in names:
2822 if path in names:
2728 return
2823 return
2729 path_ = path + '/'
2824 path_ = path + '/'
2730 for f in names:
2825 for f in names:
2731 if f.startswith(path_):
2826 if f.startswith(path_):
2732 return
2827 return
2733 ui.warn("%s: %s\n" % (m.rel(path), msg))
2828 ui.warn("%s: %s\n" % (m.rel(path), msg))
2734
2829
2735 m = cmdutil.match(repo, pats, opts)
2830 m = cmdutil.match(repo, pats, opts)
2736 m.bad = badfn
2831 m.bad = badfn
2737 for abs in repo[node].walk(m):
2832 for abs in repo[node].walk(m):
2738 if abs not in names:
2833 if abs not in names:
2739 names[abs] = m.rel(abs), m.exact(abs)
2834 names[abs] = m.rel(abs), m.exact(abs)
2740
2835
2741 m = cmdutil.matchfiles(repo, names)
2836 m = cmdutil.matchfiles(repo, names)
2742 changes = repo.status(match=m)[:4]
2837 changes = repo.status(match=m)[:4]
2743 modified, added, removed, deleted = map(set, changes)
2838 modified, added, removed, deleted = map(set, changes)
2744
2839
2745 # if f is a rename, also revert the source
2840 # if f is a rename, also revert the source
2746 cwd = repo.getcwd()
2841 cwd = repo.getcwd()
2747 for f in added:
2842 for f in added:
2748 src = repo.dirstate.copied(f)
2843 src = repo.dirstate.copied(f)
2749 if src and src not in names and repo.dirstate[src] == 'r':
2844 if src and src not in names and repo.dirstate[src] == 'r':
2750 removed.add(src)
2845 removed.add(src)
2751 names[src] = (repo.pathto(src, cwd), True)
2846 names[src] = (repo.pathto(src, cwd), True)
2752
2847
2753 def removeforget(abs):
2848 def removeforget(abs):
2754 if repo.dirstate[abs] == 'a':
2849 if repo.dirstate[abs] == 'a':
2755 return _('forgetting %s\n')
2850 return _('forgetting %s\n')
2756 return _('removing %s\n')
2851 return _('removing %s\n')
2757
2852
2758 revert = ([], _('reverting %s\n'))
2853 revert = ([], _('reverting %s\n'))
2759 add = ([], _('adding %s\n'))
2854 add = ([], _('adding %s\n'))
2760 remove = ([], removeforget)
2855 remove = ([], removeforget)
2761 undelete = ([], _('undeleting %s\n'))
2856 undelete = ([], _('undeleting %s\n'))
2762
2857
2763 disptable = (
2858 disptable = (
2764 # dispatch table:
2859 # dispatch table:
2765 # file state
2860 # file state
2766 # action if in target manifest
2861 # action if in target manifest
2767 # action if not in target manifest
2862 # action if not in target manifest
2768 # make backup if in target manifest
2863 # make backup if in target manifest
2769 # make backup if not in target manifest
2864 # make backup if not in target manifest
2770 (modified, revert, remove, True, True),
2865 (modified, revert, remove, True, True),
2771 (added, revert, remove, True, False),
2866 (added, revert, remove, True, False),
2772 (removed, undelete, None, False, False),
2867 (removed, undelete, None, False, False),
2773 (deleted, revert, remove, False, False),
2868 (deleted, revert, remove, False, False),
2774 )
2869 )
2775
2870
2776 for abs, (rel, exact) in sorted(names.items()):
2871 for abs, (rel, exact) in sorted(names.items()):
2777 mfentry = mf.get(abs)
2872 mfentry = mf.get(abs)
2778 target = repo.wjoin(abs)
2873 target = repo.wjoin(abs)
2779 def handle(xlist, dobackup):
2874 def handle(xlist, dobackup):
2780 xlist[0].append(abs)
2875 xlist[0].append(abs)
2781 if dobackup and not opts.get('no_backup') and util.lexists(target):
2876 if dobackup and not opts.get('no_backup') and util.lexists(target):
2782 bakname = "%s.orig" % rel
2877 bakname = "%s.orig" % rel
2783 ui.note(_('saving current version of %s as %s\n') %
2878 ui.note(_('saving current version of %s as %s\n') %
2784 (rel, bakname))
2879 (rel, bakname))
2785 if not opts.get('dry_run'):
2880 if not opts.get('dry_run'):
2786 util.copyfile(target, bakname)
2881 util.copyfile(target, bakname)
2787 if ui.verbose or not exact:
2882 if ui.verbose or not exact:
2788 msg = xlist[1]
2883 msg = xlist[1]
2789 if not isinstance(msg, basestring):
2884 if not isinstance(msg, basestring):
2790 msg = msg(abs)
2885 msg = msg(abs)
2791 ui.status(msg % rel)
2886 ui.status(msg % rel)
2792 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2887 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2793 if abs not in table:
2888 if abs not in table:
2794 continue
2889 continue
2795 # file has changed in dirstate
2890 # file has changed in dirstate
2796 if mfentry:
2891 if mfentry:
2797 handle(hitlist, backuphit)
2892 handle(hitlist, backuphit)
2798 elif misslist is not None:
2893 elif misslist is not None:
2799 handle(misslist, backupmiss)
2894 handle(misslist, backupmiss)
2800 break
2895 break
2801 else:
2896 else:
2802 if abs not in repo.dirstate:
2897 if abs not in repo.dirstate:
2803 if mfentry:
2898 if mfentry:
2804 handle(add, True)
2899 handle(add, True)
2805 elif exact:
2900 elif exact:
2806 ui.warn(_('file not managed: %s\n') % rel)
2901 ui.warn(_('file not managed: %s\n') % rel)
2807 continue
2902 continue
2808 # file has not changed in dirstate
2903 # file has not changed in dirstate
2809 if node == parent:
2904 if node == parent:
2810 if exact:
2905 if exact:
2811 ui.warn(_('no changes needed to %s\n') % rel)
2906 ui.warn(_('no changes needed to %s\n') % rel)
2812 continue
2907 continue
2813 if pmf is None:
2908 if pmf is None:
2814 # only need parent manifest in this unlikely case,
2909 # only need parent manifest in this unlikely case,
2815 # so do not read by default
2910 # so do not read by default
2816 pmf = repo[parent].manifest()
2911 pmf = repo[parent].manifest()
2817 if abs in pmf:
2912 if abs in pmf:
2818 if mfentry:
2913 if mfentry:
2819 # if version of file is same in parent and target
2914 # if version of file is same in parent and target
2820 # manifests, do nothing
2915 # manifests, do nothing
2821 if (pmf[abs] != mfentry or
2916 if (pmf[abs] != mfentry or
2822 pmf.flags(abs) != mf.flags(abs)):
2917 pmf.flags(abs) != mf.flags(abs)):
2823 handle(revert, False)
2918 handle(revert, False)
2824 else:
2919 else:
2825 handle(remove, False)
2920 handle(remove, False)
2826
2921
2827 if not opts.get('dry_run'):
2922 if not opts.get('dry_run'):
2828 def checkout(f):
2923 def checkout(f):
2829 fc = ctx[f]
2924 fc = ctx[f]
2830 repo.wwrite(f, fc.data(), fc.flags())
2925 repo.wwrite(f, fc.data(), fc.flags())
2831
2926
2832 audit_path = util.path_auditor(repo.root)
2927 audit_path = util.path_auditor(repo.root)
2833 for f in remove[0]:
2928 for f in remove[0]:
2834 if repo.dirstate[f] == 'a':
2929 if repo.dirstate[f] == 'a':
2835 repo.dirstate.forget(f)
2930 repo.dirstate.forget(f)
2836 continue
2931 continue
2837 audit_path(f)
2932 audit_path(f)
2838 try:
2933 try:
2839 util.unlink(repo.wjoin(f))
2934 util.unlink(repo.wjoin(f))
2840 except OSError:
2935 except OSError:
2841 pass
2936 pass
2842 repo.dirstate.remove(f)
2937 repo.dirstate.remove(f)
2843
2938
2844 normal = None
2939 normal = None
2845 if node == parent:
2940 if node == parent:
2846 # We're reverting to our parent. If possible, we'd like status
2941 # We're reverting to our parent. If possible, we'd like status
2847 # to report the file as clean. We have to use normallookup for
2942 # to report the file as clean. We have to use normallookup for
2848 # merges to avoid losing information about merged/dirty files.
2943 # merges to avoid losing information about merged/dirty files.
2849 if p2 != nullid:
2944 if p2 != nullid:
2850 normal = repo.dirstate.normallookup
2945 normal = repo.dirstate.normallookup
2851 else:
2946 else:
2852 normal = repo.dirstate.normal
2947 normal = repo.dirstate.normal
2853 for f in revert[0]:
2948 for f in revert[0]:
2854 checkout(f)
2949 checkout(f)
2855 if normal:
2950 if normal:
2856 normal(f)
2951 normal(f)
2857
2952
2858 for f in add[0]:
2953 for f in add[0]:
2859 checkout(f)
2954 checkout(f)
2860 repo.dirstate.add(f)
2955 repo.dirstate.add(f)
2861
2956
2862 normal = repo.dirstate.normallookup
2957 normal = repo.dirstate.normallookup
2863 if node == parent and p2 == nullid:
2958 if node == parent and p2 == nullid:
2864 normal = repo.dirstate.normal
2959 normal = repo.dirstate.normal
2865 for f in undelete[0]:
2960 for f in undelete[0]:
2866 checkout(f)
2961 checkout(f)
2867 normal(f)
2962 normal(f)
2868
2963
2869 finally:
2964 finally:
2870 wlock.release()
2965 wlock.release()
2871
2966
2872 def rollback(ui, repo, **opts):
2967 def rollback(ui, repo, **opts):
2873 """roll back the last transaction (dangerous)
2968 """roll back the last transaction (dangerous)
2874
2969
2875 This command should be used with care. There is only one level of
2970 This command should be used with care. There is only one level of
2876 rollback, and there is no way to undo a rollback. It will also
2971 rollback, and there is no way to undo a rollback. It will also
2877 restore the dirstate at the time of the last transaction, losing
2972 restore the dirstate at the time of the last transaction, losing
2878 any dirstate changes since that time. This command does not alter
2973 any dirstate changes since that time. This command does not alter
2879 the working directory.
2974 the working directory.
2880
2975
2881 Transactions are used to encapsulate the effects of all commands
2976 Transactions are used to encapsulate the effects of all commands
2882 that create new changesets or propagate existing changesets into a
2977 that create new changesets or propagate existing changesets into a
2883 repository. For example, the following commands are transactional,
2978 repository. For example, the following commands are transactional,
2884 and their effects can be rolled back:
2979 and their effects can be rolled back:
2885
2980
2886 - commit
2981 - commit
2887 - import
2982 - import
2888 - pull
2983 - pull
2889 - push (with this repository as the destination)
2984 - push (with this repository as the destination)
2890 - unbundle
2985 - unbundle
2891
2986
2892 This command is not intended for use on public repositories. Once
2987 This command is not intended for use on public repositories. Once
2893 changes are visible for pull by other users, rolling a transaction
2988 changes are visible for pull by other users, rolling a transaction
2894 back locally is ineffective (someone else may already have pulled
2989 back locally is ineffective (someone else may already have pulled
2895 the changes). Furthermore, a race is possible with readers of the
2990 the changes). Furthermore, a race is possible with readers of the
2896 repository; for example an in-progress pull from the repository
2991 repository; for example an in-progress pull from the repository
2897 may fail if a rollback is performed.
2992 may fail if a rollback is performed.
2993
2994 Returns 0 on success, 1 if no rollback data is available.
2898 """
2995 """
2899 repo.rollback(opts.get('dry_run'))
2996 return repo.rollback(opts.get('dry_run'))
2900
2997
2901 def root(ui, repo):
2998 def root(ui, repo):
2902 """print the root (top) of the current working directory
2999 """print the root (top) of the current working directory
2903
3000
2904 Print the root directory of the current repository.
3001 Print the root directory of the current repository.
3002
3003 Returns 0 on success.
2905 """
3004 """
2906 ui.write(repo.root + "\n")
3005 ui.write(repo.root + "\n")
2907
3006
2908 def serve(ui, repo, **opts):
3007 def serve(ui, repo, **opts):
2909 """start stand-alone webserver
3008 """start stand-alone webserver
2910
3009
2911 Start a local HTTP repository browser and pull server. You can use
3010 Start a local HTTP repository browser and pull server. You can use
2912 this for ad-hoc sharing and browing of repositories. It is
3011 this for ad-hoc sharing and browing of repositories. It is
2913 recommended to use a real web server to serve a repository for
3012 recommended to use a real web server to serve a repository for
2914 longer periods of time.
3013 longer periods of time.
2915
3014
2916 Please note that the server does not implement access control.
3015 Please note that the server does not implement access control.
2917 This means that, by default, anybody can read from the server and
3016 This means that, by default, anybody can read from the server and
2918 nobody can write to it by default. Set the ``web.allow_push``
3017 nobody can write to it by default. Set the ``web.allow_push``
2919 option to ``*`` to allow everybody to push to the server. You
3018 option to ``*`` to allow everybody to push to the server. You
2920 should use a real web server if you need to authenticate users.
3019 should use a real web server if you need to authenticate users.
2921
3020
2922 By default, the server logs accesses to stdout and errors to
3021 By default, the server logs accesses to stdout and errors to
2923 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3022 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2924 files.
3023 files.
2925
3024
2926 To have the server choose a free port number to listen on, specify
3025 To have the server choose a free port number to listen on, specify
2927 a port number of 0; in this case, the server will print the port
3026 a port number of 0; in this case, the server will print the port
2928 number it uses.
3027 number it uses.
3028
3029 Returns 0 on success.
2929 """
3030 """
2930
3031
2931 if opts["stdio"]:
3032 if opts["stdio"]:
2932 if repo is None:
3033 if repo is None:
2933 raise error.RepoError(_("There is no Mercurial repository here"
3034 raise error.RepoError(_("There is no Mercurial repository here"
2934 " (.hg not found)"))
3035 " (.hg not found)"))
2935 s = sshserver.sshserver(ui, repo)
3036 s = sshserver.sshserver(ui, repo)
2936 s.serve_forever()
3037 s.serve_forever()
2937
3038
2938 # this way we can check if something was given in the command-line
3039 # this way we can check if something was given in the command-line
2939 if opts.get('port'):
3040 if opts.get('port'):
2940 opts['port'] = int(opts.get('port'))
3041 opts['port'] = int(opts.get('port'))
2941
3042
2942 baseui = repo and repo.baseui or ui
3043 baseui = repo and repo.baseui or ui
2943 optlist = ("name templates style address port prefix ipv6"
3044 optlist = ("name templates style address port prefix ipv6"
2944 " accesslog errorlog certificate encoding")
3045 " accesslog errorlog certificate encoding")
2945 for o in optlist.split():
3046 for o in optlist.split():
2946 val = opts.get(o, '')
3047 val = opts.get(o, '')
2947 if val in (None, ''): # should check against default options instead
3048 if val in (None, ''): # should check against default options instead
2948 continue
3049 continue
2949 baseui.setconfig("web", o, val)
3050 baseui.setconfig("web", o, val)
2950 if repo and repo.ui != baseui:
3051 if repo and repo.ui != baseui:
2951 repo.ui.setconfig("web", o, val)
3052 repo.ui.setconfig("web", o, val)
2952
3053
2953 o = opts.get('web_conf') or opts.get('webdir_conf')
3054 o = opts.get('web_conf') or opts.get('webdir_conf')
2954 if not o:
3055 if not o:
2955 if not repo:
3056 if not repo:
2956 raise error.RepoError(_("There is no Mercurial repository"
3057 raise error.RepoError(_("There is no Mercurial repository"
2957 " here (.hg not found)"))
3058 " here (.hg not found)"))
2958 o = repo.root
3059 o = repo.root
2959
3060
2960 app = hgweb.hgweb(o, baseui=ui)
3061 app = hgweb.hgweb(o, baseui=ui)
2961
3062
2962 class service(object):
3063 class service(object):
2963 def init(self):
3064 def init(self):
2964 util.set_signal_handler()
3065 util.set_signal_handler()
2965 self.httpd = hgweb.server.create_server(ui, app)
3066 self.httpd = hgweb.server.create_server(ui, app)
2966
3067
2967 if opts['port'] and not ui.verbose:
3068 if opts['port'] and not ui.verbose:
2968 return
3069 return
2969
3070
2970 if self.httpd.prefix:
3071 if self.httpd.prefix:
2971 prefix = self.httpd.prefix.strip('/') + '/'
3072 prefix = self.httpd.prefix.strip('/') + '/'
2972 else:
3073 else:
2973 prefix = ''
3074 prefix = ''
2974
3075
2975 port = ':%d' % self.httpd.port
3076 port = ':%d' % self.httpd.port
2976 if port == ':80':
3077 if port == ':80':
2977 port = ''
3078 port = ''
2978
3079
2979 bindaddr = self.httpd.addr
3080 bindaddr = self.httpd.addr
2980 if bindaddr == '0.0.0.0':
3081 if bindaddr == '0.0.0.0':
2981 bindaddr = '*'
3082 bindaddr = '*'
2982 elif ':' in bindaddr: # IPv6
3083 elif ':' in bindaddr: # IPv6
2983 bindaddr = '[%s]' % bindaddr
3084 bindaddr = '[%s]' % bindaddr
2984
3085
2985 fqaddr = self.httpd.fqaddr
3086 fqaddr = self.httpd.fqaddr
2986 if ':' in fqaddr:
3087 if ':' in fqaddr:
2987 fqaddr = '[%s]' % fqaddr
3088 fqaddr = '[%s]' % fqaddr
2988 if opts['port']:
3089 if opts['port']:
2989 write = ui.status
3090 write = ui.status
2990 else:
3091 else:
2991 write = ui.write
3092 write = ui.write
2992 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3093 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2993 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3094 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2994
3095
2995 def run(self):
3096 def run(self):
2996 self.httpd.serve_forever()
3097 self.httpd.serve_forever()
2997
3098
2998 service = service()
3099 service = service()
2999
3100
3000 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3101 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3001
3102
3002 def status(ui, repo, *pats, **opts):
3103 def status(ui, repo, *pats, **opts):
3003 """show changed files in the working directory
3104 """show changed files in the working directory
3004
3105
3005 Show status of files in the repository. If names are given, only
3106 Show status of files in the repository. If names are given, only
3006 files that match are shown. Files that are clean or ignored or
3107 files that match are shown. Files that are clean or ignored or
3007 the source of a copy/move operation, are not listed unless
3108 the source of a copy/move operation, are not listed unless
3008 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3109 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3009 Unless options described with "show only ..." are given, the
3110 Unless options described with "show only ..." are given, the
3010 options -mardu are used.
3111 options -mardu are used.
3011
3112
3012 Option -q/--quiet hides untracked (unknown and ignored) files
3113 Option -q/--quiet hides untracked (unknown and ignored) files
3013 unless explicitly requested with -u/--unknown or -i/--ignored.
3114 unless explicitly requested with -u/--unknown or -i/--ignored.
3014
3115
3015 NOTE: status may appear to disagree with diff if permissions have
3116 NOTE: status may appear to disagree with diff if permissions have
3016 changed or a merge has occurred. The standard diff format does not
3117 changed or a merge has occurred. The standard diff format does not
3017 report permission changes and diff only reports changes relative
3118 report permission changes and diff only reports changes relative
3018 to one merge parent.
3119 to one merge parent.
3019
3120
3020 If one revision is given, it is used as the base revision.
3121 If one revision is given, it is used as the base revision.
3021 If two revisions are given, the differences between them are
3122 If two revisions are given, the differences between them are
3022 shown. The --change option can also be used as a shortcut to list
3123 shown. The --change option can also be used as a shortcut to list
3023 the changed files of a revision from its first parent.
3124 the changed files of a revision from its first parent.
3024
3125
3025 The codes used to show the status of files are::
3126 The codes used to show the status of files are::
3026
3127
3027 M = modified
3128 M = modified
3028 A = added
3129 A = added
3029 R = removed
3130 R = removed
3030 C = clean
3131 C = clean
3031 ! = missing (deleted by non-hg command, but still tracked)
3132 ! = missing (deleted by non-hg command, but still tracked)
3032 ? = not tracked
3133 ? = not tracked
3033 I = ignored
3134 I = ignored
3034 = origin of the previous file listed as A (added)
3135 = origin of the previous file listed as A (added)
3136
3137 Returns 0 on success.
3035 """
3138 """
3036
3139
3037 revs = opts.get('rev')
3140 revs = opts.get('rev')
3038 change = opts.get('change')
3141 change = opts.get('change')
3039
3142
3040 if revs and change:
3143 if revs and change:
3041 msg = _('cannot specify --rev and --change at the same time')
3144 msg = _('cannot specify --rev and --change at the same time')
3042 raise util.Abort(msg)
3145 raise util.Abort(msg)
3043 elif change:
3146 elif change:
3044 node2 = repo.lookup(change)
3147 node2 = repo.lookup(change)
3045 node1 = repo[node2].parents()[0].node()
3148 node1 = repo[node2].parents()[0].node()
3046 else:
3149 else:
3047 node1, node2 = cmdutil.revpair(repo, revs)
3150 node1, node2 = cmdutil.revpair(repo, revs)
3048
3151
3049 cwd = (pats and repo.getcwd()) or ''
3152 cwd = (pats and repo.getcwd()) or ''
3050 end = opts.get('print0') and '\0' or '\n'
3153 end = opts.get('print0') and '\0' or '\n'
3051 copy = {}
3154 copy = {}
3052 states = 'modified added removed deleted unknown ignored clean'.split()
3155 states = 'modified added removed deleted unknown ignored clean'.split()
3053 show = [k for k in states if opts.get(k)]
3156 show = [k for k in states if opts.get(k)]
3054 if opts.get('all'):
3157 if opts.get('all'):
3055 show += ui.quiet and (states[:4] + ['clean']) or states
3158 show += ui.quiet and (states[:4] + ['clean']) or states
3056 if not show:
3159 if not show:
3057 show = ui.quiet and states[:4] or states[:5]
3160 show = ui.quiet and states[:4] or states[:5]
3058
3161
3059 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3162 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3060 'ignored' in show, 'clean' in show, 'unknown' in show)
3163 'ignored' in show, 'clean' in show, 'unknown' in show)
3061 changestates = zip(states, 'MAR!?IC', stat)
3164 changestates = zip(states, 'MAR!?IC', stat)
3062
3165
3063 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3166 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3064 ctxn = repo[nullid]
3167 ctxn = repo[nullid]
3065 ctx1 = repo[node1]
3168 ctx1 = repo[node1]
3066 ctx2 = repo[node2]
3169 ctx2 = repo[node2]
3067 added = stat[1]
3170 added = stat[1]
3068 if node2 is None:
3171 if node2 is None:
3069 added = stat[0] + stat[1] # merged?
3172 added = stat[0] + stat[1] # merged?
3070
3173
3071 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3174 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3072 if k in added:
3175 if k in added:
3073 copy[k] = v
3176 copy[k] = v
3074 elif v in added:
3177 elif v in added:
3075 copy[v] = k
3178 copy[v] = k
3076
3179
3077 for state, char, files in changestates:
3180 for state, char, files in changestates:
3078 if state in show:
3181 if state in show:
3079 format = "%s %%s%s" % (char, end)
3182 format = "%s %%s%s" % (char, end)
3080 if opts.get('no_status'):
3183 if opts.get('no_status'):
3081 format = "%%s%s" % end
3184 format = "%%s%s" % end
3082
3185
3083 for f in files:
3186 for f in files:
3084 ui.write(format % repo.pathto(f, cwd),
3187 ui.write(format % repo.pathto(f, cwd),
3085 label='status.' + state)
3188 label='status.' + state)
3086 if f in copy:
3189 if f in copy:
3087 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3190 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3088 label='status.copied')
3191 label='status.copied')
3089
3192
3090 def summary(ui, repo, **opts):
3193 def summary(ui, repo, **opts):
3091 """summarize working directory state
3194 """summarize working directory state
3092
3195
3093 This generates a brief summary of the working directory state,
3196 This generates a brief summary of the working directory state,
3094 including parents, branch, commit status, and available updates.
3197 including parents, branch, commit status, and available updates.
3095
3198
3096 With the --remote option, this will check the default paths for
3199 With the --remote option, this will check the default paths for
3097 incoming and outgoing changes. This can be time-consuming.
3200 incoming and outgoing changes. This can be time-consuming.
3201
3202 Returns 0 on success.
3098 """
3203 """
3099
3204
3100 ctx = repo[None]
3205 ctx = repo[None]
3101 parents = ctx.parents()
3206 parents = ctx.parents()
3102 pnode = parents[0].node()
3207 pnode = parents[0].node()
3103
3208
3104 for p in parents:
3209 for p in parents:
3105 # label with log.changeset (instead of log.parent) since this
3210 # label with log.changeset (instead of log.parent) since this
3106 # shows a working directory parent *changeset*:
3211 # shows a working directory parent *changeset*:
3107 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3212 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3108 label='log.changeset')
3213 label='log.changeset')
3109 ui.write(' '.join(p.tags()), label='log.tag')
3214 ui.write(' '.join(p.tags()), label='log.tag')
3110 if p.rev() == -1:
3215 if p.rev() == -1:
3111 if not len(repo):
3216 if not len(repo):
3112 ui.write(_(' (empty repository)'))
3217 ui.write(_(' (empty repository)'))
3113 else:
3218 else:
3114 ui.write(_(' (no revision checked out)'))
3219 ui.write(_(' (no revision checked out)'))
3115 ui.write('\n')
3220 ui.write('\n')
3116 if p.description():
3221 if p.description():
3117 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3222 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3118 label='log.summary')
3223 label='log.summary')
3119
3224
3120 branch = ctx.branch()
3225 branch = ctx.branch()
3121 bheads = repo.branchheads(branch)
3226 bheads = repo.branchheads(branch)
3122 m = _('branch: %s\n') % branch
3227 m = _('branch: %s\n') % branch
3123 if branch != 'default':
3228 if branch != 'default':
3124 ui.write(m, label='log.branch')
3229 ui.write(m, label='log.branch')
3125 else:
3230 else:
3126 ui.status(m, label='log.branch')
3231 ui.status(m, label='log.branch')
3127
3232
3128 st = list(repo.status(unknown=True))[:6]
3233 st = list(repo.status(unknown=True))[:6]
3129
3234
3130 ms = mergemod.mergestate(repo)
3235 ms = mergemod.mergestate(repo)
3131 st.append([f for f in ms if ms[f] == 'u'])
3236 st.append([f for f in ms if ms[f] == 'u'])
3132
3237
3133 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3238 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3134 st.append(subs)
3239 st.append(subs)
3135
3240
3136 labels = [ui.label(_('%d modified'), 'status.modified'),
3241 labels = [ui.label(_('%d modified'), 'status.modified'),
3137 ui.label(_('%d added'), 'status.added'),
3242 ui.label(_('%d added'), 'status.added'),
3138 ui.label(_('%d removed'), 'status.removed'),
3243 ui.label(_('%d removed'), 'status.removed'),
3139 ui.label(_('%d deleted'), 'status.deleted'),
3244 ui.label(_('%d deleted'), 'status.deleted'),
3140 ui.label(_('%d unknown'), 'status.unknown'),
3245 ui.label(_('%d unknown'), 'status.unknown'),
3141 ui.label(_('%d ignored'), 'status.ignored'),
3246 ui.label(_('%d ignored'), 'status.ignored'),
3142 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3247 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3143 ui.label(_('%d subrepos'), 'status.modified')]
3248 ui.label(_('%d subrepos'), 'status.modified')]
3144 t = []
3249 t = []
3145 for s, l in zip(st, labels):
3250 for s, l in zip(st, labels):
3146 if s:
3251 if s:
3147 t.append(l % len(s))
3252 t.append(l % len(s))
3148
3253
3149 t = ', '.join(t)
3254 t = ', '.join(t)
3150 cleanworkdir = False
3255 cleanworkdir = False
3151
3256
3152 if len(parents) > 1:
3257 if len(parents) > 1:
3153 t += _(' (merge)')
3258 t += _(' (merge)')
3154 elif branch != parents[0].branch():
3259 elif branch != parents[0].branch():
3155 t += _(' (new branch)')
3260 t += _(' (new branch)')
3156 elif (parents[0].extra().get('close') and
3261 elif (parents[0].extra().get('close') and
3157 pnode in repo.branchheads(branch, closed=True)):
3262 pnode in repo.branchheads(branch, closed=True)):
3158 t += _(' (head closed)')
3263 t += _(' (head closed)')
3159 elif (not st[0] and not st[1] and not st[2] and not st[7]):
3264 elif (not st[0] and not st[1] and not st[2] and not st[7]):
3160 t += _(' (clean)')
3265 t += _(' (clean)')
3161 cleanworkdir = True
3266 cleanworkdir = True
3162 elif pnode not in bheads:
3267 elif pnode not in bheads:
3163 t += _(' (new branch head)')
3268 t += _(' (new branch head)')
3164
3269
3165 if cleanworkdir:
3270 if cleanworkdir:
3166 ui.status(_('commit: %s\n') % t.strip())
3271 ui.status(_('commit: %s\n') % t.strip())
3167 else:
3272 else:
3168 ui.write(_('commit: %s\n') % t.strip())
3273 ui.write(_('commit: %s\n') % t.strip())
3169
3274
3170 # all ancestors of branch heads - all ancestors of parent = new csets
3275 # all ancestors of branch heads - all ancestors of parent = new csets
3171 new = [0] * len(repo)
3276 new = [0] * len(repo)
3172 cl = repo.changelog
3277 cl = repo.changelog
3173 for a in [cl.rev(n) for n in bheads]:
3278 for a in [cl.rev(n) for n in bheads]:
3174 new[a] = 1
3279 new[a] = 1
3175 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3280 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3176 new[a] = 1
3281 new[a] = 1
3177 for a in [p.rev() for p in parents]:
3282 for a in [p.rev() for p in parents]:
3178 if a >= 0:
3283 if a >= 0:
3179 new[a] = 0
3284 new[a] = 0
3180 for a in cl.ancestors(*[p.rev() for p in parents]):
3285 for a in cl.ancestors(*[p.rev() for p in parents]):
3181 new[a] = 0
3286 new[a] = 0
3182 new = sum(new)
3287 new = sum(new)
3183
3288
3184 if new == 0:
3289 if new == 0:
3185 ui.status(_('update: (current)\n'))
3290 ui.status(_('update: (current)\n'))
3186 elif pnode not in bheads:
3291 elif pnode not in bheads:
3187 ui.write(_('update: %d new changesets (update)\n') % new)
3292 ui.write(_('update: %d new changesets (update)\n') % new)
3188 else:
3293 else:
3189 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3294 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3190 (new, len(bheads)))
3295 (new, len(bheads)))
3191
3296
3192 if opts.get('remote'):
3297 if opts.get('remote'):
3193 t = []
3298 t = []
3194 source, branches = hg.parseurl(ui.expandpath('default'))
3299 source, branches = hg.parseurl(ui.expandpath('default'))
3195 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3300 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3196 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3301 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3197 ui.debug('comparing with %s\n' % url.hidepassword(source))
3302 ui.debug('comparing with %s\n' % url.hidepassword(source))
3198 repo.ui.pushbuffer()
3303 repo.ui.pushbuffer()
3199 common, incoming, rheads = repo.findcommonincoming(other)
3304 common, incoming, rheads = repo.findcommonincoming(other)
3200 repo.ui.popbuffer()
3305 repo.ui.popbuffer()
3201 if incoming:
3306 if incoming:
3202 t.append(_('1 or more incoming'))
3307 t.append(_('1 or more incoming'))
3203
3308
3204 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3309 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3205 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3310 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3206 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3311 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3207 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3312 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3208 repo.ui.pushbuffer()
3313 repo.ui.pushbuffer()
3209 o = repo.findoutgoing(other)
3314 o = repo.findoutgoing(other)
3210 repo.ui.popbuffer()
3315 repo.ui.popbuffer()
3211 o = repo.changelog.nodesbetween(o, None)[0]
3316 o = repo.changelog.nodesbetween(o, None)[0]
3212 if o:
3317 if o:
3213 t.append(_('%d outgoing') % len(o))
3318 t.append(_('%d outgoing') % len(o))
3214
3319
3215 if t:
3320 if t:
3216 ui.write(_('remote: %s\n') % (', '.join(t)))
3321 ui.write(_('remote: %s\n') % (', '.join(t)))
3217 else:
3322 else:
3218 ui.status(_('remote: (synced)\n'))
3323 ui.status(_('remote: (synced)\n'))
3219
3324
3220 def tag(ui, repo, name1, *names, **opts):
3325 def tag(ui, repo, name1, *names, **opts):
3221 """add one or more tags for the current or given revision
3326 """add one or more tags for the current or given revision
3222
3327
3223 Name a particular revision using <name>.
3328 Name a particular revision using <name>.
3224
3329
3225 Tags are used to name particular revisions of the repository and are
3330 Tags are used to name particular revisions of the repository and are
3226 very useful to compare different revisions, to go back to significant
3331 very useful to compare different revisions, to go back to significant
3227 earlier versions or to mark branch points as releases, etc.
3332 earlier versions or to mark branch points as releases, etc.
3228
3333
3229 If no revision is given, the parent of the working directory is
3334 If no revision is given, the parent of the working directory is
3230 used, or tip if no revision is checked out.
3335 used, or tip if no revision is checked out.
3231
3336
3232 To facilitate version control, distribution, and merging of tags,
3337 To facilitate version control, distribution, and merging of tags,
3233 they are stored as a file named ".hgtags" which is managed
3338 they are stored as a file named ".hgtags" which is managed
3234 similarly to other project files and can be hand-edited if
3339 similarly to other project files and can be hand-edited if
3235 necessary. The file '.hg/localtags' is used for local tags (not
3340 necessary. The file '.hg/localtags' is used for local tags (not
3236 shared among repositories).
3341 shared among repositories).
3237
3342
3238 See :hg:`help dates` for a list of formats valid for -d/--date.
3343 See :hg:`help dates` for a list of formats valid for -d/--date.
3239
3344
3240 Since tag names have priority over branch names during revision
3345 Since tag names have priority over branch names during revision
3241 lookup, using an existing branch name as a tag name is discouraged.
3346 lookup, using an existing branch name as a tag name is discouraged.
3347
3348 Returns 0 on success.
3242 """
3349 """
3243
3350
3244 rev_ = "."
3351 rev_ = "."
3245 names = [t.strip() for t in (name1,) + names]
3352 names = [t.strip() for t in (name1,) + names]
3246 if len(names) != len(set(names)):
3353 if len(names) != len(set(names)):
3247 raise util.Abort(_('tag names must be unique'))
3354 raise util.Abort(_('tag names must be unique'))
3248 for n in names:
3355 for n in names:
3249 if n in ['tip', '.', 'null']:
3356 if n in ['tip', '.', 'null']:
3250 raise util.Abort(_('the name \'%s\' is reserved') % n)
3357 raise util.Abort(_('the name \'%s\' is reserved') % n)
3251 if opts.get('rev') and opts.get('remove'):
3358 if opts.get('rev') and opts.get('remove'):
3252 raise util.Abort(_("--rev and --remove are incompatible"))
3359 raise util.Abort(_("--rev and --remove are incompatible"))
3253 if opts.get('rev'):
3360 if opts.get('rev'):
3254 rev_ = opts['rev']
3361 rev_ = opts['rev']
3255 message = opts.get('message')
3362 message = opts.get('message')
3256 if opts.get('remove'):
3363 if opts.get('remove'):
3257 expectedtype = opts.get('local') and 'local' or 'global'
3364 expectedtype = opts.get('local') and 'local' or 'global'
3258 for n in names:
3365 for n in names:
3259 if not repo.tagtype(n):
3366 if not repo.tagtype(n):
3260 raise util.Abort(_('tag \'%s\' does not exist') % n)
3367 raise util.Abort(_('tag \'%s\' does not exist') % n)
3261 if repo.tagtype(n) != expectedtype:
3368 if repo.tagtype(n) != expectedtype:
3262 if expectedtype == 'global':
3369 if expectedtype == 'global':
3263 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3370 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3264 else:
3371 else:
3265 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3372 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3266 rev_ = nullid
3373 rev_ = nullid
3267 if not message:
3374 if not message:
3268 # we don't translate commit messages
3375 # we don't translate commit messages
3269 message = 'Removed tag %s' % ', '.join(names)
3376 message = 'Removed tag %s' % ', '.join(names)
3270 elif not opts.get('force'):
3377 elif not opts.get('force'):
3271 for n in names:
3378 for n in names:
3272 if n in repo.tags():
3379 if n in repo.tags():
3273 raise util.Abort(_('tag \'%s\' already exists '
3380 raise util.Abort(_('tag \'%s\' already exists '
3274 '(use -f to force)') % n)
3381 '(use -f to force)') % n)
3275 if not rev_ and repo.dirstate.parents()[1] != nullid:
3382 if not rev_ and repo.dirstate.parents()[1] != nullid:
3276 raise util.Abort(_('uncommitted merge - please provide a '
3383 raise util.Abort(_('uncommitted merge - please provide a '
3277 'specific revision'))
3384 'specific revision'))
3278 r = repo[rev_].node()
3385 r = repo[rev_].node()
3279
3386
3280 if not message:
3387 if not message:
3281 # we don't translate commit messages
3388 # we don't translate commit messages
3282 message = ('Added tag %s for changeset %s' %
3389 message = ('Added tag %s for changeset %s' %
3283 (', '.join(names), short(r)))
3390 (', '.join(names), short(r)))
3284
3391
3285 date = opts.get('date')
3392 date = opts.get('date')
3286 if date:
3393 if date:
3287 date = util.parsedate(date)
3394 date = util.parsedate(date)
3288
3395
3289 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3396 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3290
3397
3291 def tags(ui, repo):
3398 def tags(ui, repo):
3292 """list repository tags
3399 """list repository tags
3293
3400
3294 This lists both regular and local tags. When the -v/--verbose
3401 This lists both regular and local tags. When the -v/--verbose
3295 switch is used, a third column "local" is printed for local tags.
3402 switch is used, a third column "local" is printed for local tags.
3403
3404 Returns 0 on success.
3296 """
3405 """
3297
3406
3298 hexfunc = ui.debugflag and hex or short
3407 hexfunc = ui.debugflag and hex or short
3299 tagtype = ""
3408 tagtype = ""
3300
3409
3301 for t, n in reversed(repo.tagslist()):
3410 for t, n in reversed(repo.tagslist()):
3302 if ui.quiet:
3411 if ui.quiet:
3303 ui.write("%s\n" % t)
3412 ui.write("%s\n" % t)
3304 continue
3413 continue
3305
3414
3306 try:
3415 try:
3307 hn = hexfunc(n)
3416 hn = hexfunc(n)
3308 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3417 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3309 except error.LookupError:
3418 except error.LookupError:
3310 r = " ?:%s" % hn
3419 r = " ?:%s" % hn
3311 else:
3420 else:
3312 spaces = " " * (30 - encoding.colwidth(t))
3421 spaces = " " * (30 - encoding.colwidth(t))
3313 if ui.verbose:
3422 if ui.verbose:
3314 if repo.tagtype(t) == 'local':
3423 if repo.tagtype(t) == 'local':
3315 tagtype = " local"
3424 tagtype = " local"
3316 else:
3425 else:
3317 tagtype = ""
3426 tagtype = ""
3318 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3427 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3319
3428
3320 def tip(ui, repo, **opts):
3429 def tip(ui, repo, **opts):
3321 """show the tip revision
3430 """show the tip revision
3322
3431
3323 The tip revision (usually just called the tip) is the changeset
3432 The tip revision (usually just called the tip) is the changeset
3324 most recently added to the repository (and therefore the most
3433 most recently added to the repository (and therefore the most
3325 recently changed head).
3434 recently changed head).
3326
3435
3327 If you have just made a commit, that commit will be the tip. If
3436 If you have just made a commit, that commit will be the tip. If
3328 you have just pulled changes from another repository, the tip of
3437 you have just pulled changes from another repository, the tip of
3329 that repository becomes the current tip. The "tip" tag is special
3438 that repository becomes the current tip. The "tip" tag is special
3330 and cannot be renamed or assigned to a different changeset.
3439 and cannot be renamed or assigned to a different changeset.
3440
3441 Returns 0 on success.
3331 """
3442 """
3332 displayer = cmdutil.show_changeset(ui, repo, opts)
3443 displayer = cmdutil.show_changeset(ui, repo, opts)
3333 displayer.show(repo[len(repo) - 1])
3444 displayer.show(repo[len(repo) - 1])
3334 displayer.close()
3445 displayer.close()
3335
3446
3336 def unbundle(ui, repo, fname1, *fnames, **opts):
3447 def unbundle(ui, repo, fname1, *fnames, **opts):
3337 """apply one or more changegroup files
3448 """apply one or more changegroup files
3338
3449
3339 Apply one or more compressed changegroup files generated by the
3450 Apply one or more compressed changegroup files generated by the
3340 bundle command.
3451 bundle command.
3452
3453 Returns 0 on success, 1 if an update has unresolved files.
3341 """
3454 """
3342 fnames = (fname1,) + fnames
3455 fnames = (fname1,) + fnames
3343
3456
3344 lock = repo.lock()
3457 lock = repo.lock()
3345 try:
3458 try:
3346 for fname in fnames:
3459 for fname in fnames:
3347 f = url.open(ui, fname)
3460 f = url.open(ui, fname)
3348 gen = changegroup.readbundle(f, fname)
3461 gen = changegroup.readbundle(f, fname)
3349 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3462 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3350 finally:
3463 finally:
3351 lock.release()
3464 lock.release()
3352
3465
3353 return postincoming(ui, repo, modheads, opts.get('update'), None)
3466 return postincoming(ui, repo, modheads, opts.get('update'), None)
3354
3467
3355 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3468 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3356 """update working directory (or switch revisions)
3469 """update working directory (or switch revisions)
3357
3470
3358 Update the repository's working directory to the specified
3471 Update the repository's working directory to the specified
3359 changeset.
3472 changeset.
3360
3473
3361 If no changeset is specified, attempt to update to the head of the
3474 If no changeset is specified, attempt to update to the head of the
3362 current branch. If this head is a descendant of the working
3475 current branch. If this head is a descendant of the working
3363 directory's parent, update to it, otherwise abort.
3476 directory's parent, update to it, otherwise abort.
3364
3477
3365 The following rules apply when the working directory contains
3478 The following rules apply when the working directory contains
3366 uncommitted changes:
3479 uncommitted changes:
3367
3480
3368 1. If neither -c/--check nor -C/--clean is specified, and if
3481 1. If neither -c/--check nor -C/--clean is specified, and if
3369 the requested changeset is an ancestor or descendant of
3482 the requested changeset is an ancestor or descendant of
3370 the working directory's parent, the uncommitted changes
3483 the working directory's parent, the uncommitted changes
3371 are merged into the requested changeset and the merged
3484 are merged into the requested changeset and the merged
3372 result is left uncommitted. If the requested changeset is
3485 result is left uncommitted. If the requested changeset is
3373 not an ancestor or descendant (that is, it is on another
3486 not an ancestor or descendant (that is, it is on another
3374 branch), the update is aborted and the uncommitted changes
3487 branch), the update is aborted and the uncommitted changes
3375 are preserved.
3488 are preserved.
3376
3489
3377 2. With the -c/--check option, the update is aborted and the
3490 2. With the -c/--check option, the update is aborted and the
3378 uncommitted changes are preserved.
3491 uncommitted changes are preserved.
3379
3492
3380 3. With the -C/--clean option, uncommitted changes are discarded and
3493 3. With the -C/--clean option, uncommitted changes are discarded and
3381 the working directory is updated to the requested changeset.
3494 the working directory is updated to the requested changeset.
3382
3495
3383 Use null as the changeset to remove the working directory (like
3496 Use null as the changeset to remove the working directory (like
3384 :hg:`clone -U`).
3497 :hg:`clone -U`).
3385
3498
3386 If you want to update just one file to an older changeset, use :hg:`revert`.
3499 If you want to update just one file to an older changeset, use :hg:`revert`.
3387
3500
3388 See :hg:`help dates` for a list of formats valid for -d/--date.
3501 See :hg:`help dates` for a list of formats valid for -d/--date.
3502
3503 Returns 0 on success, 1 if there are unresolved files.
3389 """
3504 """
3390 if rev and node:
3505 if rev and node:
3391 raise util.Abort(_("please specify just one revision"))
3506 raise util.Abort(_("please specify just one revision"))
3392
3507
3393 if not rev:
3508 if not rev:
3394 rev = node
3509 rev = node
3395
3510
3396 if check and clean:
3511 if check and clean:
3397 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3512 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3398
3513
3399 if check:
3514 if check:
3400 # we could use dirty() but we can ignore merge and branch trivia
3515 # we could use dirty() but we can ignore merge and branch trivia
3401 c = repo[None]
3516 c = repo[None]
3402 if c.modified() or c.added() or c.removed():
3517 if c.modified() or c.added() or c.removed():
3403 raise util.Abort(_("uncommitted local changes"))
3518 raise util.Abort(_("uncommitted local changes"))
3404
3519
3405 if date:
3520 if date:
3406 if rev:
3521 if rev:
3407 raise util.Abort(_("you can't specify a revision and a date"))
3522 raise util.Abort(_("you can't specify a revision and a date"))
3408 rev = cmdutil.finddate(ui, repo, date)
3523 rev = cmdutil.finddate(ui, repo, date)
3409
3524
3410 if clean or check:
3525 if clean or check:
3411 return hg.clean(repo, rev)
3526 return hg.clean(repo, rev)
3412 else:
3527 else:
3413 return hg.update(repo, rev)
3528 return hg.update(repo, rev)
3414
3529
3415 def verify(ui, repo):
3530 def verify(ui, repo):
3416 """verify the integrity of the repository
3531 """verify the integrity of the repository
3417
3532
3418 Verify the integrity of the current repository.
3533 Verify the integrity of the current repository.
3419
3534
3420 This will perform an extensive check of the repository's
3535 This will perform an extensive check of the repository's
3421 integrity, validating the hashes and checksums of each entry in
3536 integrity, validating the hashes and checksums of each entry in
3422 the changelog, manifest, and tracked files, as well as the
3537 the changelog, manifest, and tracked files, as well as the
3423 integrity of their crosslinks and indices.
3538 integrity of their crosslinks and indices.
3539
3540 Returns 0 on success, 1 if errors are encountered.
3424 """
3541 """
3425 return hg.verify(repo)
3542 return hg.verify(repo)
3426
3543
3427 def version_(ui):
3544 def version_(ui):
3428 """output version and copyright information"""
3545 """output version and copyright information"""
3429 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3546 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3430 % util.version())
3547 % util.version())
3431 ui.status(_(
3548 ui.status(_(
3432 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3549 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3433 "This is free software; see the source for copying conditions. "
3550 "This is free software; see the source for copying conditions. "
3434 "There is NO\nwarranty; "
3551 "There is NO\nwarranty; "
3435 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3552 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3436 ))
3553 ))
3437
3554
3438 # Command options and aliases are listed here, alphabetically
3555 # Command options and aliases are listed here, alphabetically
3439
3556
3440 globalopts = [
3557 globalopts = [
3441 ('R', 'repository', '',
3558 ('R', 'repository', '',
3442 _('repository root directory or name of overlay bundle file')),
3559 _('repository root directory or name of overlay bundle file')),
3443 ('', 'cwd', '', _('change working directory')),
3560 ('', 'cwd', '', _('change working directory')),
3444 ('y', 'noninteractive', None,
3561 ('y', 'noninteractive', None,
3445 _('do not prompt, assume \'yes\' for any required answers')),
3562 _('do not prompt, assume \'yes\' for any required answers')),
3446 ('q', 'quiet', None, _('suppress output')),
3563 ('q', 'quiet', None, _('suppress output')),
3447 ('v', 'verbose', None, _('enable additional output')),
3564 ('v', 'verbose', None, _('enable additional output')),
3448 ('', 'config', [],
3565 ('', 'config', [],
3449 _('set/override config option (use \'section.name=value\')')),
3566 _('set/override config option (use \'section.name=value\')')),
3450 ('', 'debug', None, _('enable debugging output')),
3567 ('', 'debug', None, _('enable debugging output')),
3451 ('', 'debugger', None, _('start debugger')),
3568 ('', 'debugger', None, _('start debugger')),
3452 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3569 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3453 ('', 'encodingmode', encoding.encodingmode,
3570 ('', 'encodingmode', encoding.encodingmode,
3454 _('set the charset encoding mode')),
3571 _('set the charset encoding mode')),
3455 ('', 'traceback', None, _('always print a traceback on exception')),
3572 ('', 'traceback', None, _('always print a traceback on exception')),
3456 ('', 'time', None, _('time how long the command takes')),
3573 ('', 'time', None, _('time how long the command takes')),
3457 ('', 'profile', None, _('print command execution profile')),
3574 ('', 'profile', None, _('print command execution profile')),
3458 ('', 'version', None, _('output version information and exit')),
3575 ('', 'version', None, _('output version information and exit')),
3459 ('h', 'help', None, _('display help and exit')),
3576 ('h', 'help', None, _('display help and exit')),
3460 ]
3577 ]
3461
3578
3462 dryrunopts = [('n', 'dry-run', None,
3579 dryrunopts = [('n', 'dry-run', None,
3463 _('do not perform actions, just print output'))]
3580 _('do not perform actions, just print output'))]
3464
3581
3465 remoteopts = [
3582 remoteopts = [
3466 ('e', 'ssh', '', _('specify ssh command to use')),
3583 ('e', 'ssh', '', _('specify ssh command to use')),
3467 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3584 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3468 ]
3585 ]
3469
3586
3470 walkopts = [
3587 walkopts = [
3471 ('I', 'include', [], _('include names matching the given patterns')),
3588 ('I', 'include', [], _('include names matching the given patterns')),
3472 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3589 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3473 ]
3590 ]
3474
3591
3475 commitopts = [
3592 commitopts = [
3476 ('m', 'message', '', _('use <text> as commit message')),
3593 ('m', 'message', '', _('use <text> as commit message')),
3477 ('l', 'logfile', '', _('read commit message from <file>')),
3594 ('l', 'logfile', '', _('read commit message from <file>')),
3478 ]
3595 ]
3479
3596
3480 commitopts2 = [
3597 commitopts2 = [
3481 ('d', 'date', '', _('record datecode as commit date')),
3598 ('d', 'date', '', _('record datecode as commit date')),
3482 ('u', 'user', '', _('record the specified user as committer')),
3599 ('u', 'user', '', _('record the specified user as committer')),
3483 ]
3600 ]
3484
3601
3485 templateopts = [
3602 templateopts = [
3486 ('', 'style', '', _('display using template map file')),
3603 ('', 'style', '', _('display using template map file')),
3487 ('', 'template', '', _('display with template')),
3604 ('', 'template', '', _('display with template')),
3488 ]
3605 ]
3489
3606
3490 logopts = [
3607 logopts = [
3491 ('p', 'patch', None, _('show patch')),
3608 ('p', 'patch', None, _('show patch')),
3492 ('g', 'git', None, _('use git extended diff format')),
3609 ('g', 'git', None, _('use git extended diff format')),
3493 ('l', 'limit', '', _('limit number of changes displayed')),
3610 ('l', 'limit', '', _('limit number of changes displayed')),
3494 ('M', 'no-merges', None, _('do not show merges')),
3611 ('M', 'no-merges', None, _('do not show merges')),
3495 ('', 'stat', None, _('output diffstat-style summary of changes')),
3612 ('', 'stat', None, _('output diffstat-style summary of changes')),
3496 ] + templateopts
3613 ] + templateopts
3497
3614
3498 diffopts = [
3615 diffopts = [
3499 ('a', 'text', None, _('treat all files as text')),
3616 ('a', 'text', None, _('treat all files as text')),
3500 ('g', 'git', None, _('use git extended diff format')),
3617 ('g', 'git', None, _('use git extended diff format')),
3501 ('', 'nodates', None, _('omit dates from diff headers'))
3618 ('', 'nodates', None, _('omit dates from diff headers'))
3502 ]
3619 ]
3503
3620
3504 diffopts2 = [
3621 diffopts2 = [
3505 ('p', 'show-function', None, _('show which function each change is in')),
3622 ('p', 'show-function', None, _('show which function each change is in')),
3506 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3623 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3507 ('w', 'ignore-all-space', None,
3624 ('w', 'ignore-all-space', None,
3508 _('ignore white space when comparing lines')),
3625 _('ignore white space when comparing lines')),
3509 ('b', 'ignore-space-change', None,
3626 ('b', 'ignore-space-change', None,
3510 _('ignore changes in the amount of white space')),
3627 _('ignore changes in the amount of white space')),
3511 ('B', 'ignore-blank-lines', None,
3628 ('B', 'ignore-blank-lines', None,
3512 _('ignore changes whose lines are all blank')),
3629 _('ignore changes whose lines are all blank')),
3513 ('U', 'unified', '', _('number of lines of context to show')),
3630 ('U', 'unified', '', _('number of lines of context to show')),
3514 ('', 'stat', None, _('output diffstat-style summary of changes')),
3631 ('', 'stat', None, _('output diffstat-style summary of changes')),
3515 ]
3632 ]
3516
3633
3517 similarityopts = [
3634 similarityopts = [
3518 ('s', 'similarity', '',
3635 ('s', 'similarity', '',
3519 _('guess renamed files by similarity (0<=s<=100)'))
3636 _('guess renamed files by similarity (0<=s<=100)'))
3520 ]
3637 ]
3521
3638
3522 table = {
3639 table = {
3523 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3640 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3524 "addremove":
3641 "addremove":
3525 (addremove, similarityopts + walkopts + dryrunopts,
3642 (addremove, similarityopts + walkopts + dryrunopts,
3526 _('[OPTION]... [FILE]...')),
3643 _('[OPTION]... [FILE]...')),
3527 "^annotate|blame":
3644 "^annotate|blame":
3528 (annotate,
3645 (annotate,
3529 [('r', 'rev', '', _('annotate the specified revision')),
3646 [('r', 'rev', '', _('annotate the specified revision')),
3530 ('', 'follow', None,
3647 ('', 'follow', None,
3531 _('follow copies/renames and list the filename (DEPRECATED)')),
3648 _('follow copies/renames and list the filename (DEPRECATED)')),
3532 ('', 'no-follow', None, _("don't follow copies and renames")),
3649 ('', 'no-follow', None, _("don't follow copies and renames")),
3533 ('a', 'text', None, _('treat all files as text')),
3650 ('a', 'text', None, _('treat all files as text')),
3534 ('u', 'user', None, _('list the author (long with -v)')),
3651 ('u', 'user', None, _('list the author (long with -v)')),
3535 ('f', 'file', None, _('list the filename')),
3652 ('f', 'file', None, _('list the filename')),
3536 ('d', 'date', None, _('list the date (short with -q)')),
3653 ('d', 'date', None, _('list the date (short with -q)')),
3537 ('n', 'number', None, _('list the revision number (default)')),
3654 ('n', 'number', None, _('list the revision number (default)')),
3538 ('c', 'changeset', None, _('list the changeset')),
3655 ('c', 'changeset', None, _('list the changeset')),
3539 ('l', 'line-number', None,
3656 ('l', 'line-number', None,
3540 _('show line number at the first appearance'))
3657 _('show line number at the first appearance'))
3541 ] + walkopts,
3658 ] + walkopts,
3542 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3659 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3543 "archive":
3660 "archive":
3544 (archive,
3661 (archive,
3545 [('', 'no-decode', None, _('do not pass files through decoders')),
3662 [('', 'no-decode', None, _('do not pass files through decoders')),
3546 ('p', 'prefix', '', _('directory prefix for files in archive')),
3663 ('p', 'prefix', '', _('directory prefix for files in archive')),
3547 ('r', 'rev', '', _('revision to distribute')),
3664 ('r', 'rev', '', _('revision to distribute')),
3548 ('t', 'type', '', _('type of distribution to create')),
3665 ('t', 'type', '', _('type of distribution to create')),
3549 ] + walkopts,
3666 ] + walkopts,
3550 _('[OPTION]... DEST')),
3667 _('[OPTION]... DEST')),
3551 "backout":
3668 "backout":
3552 (backout,
3669 (backout,
3553 [('', 'merge', None,
3670 [('', 'merge', None,
3554 _('merge with old dirstate parent after backout')),
3671 _('merge with old dirstate parent after backout')),
3555 ('', 'parent', '', _('parent to choose when backing out merge')),
3672 ('', 'parent', '', _('parent to choose when backing out merge')),
3556 ('r', 'rev', '', _('revision to backout')),
3673 ('r', 'rev', '', _('revision to backout')),
3557 ] + walkopts + commitopts + commitopts2,
3674 ] + walkopts + commitopts + commitopts2,
3558 _('[OPTION]... [-r] REV')),
3675 _('[OPTION]... [-r] REV')),
3559 "bisect":
3676 "bisect":
3560 (bisect,
3677 (bisect,
3561 [('r', 'reset', False, _('reset bisect state')),
3678 [('r', 'reset', False, _('reset bisect state')),
3562 ('g', 'good', False, _('mark changeset good')),
3679 ('g', 'good', False, _('mark changeset good')),
3563 ('b', 'bad', False, _('mark changeset bad')),
3680 ('b', 'bad', False, _('mark changeset bad')),
3564 ('s', 'skip', False, _('skip testing changeset')),
3681 ('s', 'skip', False, _('skip testing changeset')),
3565 ('c', 'command', '', _('use command to check changeset state')),
3682 ('c', 'command', '', _('use command to check changeset state')),
3566 ('U', 'noupdate', False, _('do not update to target'))],
3683 ('U', 'noupdate', False, _('do not update to target'))],
3567 _("[-gbsr] [-U] [-c CMD] [REV]")),
3684 _("[-gbsr] [-U] [-c CMD] [REV]")),
3568 "branch":
3685 "branch":
3569 (branch,
3686 (branch,
3570 [('f', 'force', None,
3687 [('f', 'force', None,
3571 _('set branch name even if it shadows an existing branch')),
3688 _('set branch name even if it shadows an existing branch')),
3572 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3689 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3573 _('[-fC] [NAME]')),
3690 _('[-fC] [NAME]')),
3574 "branches":
3691 "branches":
3575 (branches,
3692 (branches,
3576 [('a', 'active', False,
3693 [('a', 'active', False,
3577 _('show only branches that have unmerged heads')),
3694 _('show only branches that have unmerged heads')),
3578 ('c', 'closed', False,
3695 ('c', 'closed', False,
3579 _('show normal and closed branches'))],
3696 _('show normal and closed branches'))],
3580 _('[-ac]')),
3697 _('[-ac]')),
3581 "bundle":
3698 "bundle":
3582 (bundle,
3699 (bundle,
3583 [('f', 'force', None,
3700 [('f', 'force', None,
3584 _('run even when the destination is unrelated')),
3701 _('run even when the destination is unrelated')),
3585 ('r', 'rev', [],
3702 ('r', 'rev', [],
3586 _('a changeset intended to be added to the destination')),
3703 _('a changeset intended to be added to the destination')),
3587 ('b', 'branch', [],
3704 ('b', 'branch', [],
3588 _('a specific branch you would like to bundle')),
3705 _('a specific branch you would like to bundle')),
3589 ('', 'base', [],
3706 ('', 'base', [],
3590 _('a base changeset assumed to be available at the destination')),
3707 _('a base changeset assumed to be available at the destination')),
3591 ('a', 'all', None, _('bundle all changesets in the repository')),
3708 ('a', 'all', None, _('bundle all changesets in the repository')),
3592 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3709 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3593 ] + remoteopts,
3710 ] + remoteopts,
3594 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3711 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3595 "cat":
3712 "cat":
3596 (cat,
3713 (cat,
3597 [('o', 'output', '', _('print output to file with formatted name')),
3714 [('o', 'output', '', _('print output to file with formatted name')),
3598 ('r', 'rev', '', _('print the given revision')),
3715 ('r', 'rev', '', _('print the given revision')),
3599 ('', 'decode', None, _('apply any matching decode filter')),
3716 ('', 'decode', None, _('apply any matching decode filter')),
3600 ] + walkopts,
3717 ] + walkopts,
3601 _('[OPTION]... FILE...')),
3718 _('[OPTION]... FILE...')),
3602 "^clone":
3719 "^clone":
3603 (clone,
3720 (clone,
3604 [('U', 'noupdate', None,
3721 [('U', 'noupdate', None,
3605 _('the clone will include an empty working copy (only a repository)')),
3722 _('the clone will include an empty working copy (only a repository)')),
3606 ('u', 'updaterev', '',
3723 ('u', 'updaterev', '',
3607 _('revision, tag or branch to check out')),
3724 _('revision, tag or branch to check out')),
3608 ('r', 'rev', [],
3725 ('r', 'rev', [],
3609 _('include the specified changeset')),
3726 _('include the specified changeset')),
3610 ('b', 'branch', [],
3727 ('b', 'branch', [],
3611 _('clone only the specified branch')),
3728 _('clone only the specified branch')),
3612 ('', 'pull', None, _('use pull protocol to copy metadata')),
3729 ('', 'pull', None, _('use pull protocol to copy metadata')),
3613 ('', 'uncompressed', None,
3730 ('', 'uncompressed', None,
3614 _('use uncompressed transfer (fast over LAN)')),
3731 _('use uncompressed transfer (fast over LAN)')),
3615 ] + remoteopts,
3732 ] + remoteopts,
3616 _('[OPTION]... SOURCE [DEST]')),
3733 _('[OPTION]... SOURCE [DEST]')),
3617 "^commit|ci":
3734 "^commit|ci":
3618 (commit,
3735 (commit,
3619 [('A', 'addremove', None,
3736 [('A', 'addremove', None,
3620 _('mark new/missing files as added/removed before committing')),
3737 _('mark new/missing files as added/removed before committing')),
3621 ('', 'close-branch', None,
3738 ('', 'close-branch', None,
3622 _('mark a branch as closed, hiding it from the branch list')),
3739 _('mark a branch as closed, hiding it from the branch list')),
3623 ] + walkopts + commitopts + commitopts2,
3740 ] + walkopts + commitopts + commitopts2,
3624 _('[OPTION]... [FILE]...')),
3741 _('[OPTION]... [FILE]...')),
3625 "copy|cp":
3742 "copy|cp":
3626 (copy,
3743 (copy,
3627 [('A', 'after', None, _('record a copy that has already occurred')),
3744 [('A', 'after', None, _('record a copy that has already occurred')),
3628 ('f', 'force', None,
3745 ('f', 'force', None,
3629 _('forcibly copy over an existing managed file')),
3746 _('forcibly copy over an existing managed file')),
3630 ] + walkopts + dryrunopts,
3747 ] + walkopts + dryrunopts,
3631 _('[OPTION]... [SOURCE]... DEST')),
3748 _('[OPTION]... [SOURCE]... DEST')),
3632 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3749 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3633 "debugcheckstate": (debugcheckstate, [], ''),
3750 "debugcheckstate": (debugcheckstate, [], ''),
3634 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3751 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3635 "debugcomplete":
3752 "debugcomplete":
3636 (debugcomplete,
3753 (debugcomplete,
3637 [('o', 'options', None, _('show the command options'))],
3754 [('o', 'options', None, _('show the command options'))],
3638 _('[-o] CMD')),
3755 _('[-o] CMD')),
3639 "debugdate":
3756 "debugdate":
3640 (debugdate,
3757 (debugdate,
3641 [('e', 'extended', None, _('try extended date formats'))],
3758 [('e', 'extended', None, _('try extended date formats'))],
3642 _('[-e] DATE [RANGE]')),
3759 _('[-e] DATE [RANGE]')),
3643 "debugdata": (debugdata, [], _('FILE REV')),
3760 "debugdata": (debugdata, [], _('FILE REV')),
3644 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3761 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3645 "debugindex": (debugindex, [], _('FILE')),
3762 "debugindex": (debugindex, [], _('FILE')),
3646 "debugindexdot": (debugindexdot, [], _('FILE')),
3763 "debugindexdot": (debugindexdot, [], _('FILE')),
3647 "debuginstall": (debuginstall, [], ''),
3764 "debuginstall": (debuginstall, [], ''),
3648 "debugrebuildstate":
3765 "debugrebuildstate":
3649 (debugrebuildstate,
3766 (debugrebuildstate,
3650 [('r', 'rev', '', _('revision to rebuild to'))],
3767 [('r', 'rev', '', _('revision to rebuild to'))],
3651 _('[-r REV] [REV]')),
3768 _('[-r REV] [REV]')),
3652 "debugrename":
3769 "debugrename":
3653 (debugrename,
3770 (debugrename,
3654 [('r', 'rev', '', _('revision to debug'))],
3771 [('r', 'rev', '', _('revision to debug'))],
3655 _('[-r REV] FILE')),
3772 _('[-r REV] FILE')),
3656 "debugsetparents":
3773 "debugsetparents":
3657 (debugsetparents, [], _('REV1 [REV2]')),
3774 (debugsetparents, [], _('REV1 [REV2]')),
3658 "debugstate":
3775 "debugstate":
3659 (debugstate,
3776 (debugstate,
3660 [('', 'nodates', None, _('do not display the saved mtime'))],
3777 [('', 'nodates', None, _('do not display the saved mtime'))],
3661 _('[OPTION]...')),
3778 _('[OPTION]...')),
3662 "debugsub":
3779 "debugsub":
3663 (debugsub,
3780 (debugsub,
3664 [('r', 'rev', '', _('revision to check'))],
3781 [('r', 'rev', '', _('revision to check'))],
3665 _('[-r REV] [REV]')),
3782 _('[-r REV] [REV]')),
3666 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3783 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3667 "^diff":
3784 "^diff":
3668 (diff,
3785 (diff,
3669 [('r', 'rev', [], _('revision')),
3786 [('r', 'rev', [], _('revision')),
3670 ('c', 'change', '', _('change made by revision'))
3787 ('c', 'change', '', _('change made by revision'))
3671 ] + diffopts + diffopts2 + walkopts,
3788 ] + diffopts + diffopts2 + walkopts,
3672 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
3789 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
3673 "^export":
3790 "^export":
3674 (export,
3791 (export,
3675 [('o', 'output', '', _('print output to file with formatted name')),
3792 [('o', 'output', '', _('print output to file with formatted name')),
3676 ('', 'switch-parent', None, _('diff against the second parent')),
3793 ('', 'switch-parent', None, _('diff against the second parent')),
3677 ('r', 'rev', [], _('revisions to export')),
3794 ('r', 'rev', [], _('revisions to export')),
3678 ] + diffopts,
3795 ] + diffopts,
3679 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3796 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3680 "^forget":
3797 "^forget":
3681 (forget,
3798 (forget,
3682 [] + walkopts,
3799 [] + walkopts,
3683 _('[OPTION]... FILE...')),
3800 _('[OPTION]... FILE...')),
3684 "grep":
3801 "grep":
3685 (grep,
3802 (grep,
3686 [('0', 'print0', None, _('end fields with NUL')),
3803 [('0', 'print0', None, _('end fields with NUL')),
3687 ('', 'all', None, _('print all revisions that match')),
3804 ('', 'all', None, _('print all revisions that match')),
3688 ('f', 'follow', None,
3805 ('f', 'follow', None,
3689 _('follow changeset history,'
3806 _('follow changeset history,'
3690 ' or file history across copies and renames')),
3807 ' or file history across copies and renames')),
3691 ('i', 'ignore-case', None, _('ignore case when matching')),
3808 ('i', 'ignore-case', None, _('ignore case when matching')),
3692 ('l', 'files-with-matches', None,
3809 ('l', 'files-with-matches', None,
3693 _('print only filenames and revisions that match')),
3810 _('print only filenames and revisions that match')),
3694 ('n', 'line-number', None, _('print matching line numbers')),
3811 ('n', 'line-number', None, _('print matching line numbers')),
3695 ('r', 'rev', [], _('only search files changed within revision range')),
3812 ('r', 'rev', [], _('only search files changed within revision range')),
3696 ('u', 'user', None, _('list the author (long with -v)')),
3813 ('u', 'user', None, _('list the author (long with -v)')),
3697 ('d', 'date', None, _('list the date (short with -q)')),
3814 ('d', 'date', None, _('list the date (short with -q)')),
3698 ] + walkopts,
3815 ] + walkopts,
3699 _('[OPTION]... PATTERN [FILE]...')),
3816 _('[OPTION]... PATTERN [FILE]...')),
3700 "heads":
3817 "heads":
3701 (heads,
3818 (heads,
3702 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3819 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3703 ('t', 'topo', False, _('show topological heads only')),
3820 ('t', 'topo', False, _('show topological heads only')),
3704 ('a', 'active', False,
3821 ('a', 'active', False,
3705 _('show active branchheads only [DEPRECATED]')),
3822 _('show active branchheads only [DEPRECATED]')),
3706 ('c', 'closed', False,
3823 ('c', 'closed', False,
3707 _('show normal and closed branch heads')),
3824 _('show normal and closed branch heads')),
3708 ] + templateopts,
3825 ] + templateopts,
3709 _('[-ac] [-r STARTREV] [REV]...')),
3826 _('[-ac] [-r STARTREV] [REV]...')),
3710 "help": (help_, [], _('[TOPIC]')),
3827 "help": (help_, [], _('[TOPIC]')),
3711 "identify|id":
3828 "identify|id":
3712 (identify,
3829 (identify,
3713 [('r', 'rev', '', _('identify the specified revision')),
3830 [('r', 'rev', '', _('identify the specified revision')),
3714 ('n', 'num', None, _('show local revision number')),
3831 ('n', 'num', None, _('show local revision number')),
3715 ('i', 'id', None, _('show global revision id')),
3832 ('i', 'id', None, _('show global revision id')),
3716 ('b', 'branch', None, _('show branch')),
3833 ('b', 'branch', None, _('show branch')),
3717 ('t', 'tags', None, _('show tags'))],
3834 ('t', 'tags', None, _('show tags'))],
3718 _('[-nibt] [-r REV] [SOURCE]')),
3835 _('[-nibt] [-r REV] [SOURCE]')),
3719 "import|patch":
3836 "import|patch":
3720 (import_,
3837 (import_,
3721 [('p', 'strip', 1,
3838 [('p', 'strip', 1,
3722 _('directory strip option for patch. This has the same '
3839 _('directory strip option for patch. This has the same '
3723 'meaning as the corresponding patch option')),
3840 'meaning as the corresponding patch option')),
3724 ('b', 'base', '', _('base path')),
3841 ('b', 'base', '', _('base path')),
3725 ('f', 'force', None,
3842 ('f', 'force', None,
3726 _('skip check for outstanding uncommitted changes')),
3843 _('skip check for outstanding uncommitted changes')),
3727 ('', 'no-commit', None,
3844 ('', 'no-commit', None,
3728 _("don't commit, just update the working directory")),
3845 _("don't commit, just update the working directory")),
3729 ('', 'exact', None,
3846 ('', 'exact', None,
3730 _('apply patch to the nodes from which it was generated')),
3847 _('apply patch to the nodes from which it was generated')),
3731 ('', 'import-branch', None,
3848 ('', 'import-branch', None,
3732 _('use any branch information in patch (implied by --exact)'))] +
3849 _('use any branch information in patch (implied by --exact)'))] +
3733 commitopts + commitopts2 + similarityopts,
3850 commitopts + commitopts2 + similarityopts,
3734 _('[OPTION]... PATCH...')),
3851 _('[OPTION]... PATCH...')),
3735 "incoming|in":
3852 "incoming|in":
3736 (incoming,
3853 (incoming,
3737 [('f', 'force', None,
3854 [('f', 'force', None,
3738 _('run even if remote repository is unrelated')),
3855 _('run even if remote repository is unrelated')),
3739 ('n', 'newest-first', None, _('show newest record first')),
3856 ('n', 'newest-first', None, _('show newest record first')),
3740 ('', 'bundle', '', _('file to store the bundles into')),
3857 ('', 'bundle', '', _('file to store the bundles into')),
3741 ('r', 'rev', [],
3858 ('r', 'rev', [],
3742 _('a remote changeset intended to be added')),
3859 _('a remote changeset intended to be added')),
3743 ('b', 'branch', [],
3860 ('b', 'branch', [],
3744 _('a specific branch you would like to pull')),
3861 _('a specific branch you would like to pull')),
3745 ] + logopts + remoteopts,
3862 ] + logopts + remoteopts,
3746 _('[-p] [-n] [-M] [-f] [-r REV]...'
3863 _('[-p] [-n] [-M] [-f] [-r REV]...'
3747 ' [--bundle FILENAME] [SOURCE]')),
3864 ' [--bundle FILENAME] [SOURCE]')),
3748 "^init":
3865 "^init":
3749 (init,
3866 (init,
3750 remoteopts,
3867 remoteopts,
3751 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3868 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3752 "locate":
3869 "locate":
3753 (locate,
3870 (locate,
3754 [('r', 'rev', '', _('search the repository as it is in REV')),
3871 [('r', 'rev', '', _('search the repository as it is in REV')),
3755 ('0', 'print0', None,
3872 ('0', 'print0', None,
3756 _('end filenames with NUL, for use with xargs')),
3873 _('end filenames with NUL, for use with xargs')),
3757 ('f', 'fullpath', None,
3874 ('f', 'fullpath', None,
3758 _('print complete paths from the filesystem root')),
3875 _('print complete paths from the filesystem root')),
3759 ] + walkopts,
3876 ] + walkopts,
3760 _('[OPTION]... [PATTERN]...')),
3877 _('[OPTION]... [PATTERN]...')),
3761 "^log|history":
3878 "^log|history":
3762 (log,
3879 (log,
3763 [('f', 'follow', None,
3880 [('f', 'follow', None,
3764 _('follow changeset history,'
3881 _('follow changeset history,'
3765 ' or file history across copies and renames')),
3882 ' or file history across copies and renames')),
3766 ('', 'follow-first', None,
3883 ('', 'follow-first', None,
3767 _('only follow the first parent of merge changesets')),
3884 _('only follow the first parent of merge changesets')),
3768 ('d', 'date', '', _('show revisions matching date spec')),
3885 ('d', 'date', '', _('show revisions matching date spec')),
3769 ('C', 'copies', None, _('show copied files')),
3886 ('C', 'copies', None, _('show copied files')),
3770 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3887 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3771 ('r', 'rev', [], _('show the specified revision or range')),
3888 ('r', 'rev', [], _('show the specified revision or range')),
3772 ('', 'removed', None, _('include revisions where files were removed')),
3889 ('', 'removed', None, _('include revisions where files were removed')),
3773 ('m', 'only-merges', None, _('show only merges')),
3890 ('m', 'only-merges', None, _('show only merges')),
3774 ('u', 'user', [], _('revisions committed by user')),
3891 ('u', 'user', [], _('revisions committed by user')),
3775 ('', 'only-branch', [],
3892 ('', 'only-branch', [],
3776 _('show only changesets within the given named branch (DEPRECATED)')),
3893 _('show only changesets within the given named branch (DEPRECATED)')),
3777 ('b', 'branch', [],
3894 ('b', 'branch', [],
3778 _('show changesets within the given named branch')),
3895 _('show changesets within the given named branch')),
3779 ('P', 'prune', [],
3896 ('P', 'prune', [],
3780 _('do not display revision or any of its ancestors')),
3897 _('do not display revision or any of its ancestors')),
3781 ] + logopts + walkopts,
3898 ] + logopts + walkopts,
3782 _('[OPTION]... [FILE]')),
3899 _('[OPTION]... [FILE]')),
3783 "manifest":
3900 "manifest":
3784 (manifest,
3901 (manifest,
3785 [('r', 'rev', '', _('revision to display'))],
3902 [('r', 'rev', '', _('revision to display'))],
3786 _('[-r REV]')),
3903 _('[-r REV]')),
3787 "^merge":
3904 "^merge":
3788 (merge,
3905 (merge,
3789 [('f', 'force', None, _('force a merge with outstanding changes')),
3906 [('f', 'force', None, _('force a merge with outstanding changes')),
3790 ('r', 'rev', '', _('revision to merge')),
3907 ('r', 'rev', '', _('revision to merge')),
3791 ('P', 'preview', None,
3908 ('P', 'preview', None,
3792 _('review revisions to merge (no merge is performed)'))],
3909 _('review revisions to merge (no merge is performed)'))],
3793 _('[-P] [-f] [[-r] REV]')),
3910 _('[-P] [-f] [[-r] REV]')),
3794 "outgoing|out":
3911 "outgoing|out":
3795 (outgoing,
3912 (outgoing,
3796 [('f', 'force', None,
3913 [('f', 'force', None,
3797 _('run even when the destination is unrelated')),
3914 _('run even when the destination is unrelated')),
3798 ('r', 'rev', [],
3915 ('r', 'rev', [],
3799 _('a changeset intended to be included in the destination')),
3916 _('a changeset intended to be included in the destination')),
3800 ('n', 'newest-first', None, _('show newest record first')),
3917 ('n', 'newest-first', None, _('show newest record first')),
3801 ('b', 'branch', [],
3918 ('b', 'branch', [],
3802 _('a specific branch you would like to push')),
3919 _('a specific branch you would like to push')),
3803 ] + logopts + remoteopts,
3920 ] + logopts + remoteopts,
3804 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3921 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3805 "parents":
3922 "parents":
3806 (parents,
3923 (parents,
3807 [('r', 'rev', '', _('show parents of the specified revision')),
3924 [('r', 'rev', '', _('show parents of the specified revision')),
3808 ] + templateopts,
3925 ] + templateopts,
3809 _('[-r REV] [FILE]')),
3926 _('[-r REV] [FILE]')),
3810 "paths": (paths, [], _('[NAME]')),
3927 "paths": (paths, [], _('[NAME]')),
3811 "^pull":
3928 "^pull":
3812 (pull,
3929 (pull,
3813 [('u', 'update', None,
3930 [('u', 'update', None,
3814 _('update to new branch head if changesets were pulled')),
3931 _('update to new branch head if changesets were pulled')),
3815 ('f', 'force', None,
3932 ('f', 'force', None,
3816 _('run even when remote repository is unrelated')),
3933 _('run even when remote repository is unrelated')),
3817 ('r', 'rev', [],
3934 ('r', 'rev', [],
3818 _('a remote changeset intended to be added')),
3935 _('a remote changeset intended to be added')),
3819 ('b', 'branch', [],
3936 ('b', 'branch', [],
3820 _('a specific branch you would like to pull')),
3937 _('a specific branch you would like to pull')),
3821 ] + remoteopts,
3938 ] + remoteopts,
3822 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3939 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3823 "^push":
3940 "^push":
3824 (push,
3941 (push,
3825 [('f', 'force', None, _('force push')),
3942 [('f', 'force', None, _('force push')),
3826 ('r', 'rev', [],
3943 ('r', 'rev', [],
3827 _('a changeset intended to be included in the destination')),
3944 _('a changeset intended to be included in the destination')),
3828 ('b', 'branch', [],
3945 ('b', 'branch', [],
3829 _('a specific branch you would like to push')),
3946 _('a specific branch you would like to push')),
3830 ] + remoteopts,
3947 ] + remoteopts,
3831 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3948 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3832 "recover": (recover, []),
3949 "recover": (recover, []),
3833 "^remove|rm":
3950 "^remove|rm":
3834 (remove,
3951 (remove,
3835 [('A', 'after', None, _('record delete for missing files')),
3952 [('A', 'after', None, _('record delete for missing files')),
3836 ('f', 'force', None,
3953 ('f', 'force', None,
3837 _('remove (and delete) file even if added or modified')),
3954 _('remove (and delete) file even if added or modified')),
3838 ] + walkopts,
3955 ] + walkopts,
3839 _('[OPTION]... FILE...')),
3956 _('[OPTION]... FILE...')),
3840 "rename|mv":
3957 "rename|mv":
3841 (rename,
3958 (rename,
3842 [('A', 'after', None, _('record a rename that has already occurred')),
3959 [('A', 'after', None, _('record a rename that has already occurred')),
3843 ('f', 'force', None,
3960 ('f', 'force', None,
3844 _('forcibly copy over an existing managed file')),
3961 _('forcibly copy over an existing managed file')),
3845 ] + walkopts + dryrunopts,
3962 ] + walkopts + dryrunopts,
3846 _('[OPTION]... SOURCE... DEST')),
3963 _('[OPTION]... SOURCE... DEST')),
3847 "resolve":
3964 "resolve":
3848 (resolve,
3965 (resolve,
3849 [('a', 'all', None, _('select all unresolved files')),
3966 [('a', 'all', None, _('select all unresolved files')),
3850 ('l', 'list', None, _('list state of files needing merge')),
3967 ('l', 'list', None, _('list state of files needing merge')),
3851 ('m', 'mark', None, _('mark files as resolved')),
3968 ('m', 'mark', None, _('mark files as resolved')),
3852 ('u', 'unmark', None, _('unmark files as resolved')),
3969 ('u', 'unmark', None, _('unmark files as resolved')),
3853 ('n', 'no-status', None, _('hide status prefix'))]
3970 ('n', 'no-status', None, _('hide status prefix'))]
3854 + walkopts,
3971 + walkopts,
3855 _('[OPTION]... [FILE]...')),
3972 _('[OPTION]... [FILE]...')),
3856 "revert":
3973 "revert":
3857 (revert,
3974 (revert,
3858 [('a', 'all', None, _('revert all changes when no arguments given')),
3975 [('a', 'all', None, _('revert all changes when no arguments given')),
3859 ('d', 'date', '', _('tipmost revision matching date')),
3976 ('d', 'date', '', _('tipmost revision matching date')),
3860 ('r', 'rev', '', _('revert to the specified revision')),
3977 ('r', 'rev', '', _('revert to the specified revision')),
3861 ('', 'no-backup', None, _('do not save backup copies of files')),
3978 ('', 'no-backup', None, _('do not save backup copies of files')),
3862 ] + walkopts + dryrunopts,
3979 ] + walkopts + dryrunopts,
3863 _('[OPTION]... [-r REV] [NAME]...')),
3980 _('[OPTION]... [-r REV] [NAME]...')),
3864 "rollback": (rollback, dryrunopts),
3981 "rollback": (rollback, dryrunopts),
3865 "root": (root, []),
3982 "root": (root, []),
3866 "^serve":
3983 "^serve":
3867 (serve,
3984 (serve,
3868 [('A', 'accesslog', '', _('name of access log file to write to')),
3985 [('A', 'accesslog', '', _('name of access log file to write to')),
3869 ('d', 'daemon', None, _('run server in background')),
3986 ('d', 'daemon', None, _('run server in background')),
3870 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3987 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3871 ('E', 'errorlog', '', _('name of error log file to write to')),
3988 ('E', 'errorlog', '', _('name of error log file to write to')),
3872 # use string type, then we can check if something was passed
3989 # use string type, then we can check if something was passed
3873 ('p', 'port', '', _('port to listen on (default: 8000)')),
3990 ('p', 'port', '', _('port to listen on (default: 8000)')),
3874 ('a', 'address', '',
3991 ('a', 'address', '',
3875 _('address to listen on (default: all interfaces)')),
3992 _('address to listen on (default: all interfaces)')),
3876 ('', 'prefix', '',
3993 ('', 'prefix', '',
3877 _('prefix path to serve from (default: server root)')),
3994 _('prefix path to serve from (default: server root)')),
3878 ('n', 'name', '',
3995 ('n', 'name', '',
3879 _('name to show in web pages (default: working directory)')),
3996 _('name to show in web pages (default: working directory)')),
3880 ('', 'web-conf', '', _('name of the hgweb config file'
3997 ('', 'web-conf', '', _('name of the hgweb config file'
3881 ' (serve more than one repository)')),
3998 ' (serve more than one repository)')),
3882 ('', 'webdir-conf', '', _('name of the hgweb config file'
3999 ('', 'webdir-conf', '', _('name of the hgweb config file'
3883 ' (DEPRECATED)')),
4000 ' (DEPRECATED)')),
3884 ('', 'pid-file', '', _('name of file to write process ID to')),
4001 ('', 'pid-file', '', _('name of file to write process ID to')),
3885 ('', 'stdio', None, _('for remote clients')),
4002 ('', 'stdio', None, _('for remote clients')),
3886 ('t', 'templates', '', _('web templates to use')),
4003 ('t', 'templates', '', _('web templates to use')),
3887 ('', 'style', '', _('template style to use')),
4004 ('', 'style', '', _('template style to use')),
3888 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4005 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3889 ('', 'certificate', '', _('SSL certificate file'))],
4006 ('', 'certificate', '', _('SSL certificate file'))],
3890 _('[OPTION]...')),
4007 _('[OPTION]...')),
3891 "showconfig|debugconfig":
4008 "showconfig|debugconfig":
3892 (showconfig,
4009 (showconfig,
3893 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4010 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3894 _('[-u] [NAME]...')),
4011 _('[-u] [NAME]...')),
3895 "^summary|sum":
4012 "^summary|sum":
3896 (summary,
4013 (summary,
3897 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4014 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3898 "^status|st":
4015 "^status|st":
3899 (status,
4016 (status,
3900 [('A', 'all', None, _('show status of all files')),
4017 [('A', 'all', None, _('show status of all files')),
3901 ('m', 'modified', None, _('show only modified files')),
4018 ('m', 'modified', None, _('show only modified files')),
3902 ('a', 'added', None, _('show only added files')),
4019 ('a', 'added', None, _('show only added files')),
3903 ('r', 'removed', None, _('show only removed files')),
4020 ('r', 'removed', None, _('show only removed files')),
3904 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4021 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3905 ('c', 'clean', None, _('show only files without changes')),
4022 ('c', 'clean', None, _('show only files without changes')),
3906 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4023 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3907 ('i', 'ignored', None, _('show only ignored files')),
4024 ('i', 'ignored', None, _('show only ignored files')),
3908 ('n', 'no-status', None, _('hide status prefix')),
4025 ('n', 'no-status', None, _('hide status prefix')),
3909 ('C', 'copies', None, _('show source of copied files')),
4026 ('C', 'copies', None, _('show source of copied files')),
3910 ('0', 'print0', None,
4027 ('0', 'print0', None,
3911 _('end filenames with NUL, for use with xargs')),
4028 _('end filenames with NUL, for use with xargs')),
3912 ('', 'rev', [], _('show difference from revision')),
4029 ('', 'rev', [], _('show difference from revision')),
3913 ('', 'change', '', _('list the changed files of a revision')),
4030 ('', 'change', '', _('list the changed files of a revision')),
3914 ] + walkopts,
4031 ] + walkopts,
3915 _('[OPTION]... [FILE]...')),
4032 _('[OPTION]... [FILE]...')),
3916 "tag":
4033 "tag":
3917 (tag,
4034 (tag,
3918 [('f', 'force', None, _('replace existing tag')),
4035 [('f', 'force', None, _('replace existing tag')),
3919 ('l', 'local', None, _('make the tag local')),
4036 ('l', 'local', None, _('make the tag local')),
3920 ('r', 'rev', '', _('revision to tag')),
4037 ('r', 'rev', '', _('revision to tag')),
3921 ('', 'remove', None, _('remove a tag')),
4038 ('', 'remove', None, _('remove a tag')),
3922 # -l/--local is already there, commitopts cannot be used
4039 # -l/--local is already there, commitopts cannot be used
3923 ('m', 'message', '', _('use <text> as commit message')),
4040 ('m', 'message', '', _('use <text> as commit message')),
3924 ] + commitopts2,
4041 ] + commitopts2,
3925 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4042 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3926 "tags": (tags, [], ''),
4043 "tags": (tags, [], ''),
3927 "tip":
4044 "tip":
3928 (tip,
4045 (tip,
3929 [('p', 'patch', None, _('show patch')),
4046 [('p', 'patch', None, _('show patch')),
3930 ('g', 'git', None, _('use git extended diff format')),
4047 ('g', 'git', None, _('use git extended diff format')),
3931 ] + templateopts,
4048 ] + templateopts,
3932 _('[-p] [-g]')),
4049 _('[-p] [-g]')),
3933 "unbundle":
4050 "unbundle":
3934 (unbundle,
4051 (unbundle,
3935 [('u', 'update', None,
4052 [('u', 'update', None,
3936 _('update to new branch head if changesets were unbundled'))],
4053 _('update to new branch head if changesets were unbundled'))],
3937 _('[-u] FILE...')),
4054 _('[-u] FILE...')),
3938 "^update|up|checkout|co":
4055 "^update|up|checkout|co":
3939 (update,
4056 (update,
3940 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4057 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3941 ('c', 'check', None, _('check for uncommitted changes')),
4058 ('c', 'check', None, _('check for uncommitted changes')),
3942 ('d', 'date', '', _('tipmost revision matching date')),
4059 ('d', 'date', '', _('tipmost revision matching date')),
3943 ('r', 'rev', '', _('revision'))],
4060 ('r', 'rev', '', _('revision'))],
3944 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4061 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3945 "verify": (verify, []),
4062 "verify": (verify, []),
3946 "version": (version_, []),
4063 "version": (version_, []),
3947 }
4064 }
3948
4065
3949 norepo = ("clone init version help debugcommands debugcomplete debugdata"
4066 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3950 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
4067 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3951 optionalrepo = ("identify paths serve showconfig debugancestor")
4068 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,2291 +1,2292 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import bin, hex, nullid, nullrev, short
8 from node import bin, hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import repo, changegroup, subrepo
10 import repo, changegroup, subrepo
11 import changelog, dirstate, filelog, manifest, context
11 import changelog, dirstate, filelog, manifest, context
12 import lock, transaction, store, encoding
12 import lock, transaction, store, encoding
13 import util, extensions, hook, error
13 import util, extensions, hook, error
14 import match as matchmod
14 import match as matchmod
15 import merge as mergemod
15 import merge as mergemod
16 import tags as tagsmod
16 import tags as tagsmod
17 import url as urlmod
17 import url as urlmod
18 from lock import release
18 from lock import release
19 import weakref, stat, errno, os, time, inspect
19 import weakref, stat, errno, os, time, inspect
20 propertycache = util.propertycache
20 propertycache = util.propertycache
21
21
22 class localrepository(repo.repository):
22 class localrepository(repo.repository):
23 capabilities = set(('lookup', 'changegroupsubset', 'branchmap'))
23 capabilities = set(('lookup', 'changegroupsubset', 'branchmap'))
24 supported = set('revlogv1 store fncache shared'.split())
24 supported = set('revlogv1 store fncache shared'.split())
25
25
26 def __init__(self, baseui, path=None, create=0):
26 def __init__(self, baseui, path=None, create=0):
27 repo.repository.__init__(self)
27 repo.repository.__init__(self)
28 self.root = os.path.realpath(util.expandpath(path))
28 self.root = os.path.realpath(util.expandpath(path))
29 self.path = os.path.join(self.root, ".hg")
29 self.path = os.path.join(self.root, ".hg")
30 self.origroot = path
30 self.origroot = path
31 self.opener = util.opener(self.path)
31 self.opener = util.opener(self.path)
32 self.wopener = util.opener(self.root)
32 self.wopener = util.opener(self.root)
33 self.baseui = baseui
33 self.baseui = baseui
34 self.ui = baseui.copy()
34 self.ui = baseui.copy()
35
35
36 try:
36 try:
37 self.ui.readconfig(self.join("hgrc"), self.root)
37 self.ui.readconfig(self.join("hgrc"), self.root)
38 extensions.loadall(self.ui)
38 extensions.loadall(self.ui)
39 except IOError:
39 except IOError:
40 pass
40 pass
41
41
42 if not os.path.isdir(self.path):
42 if not os.path.isdir(self.path):
43 if create:
43 if create:
44 if not os.path.exists(path):
44 if not os.path.exists(path):
45 os.mkdir(path)
45 os.mkdir(path)
46 os.mkdir(self.path)
46 os.mkdir(self.path)
47 requirements = ["revlogv1"]
47 requirements = ["revlogv1"]
48 if self.ui.configbool('format', 'usestore', True):
48 if self.ui.configbool('format', 'usestore', True):
49 os.mkdir(os.path.join(self.path, "store"))
49 os.mkdir(os.path.join(self.path, "store"))
50 requirements.append("store")
50 requirements.append("store")
51 if self.ui.configbool('format', 'usefncache', True):
51 if self.ui.configbool('format', 'usefncache', True):
52 requirements.append("fncache")
52 requirements.append("fncache")
53 # create an invalid changelog
53 # create an invalid changelog
54 self.opener("00changelog.i", "a").write(
54 self.opener("00changelog.i", "a").write(
55 '\0\0\0\2' # represents revlogv2
55 '\0\0\0\2' # represents revlogv2
56 ' dummy changelog to prevent using the old repo layout'
56 ' dummy changelog to prevent using the old repo layout'
57 )
57 )
58 reqfile = self.opener("requires", "w")
58 reqfile = self.opener("requires", "w")
59 for r in requirements:
59 for r in requirements:
60 reqfile.write("%s\n" % r)
60 reqfile.write("%s\n" % r)
61 reqfile.close()
61 reqfile.close()
62 else:
62 else:
63 raise error.RepoError(_("repository %s not found") % path)
63 raise error.RepoError(_("repository %s not found") % path)
64 elif create:
64 elif create:
65 raise error.RepoError(_("repository %s already exists") % path)
65 raise error.RepoError(_("repository %s already exists") % path)
66 else:
66 else:
67 # find requirements
67 # find requirements
68 requirements = set()
68 requirements = set()
69 try:
69 try:
70 requirements = set(self.opener("requires").read().splitlines())
70 requirements = set(self.opener("requires").read().splitlines())
71 except IOError, inst:
71 except IOError, inst:
72 if inst.errno != errno.ENOENT:
72 if inst.errno != errno.ENOENT:
73 raise
73 raise
74 for r in requirements - self.supported:
74 for r in requirements - self.supported:
75 raise error.RepoError(_("requirement '%s' not supported") % r)
75 raise error.RepoError(_("requirement '%s' not supported") % r)
76
76
77 self.sharedpath = self.path
77 self.sharedpath = self.path
78 try:
78 try:
79 s = os.path.realpath(self.opener("sharedpath").read())
79 s = os.path.realpath(self.opener("sharedpath").read())
80 if not os.path.exists(s):
80 if not os.path.exists(s):
81 raise error.RepoError(
81 raise error.RepoError(
82 _('.hg/sharedpath points to nonexistent directory %s') % s)
82 _('.hg/sharedpath points to nonexistent directory %s') % s)
83 self.sharedpath = s
83 self.sharedpath = s
84 except IOError, inst:
84 except IOError, inst:
85 if inst.errno != errno.ENOENT:
85 if inst.errno != errno.ENOENT:
86 raise
86 raise
87
87
88 self.store = store.store(requirements, self.sharedpath, util.opener)
88 self.store = store.store(requirements, self.sharedpath, util.opener)
89 self.spath = self.store.path
89 self.spath = self.store.path
90 self.sopener = self.store.opener
90 self.sopener = self.store.opener
91 self.sjoin = self.store.join
91 self.sjoin = self.store.join
92 self.opener.createmode = self.store.createmode
92 self.opener.createmode = self.store.createmode
93 self.sopener.options = {}
93 self.sopener.options = {}
94
94
95 # These two define the set of tags for this repository. _tags
95 # These two define the set of tags for this repository. _tags
96 # maps tag name to node; _tagtypes maps tag name to 'global' or
96 # maps tag name to node; _tagtypes maps tag name to 'global' or
97 # 'local'. (Global tags are defined by .hgtags across all
97 # 'local'. (Global tags are defined by .hgtags across all
98 # heads, and local tags are defined in .hg/localtags.) They
98 # heads, and local tags are defined in .hg/localtags.) They
99 # constitute the in-memory cache of tags.
99 # constitute the in-memory cache of tags.
100 self._tags = None
100 self._tags = None
101 self._tagtypes = None
101 self._tagtypes = None
102
102
103 self._branchcache = None # in UTF-8
103 self._branchcache = None # in UTF-8
104 self._branchcachetip = None
104 self._branchcachetip = None
105 self.nodetagscache = None
105 self.nodetagscache = None
106 self.filterpats = {}
106 self.filterpats = {}
107 self._datafilters = {}
107 self._datafilters = {}
108 self._transref = self._lockref = self._wlockref = None
108 self._transref = self._lockref = self._wlockref = None
109
109
110 @propertycache
110 @propertycache
111 def changelog(self):
111 def changelog(self):
112 c = changelog.changelog(self.sopener)
112 c = changelog.changelog(self.sopener)
113 if 'HG_PENDING' in os.environ:
113 if 'HG_PENDING' in os.environ:
114 p = os.environ['HG_PENDING']
114 p = os.environ['HG_PENDING']
115 if p.startswith(self.root):
115 if p.startswith(self.root):
116 c.readpending('00changelog.i.a')
116 c.readpending('00changelog.i.a')
117 self.sopener.options['defversion'] = c.version
117 self.sopener.options['defversion'] = c.version
118 return c
118 return c
119
119
120 @propertycache
120 @propertycache
121 def manifest(self):
121 def manifest(self):
122 return manifest.manifest(self.sopener)
122 return manifest.manifest(self.sopener)
123
123
124 @propertycache
124 @propertycache
125 def dirstate(self):
125 def dirstate(self):
126 return dirstate.dirstate(self.opener, self.ui, self.root)
126 return dirstate.dirstate(self.opener, self.ui, self.root)
127
127
128 def __getitem__(self, changeid):
128 def __getitem__(self, changeid):
129 if changeid is None:
129 if changeid is None:
130 return context.workingctx(self)
130 return context.workingctx(self)
131 return context.changectx(self, changeid)
131 return context.changectx(self, changeid)
132
132
133 def __contains__(self, changeid):
133 def __contains__(self, changeid):
134 try:
134 try:
135 return bool(self.lookup(changeid))
135 return bool(self.lookup(changeid))
136 except error.RepoLookupError:
136 except error.RepoLookupError:
137 return False
137 return False
138
138
139 def __nonzero__(self):
139 def __nonzero__(self):
140 return True
140 return True
141
141
142 def __len__(self):
142 def __len__(self):
143 return len(self.changelog)
143 return len(self.changelog)
144
144
145 def __iter__(self):
145 def __iter__(self):
146 for i in xrange(len(self)):
146 for i in xrange(len(self)):
147 yield i
147 yield i
148
148
149 def url(self):
149 def url(self):
150 return 'file:' + self.root
150 return 'file:' + self.root
151
151
152 def hook(self, name, throw=False, **args):
152 def hook(self, name, throw=False, **args):
153 return hook.hook(self.ui, self, name, throw, **args)
153 return hook.hook(self.ui, self, name, throw, **args)
154
154
155 tag_disallowed = ':\r\n'
155 tag_disallowed = ':\r\n'
156
156
157 def _tag(self, names, node, message, local, user, date, extra={}):
157 def _tag(self, names, node, message, local, user, date, extra={}):
158 if isinstance(names, str):
158 if isinstance(names, str):
159 allchars = names
159 allchars = names
160 names = (names,)
160 names = (names,)
161 else:
161 else:
162 allchars = ''.join(names)
162 allchars = ''.join(names)
163 for c in self.tag_disallowed:
163 for c in self.tag_disallowed:
164 if c in allchars:
164 if c in allchars:
165 raise util.Abort(_('%r cannot be used in a tag name') % c)
165 raise util.Abort(_('%r cannot be used in a tag name') % c)
166
166
167 branches = self.branchmap()
167 branches = self.branchmap()
168 for name in names:
168 for name in names:
169 self.hook('pretag', throw=True, node=hex(node), tag=name,
169 self.hook('pretag', throw=True, node=hex(node), tag=name,
170 local=local)
170 local=local)
171 if name in branches:
171 if name in branches:
172 self.ui.warn(_("warning: tag %s conflicts with existing"
172 self.ui.warn(_("warning: tag %s conflicts with existing"
173 " branch name\n") % name)
173 " branch name\n") % name)
174
174
175 def writetags(fp, names, munge, prevtags):
175 def writetags(fp, names, munge, prevtags):
176 fp.seek(0, 2)
176 fp.seek(0, 2)
177 if prevtags and prevtags[-1] != '\n':
177 if prevtags and prevtags[-1] != '\n':
178 fp.write('\n')
178 fp.write('\n')
179 for name in names:
179 for name in names:
180 m = munge and munge(name) or name
180 m = munge and munge(name) or name
181 if self._tagtypes and name in self._tagtypes:
181 if self._tagtypes and name in self._tagtypes:
182 old = self._tags.get(name, nullid)
182 old = self._tags.get(name, nullid)
183 fp.write('%s %s\n' % (hex(old), m))
183 fp.write('%s %s\n' % (hex(old), m))
184 fp.write('%s %s\n' % (hex(node), m))
184 fp.write('%s %s\n' % (hex(node), m))
185 fp.close()
185 fp.close()
186
186
187 prevtags = ''
187 prevtags = ''
188 if local:
188 if local:
189 try:
189 try:
190 fp = self.opener('localtags', 'r+')
190 fp = self.opener('localtags', 'r+')
191 except IOError:
191 except IOError:
192 fp = self.opener('localtags', 'a')
192 fp = self.opener('localtags', 'a')
193 else:
193 else:
194 prevtags = fp.read()
194 prevtags = fp.read()
195
195
196 # local tags are stored in the current charset
196 # local tags are stored in the current charset
197 writetags(fp, names, None, prevtags)
197 writetags(fp, names, None, prevtags)
198 for name in names:
198 for name in names:
199 self.hook('tag', node=hex(node), tag=name, local=local)
199 self.hook('tag', node=hex(node), tag=name, local=local)
200 return
200 return
201
201
202 try:
202 try:
203 fp = self.wfile('.hgtags', 'rb+')
203 fp = self.wfile('.hgtags', 'rb+')
204 except IOError:
204 except IOError:
205 fp = self.wfile('.hgtags', 'ab')
205 fp = self.wfile('.hgtags', 'ab')
206 else:
206 else:
207 prevtags = fp.read()
207 prevtags = fp.read()
208
208
209 # committed tags are stored in UTF-8
209 # committed tags are stored in UTF-8
210 writetags(fp, names, encoding.fromlocal, prevtags)
210 writetags(fp, names, encoding.fromlocal, prevtags)
211
211
212 if '.hgtags' not in self.dirstate:
212 if '.hgtags' not in self.dirstate:
213 self.add(['.hgtags'])
213 self.add(['.hgtags'])
214
214
215 m = matchmod.exact(self.root, '', ['.hgtags'])
215 m = matchmod.exact(self.root, '', ['.hgtags'])
216 tagnode = self.commit(message, user, date, extra=extra, match=m)
216 tagnode = self.commit(message, user, date, extra=extra, match=m)
217
217
218 for name in names:
218 for name in names:
219 self.hook('tag', node=hex(node), tag=name, local=local)
219 self.hook('tag', node=hex(node), tag=name, local=local)
220
220
221 return tagnode
221 return tagnode
222
222
223 def tag(self, names, node, message, local, user, date):
223 def tag(self, names, node, message, local, user, date):
224 '''tag a revision with one or more symbolic names.
224 '''tag a revision with one or more symbolic names.
225
225
226 names is a list of strings or, when adding a single tag, names may be a
226 names is a list of strings or, when adding a single tag, names may be a
227 string.
227 string.
228
228
229 if local is True, the tags are stored in a per-repository file.
229 if local is True, the tags are stored in a per-repository file.
230 otherwise, they are stored in the .hgtags file, and a new
230 otherwise, they are stored in the .hgtags file, and a new
231 changeset is committed with the change.
231 changeset is committed with the change.
232
232
233 keyword arguments:
233 keyword arguments:
234
234
235 local: whether to store tags in non-version-controlled file
235 local: whether to store tags in non-version-controlled file
236 (default False)
236 (default False)
237
237
238 message: commit message to use if committing
238 message: commit message to use if committing
239
239
240 user: name of user to use if committing
240 user: name of user to use if committing
241
241
242 date: date tuple to use if committing'''
242 date: date tuple to use if committing'''
243
243
244 for x in self.status()[:5]:
244 for x in self.status()[:5]:
245 if '.hgtags' in x:
245 if '.hgtags' in x:
246 raise util.Abort(_('working copy of .hgtags is changed '
246 raise util.Abort(_('working copy of .hgtags is changed '
247 '(please commit .hgtags manually)'))
247 '(please commit .hgtags manually)'))
248
248
249 self.tags() # instantiate the cache
249 self.tags() # instantiate the cache
250 self._tag(names, node, message, local, user, date)
250 self._tag(names, node, message, local, user, date)
251
251
252 def tags(self):
252 def tags(self):
253 '''return a mapping of tag to node'''
253 '''return a mapping of tag to node'''
254 if self._tags is None:
254 if self._tags is None:
255 (self._tags, self._tagtypes) = self._findtags()
255 (self._tags, self._tagtypes) = self._findtags()
256
256
257 return self._tags
257 return self._tags
258
258
259 def _findtags(self):
259 def _findtags(self):
260 '''Do the hard work of finding tags. Return a pair of dicts
260 '''Do the hard work of finding tags. Return a pair of dicts
261 (tags, tagtypes) where tags maps tag name to node, and tagtypes
261 (tags, tagtypes) where tags maps tag name to node, and tagtypes
262 maps tag name to a string like \'global\' or \'local\'.
262 maps tag name to a string like \'global\' or \'local\'.
263 Subclasses or extensions are free to add their own tags, but
263 Subclasses or extensions are free to add their own tags, but
264 should be aware that the returned dicts will be retained for the
264 should be aware that the returned dicts will be retained for the
265 duration of the localrepo object.'''
265 duration of the localrepo object.'''
266
266
267 # XXX what tagtype should subclasses/extensions use? Currently
267 # XXX what tagtype should subclasses/extensions use? Currently
268 # mq and bookmarks add tags, but do not set the tagtype at all.
268 # mq and bookmarks add tags, but do not set the tagtype at all.
269 # Should each extension invent its own tag type? Should there
269 # Should each extension invent its own tag type? Should there
270 # be one tagtype for all such "virtual" tags? Or is the status
270 # be one tagtype for all such "virtual" tags? Or is the status
271 # quo fine?
271 # quo fine?
272
272
273 alltags = {} # map tag name to (node, hist)
273 alltags = {} # map tag name to (node, hist)
274 tagtypes = {}
274 tagtypes = {}
275
275
276 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
276 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
277 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
277 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
278
278
279 # Build the return dicts. Have to re-encode tag names because
279 # Build the return dicts. Have to re-encode tag names because
280 # the tags module always uses UTF-8 (in order not to lose info
280 # the tags module always uses UTF-8 (in order not to lose info
281 # writing to the cache), but the rest of Mercurial wants them in
281 # writing to the cache), but the rest of Mercurial wants them in
282 # local encoding.
282 # local encoding.
283 tags = {}
283 tags = {}
284 for (name, (node, hist)) in alltags.iteritems():
284 for (name, (node, hist)) in alltags.iteritems():
285 if node != nullid:
285 if node != nullid:
286 tags[encoding.tolocal(name)] = node
286 tags[encoding.tolocal(name)] = node
287 tags['tip'] = self.changelog.tip()
287 tags['tip'] = self.changelog.tip()
288 tagtypes = dict([(encoding.tolocal(name), value)
288 tagtypes = dict([(encoding.tolocal(name), value)
289 for (name, value) in tagtypes.iteritems()])
289 for (name, value) in tagtypes.iteritems()])
290 return (tags, tagtypes)
290 return (tags, tagtypes)
291
291
292 def tagtype(self, tagname):
292 def tagtype(self, tagname):
293 '''
293 '''
294 return the type of the given tag. result can be:
294 return the type of the given tag. result can be:
295
295
296 'local' : a local tag
296 'local' : a local tag
297 'global' : a global tag
297 'global' : a global tag
298 None : tag does not exist
298 None : tag does not exist
299 '''
299 '''
300
300
301 self.tags()
301 self.tags()
302
302
303 return self._tagtypes.get(tagname)
303 return self._tagtypes.get(tagname)
304
304
305 def tagslist(self):
305 def tagslist(self):
306 '''return a list of tags ordered by revision'''
306 '''return a list of tags ordered by revision'''
307 l = []
307 l = []
308 for t, n in self.tags().iteritems():
308 for t, n in self.tags().iteritems():
309 try:
309 try:
310 r = self.changelog.rev(n)
310 r = self.changelog.rev(n)
311 except:
311 except:
312 r = -2 # sort to the beginning of the list if unknown
312 r = -2 # sort to the beginning of the list if unknown
313 l.append((r, t, n))
313 l.append((r, t, n))
314 return [(t, n) for r, t, n in sorted(l)]
314 return [(t, n) for r, t, n in sorted(l)]
315
315
316 def nodetags(self, node):
316 def nodetags(self, node):
317 '''return the tags associated with a node'''
317 '''return the tags associated with a node'''
318 if not self.nodetagscache:
318 if not self.nodetagscache:
319 self.nodetagscache = {}
319 self.nodetagscache = {}
320 for t, n in self.tags().iteritems():
320 for t, n in self.tags().iteritems():
321 self.nodetagscache.setdefault(n, []).append(t)
321 self.nodetagscache.setdefault(n, []).append(t)
322 for tags in self.nodetagscache.itervalues():
322 for tags in self.nodetagscache.itervalues():
323 tags.sort()
323 tags.sort()
324 return self.nodetagscache.get(node, [])
324 return self.nodetagscache.get(node, [])
325
325
326 def _branchtags(self, partial, lrev):
326 def _branchtags(self, partial, lrev):
327 # TODO: rename this function?
327 # TODO: rename this function?
328 tiprev = len(self) - 1
328 tiprev = len(self) - 1
329 if lrev != tiprev:
329 if lrev != tiprev:
330 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
330 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
331 self._updatebranchcache(partial, ctxgen)
331 self._updatebranchcache(partial, ctxgen)
332 self._writebranchcache(partial, self.changelog.tip(), tiprev)
332 self._writebranchcache(partial, self.changelog.tip(), tiprev)
333
333
334 return partial
334 return partial
335
335
336 def branchmap(self):
336 def branchmap(self):
337 '''returns a dictionary {branch: [branchheads]}'''
337 '''returns a dictionary {branch: [branchheads]}'''
338 tip = self.changelog.tip()
338 tip = self.changelog.tip()
339 if self._branchcache is not None and self._branchcachetip == tip:
339 if self._branchcache is not None and self._branchcachetip == tip:
340 return self._branchcache
340 return self._branchcache
341
341
342 oldtip = self._branchcachetip
342 oldtip = self._branchcachetip
343 self._branchcachetip = tip
343 self._branchcachetip = tip
344 if oldtip is None or oldtip not in self.changelog.nodemap:
344 if oldtip is None or oldtip not in self.changelog.nodemap:
345 partial, last, lrev = self._readbranchcache()
345 partial, last, lrev = self._readbranchcache()
346 else:
346 else:
347 lrev = self.changelog.rev(oldtip)
347 lrev = self.changelog.rev(oldtip)
348 partial = self._branchcache
348 partial = self._branchcache
349
349
350 self._branchtags(partial, lrev)
350 self._branchtags(partial, lrev)
351 # this private cache holds all heads (not just tips)
351 # this private cache holds all heads (not just tips)
352 self._branchcache = partial
352 self._branchcache = partial
353
353
354 return self._branchcache
354 return self._branchcache
355
355
356 def branchtags(self):
356 def branchtags(self):
357 '''return a dict where branch names map to the tipmost head of
357 '''return a dict where branch names map to the tipmost head of
358 the branch, open heads come before closed'''
358 the branch, open heads come before closed'''
359 bt = {}
359 bt = {}
360 for bn, heads in self.branchmap().iteritems():
360 for bn, heads in self.branchmap().iteritems():
361 tip = heads[-1]
361 tip = heads[-1]
362 for h in reversed(heads):
362 for h in reversed(heads):
363 if 'close' not in self.changelog.read(h)[5]:
363 if 'close' not in self.changelog.read(h)[5]:
364 tip = h
364 tip = h
365 break
365 break
366 bt[bn] = tip
366 bt[bn] = tip
367 return bt
367 return bt
368
368
369
369
370 def _readbranchcache(self):
370 def _readbranchcache(self):
371 partial = {}
371 partial = {}
372 try:
372 try:
373 f = self.opener("branchheads.cache")
373 f = self.opener("branchheads.cache")
374 lines = f.read().split('\n')
374 lines = f.read().split('\n')
375 f.close()
375 f.close()
376 except (IOError, OSError):
376 except (IOError, OSError):
377 return {}, nullid, nullrev
377 return {}, nullid, nullrev
378
378
379 try:
379 try:
380 last, lrev = lines.pop(0).split(" ", 1)
380 last, lrev = lines.pop(0).split(" ", 1)
381 last, lrev = bin(last), int(lrev)
381 last, lrev = bin(last), int(lrev)
382 if lrev >= len(self) or self[lrev].node() != last:
382 if lrev >= len(self) or self[lrev].node() != last:
383 # invalidate the cache
383 # invalidate the cache
384 raise ValueError('invalidating branch cache (tip differs)')
384 raise ValueError('invalidating branch cache (tip differs)')
385 for l in lines:
385 for l in lines:
386 if not l:
386 if not l:
387 continue
387 continue
388 node, label = l.split(" ", 1)
388 node, label = l.split(" ", 1)
389 partial.setdefault(label.strip(), []).append(bin(node))
389 partial.setdefault(label.strip(), []).append(bin(node))
390 except KeyboardInterrupt:
390 except KeyboardInterrupt:
391 raise
391 raise
392 except Exception, inst:
392 except Exception, inst:
393 if self.ui.debugflag:
393 if self.ui.debugflag:
394 self.ui.warn(str(inst), '\n')
394 self.ui.warn(str(inst), '\n')
395 partial, last, lrev = {}, nullid, nullrev
395 partial, last, lrev = {}, nullid, nullrev
396 return partial, last, lrev
396 return partial, last, lrev
397
397
398 def _writebranchcache(self, branches, tip, tiprev):
398 def _writebranchcache(self, branches, tip, tiprev):
399 try:
399 try:
400 f = self.opener("branchheads.cache", "w", atomictemp=True)
400 f = self.opener("branchheads.cache", "w", atomictemp=True)
401 f.write("%s %s\n" % (hex(tip), tiprev))
401 f.write("%s %s\n" % (hex(tip), tiprev))
402 for label, nodes in branches.iteritems():
402 for label, nodes in branches.iteritems():
403 for node in nodes:
403 for node in nodes:
404 f.write("%s %s\n" % (hex(node), label))
404 f.write("%s %s\n" % (hex(node), label))
405 f.rename()
405 f.rename()
406 except (IOError, OSError):
406 except (IOError, OSError):
407 pass
407 pass
408
408
409 def _updatebranchcache(self, partial, ctxgen):
409 def _updatebranchcache(self, partial, ctxgen):
410 # collect new branch entries
410 # collect new branch entries
411 newbranches = {}
411 newbranches = {}
412 for c in ctxgen:
412 for c in ctxgen:
413 newbranches.setdefault(c.branch(), []).append(c.node())
413 newbranches.setdefault(c.branch(), []).append(c.node())
414 # if older branchheads are reachable from new ones, they aren't
414 # if older branchheads are reachable from new ones, they aren't
415 # really branchheads. Note checking parents is insufficient:
415 # really branchheads. Note checking parents is insufficient:
416 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
416 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
417 for branch, newnodes in newbranches.iteritems():
417 for branch, newnodes in newbranches.iteritems():
418 bheads = partial.setdefault(branch, [])
418 bheads = partial.setdefault(branch, [])
419 bheads.extend(newnodes)
419 bheads.extend(newnodes)
420 if len(bheads) <= 1:
420 if len(bheads) <= 1:
421 continue
421 continue
422 # starting from tip means fewer passes over reachable
422 # starting from tip means fewer passes over reachable
423 while newnodes:
423 while newnodes:
424 latest = newnodes.pop()
424 latest = newnodes.pop()
425 if latest not in bheads:
425 if latest not in bheads:
426 continue
426 continue
427 minbhrev = self[min([self[bh].rev() for bh in bheads])].node()
427 minbhrev = self[min([self[bh].rev() for bh in bheads])].node()
428 reachable = self.changelog.reachable(latest, minbhrev)
428 reachable = self.changelog.reachable(latest, minbhrev)
429 reachable.remove(latest)
429 reachable.remove(latest)
430 bheads = [b for b in bheads if b not in reachable]
430 bheads = [b for b in bheads if b not in reachable]
431 partial[branch] = bheads
431 partial[branch] = bheads
432
432
433 def lookup(self, key):
433 def lookup(self, key):
434 if isinstance(key, int):
434 if isinstance(key, int):
435 return self.changelog.node(key)
435 return self.changelog.node(key)
436 elif key == '.':
436 elif key == '.':
437 return self.dirstate.parents()[0]
437 return self.dirstate.parents()[0]
438 elif key == 'null':
438 elif key == 'null':
439 return nullid
439 return nullid
440 elif key == 'tip':
440 elif key == 'tip':
441 return self.changelog.tip()
441 return self.changelog.tip()
442 n = self.changelog._match(key)
442 n = self.changelog._match(key)
443 if n:
443 if n:
444 return n
444 return n
445 if key in self.tags():
445 if key in self.tags():
446 return self.tags()[key]
446 return self.tags()[key]
447 if key in self.branchtags():
447 if key in self.branchtags():
448 return self.branchtags()[key]
448 return self.branchtags()[key]
449 n = self.changelog._partialmatch(key)
449 n = self.changelog._partialmatch(key)
450 if n:
450 if n:
451 return n
451 return n
452
452
453 # can't find key, check if it might have come from damaged dirstate
453 # can't find key, check if it might have come from damaged dirstate
454 if key in self.dirstate.parents():
454 if key in self.dirstate.parents():
455 raise error.Abort(_("working directory has unknown parent '%s'!")
455 raise error.Abort(_("working directory has unknown parent '%s'!")
456 % short(key))
456 % short(key))
457 try:
457 try:
458 if len(key) == 20:
458 if len(key) == 20:
459 key = hex(key)
459 key = hex(key)
460 except:
460 except:
461 pass
461 pass
462 raise error.RepoLookupError(_("unknown revision '%s'") % key)
462 raise error.RepoLookupError(_("unknown revision '%s'") % key)
463
463
464 def lookupbranch(self, key, remote=None):
464 def lookupbranch(self, key, remote=None):
465 repo = remote or self
465 repo = remote or self
466 if key in repo.branchmap():
466 if key in repo.branchmap():
467 return key
467 return key
468
468
469 repo = (remote and remote.local()) and remote or self
469 repo = (remote and remote.local()) and remote or self
470 return repo[key].branch()
470 return repo[key].branch()
471
471
472 def local(self):
472 def local(self):
473 return True
473 return True
474
474
475 def join(self, f):
475 def join(self, f):
476 return os.path.join(self.path, f)
476 return os.path.join(self.path, f)
477
477
478 def wjoin(self, f):
478 def wjoin(self, f):
479 return os.path.join(self.root, f)
479 return os.path.join(self.root, f)
480
480
481 def rjoin(self, f):
481 def rjoin(self, f):
482 return os.path.join(self.root, util.pconvert(f))
482 return os.path.join(self.root, util.pconvert(f))
483
483
484 def file(self, f):
484 def file(self, f):
485 if f[0] == '/':
485 if f[0] == '/':
486 f = f[1:]
486 f = f[1:]
487 return filelog.filelog(self.sopener, f)
487 return filelog.filelog(self.sopener, f)
488
488
489 def changectx(self, changeid):
489 def changectx(self, changeid):
490 return self[changeid]
490 return self[changeid]
491
491
492 def parents(self, changeid=None):
492 def parents(self, changeid=None):
493 '''get list of changectxs for parents of changeid'''
493 '''get list of changectxs for parents of changeid'''
494 return self[changeid].parents()
494 return self[changeid].parents()
495
495
496 def filectx(self, path, changeid=None, fileid=None):
496 def filectx(self, path, changeid=None, fileid=None):
497 """changeid can be a changeset revision, node, or tag.
497 """changeid can be a changeset revision, node, or tag.
498 fileid can be a file revision or node."""
498 fileid can be a file revision or node."""
499 return context.filectx(self, path, changeid, fileid)
499 return context.filectx(self, path, changeid, fileid)
500
500
501 def getcwd(self):
501 def getcwd(self):
502 return self.dirstate.getcwd()
502 return self.dirstate.getcwd()
503
503
504 def pathto(self, f, cwd=None):
504 def pathto(self, f, cwd=None):
505 return self.dirstate.pathto(f, cwd)
505 return self.dirstate.pathto(f, cwd)
506
506
507 def wfile(self, f, mode='r'):
507 def wfile(self, f, mode='r'):
508 return self.wopener(f, mode)
508 return self.wopener(f, mode)
509
509
510 def _link(self, f):
510 def _link(self, f):
511 return os.path.islink(self.wjoin(f))
511 return os.path.islink(self.wjoin(f))
512
512
513 def _filter(self, filter, filename, data):
513 def _filter(self, filter, filename, data):
514 if filter not in self.filterpats:
514 if filter not in self.filterpats:
515 l = []
515 l = []
516 for pat, cmd in self.ui.configitems(filter):
516 for pat, cmd in self.ui.configitems(filter):
517 if cmd == '!':
517 if cmd == '!':
518 continue
518 continue
519 mf = matchmod.match(self.root, '', [pat])
519 mf = matchmod.match(self.root, '', [pat])
520 fn = None
520 fn = None
521 params = cmd
521 params = cmd
522 for name, filterfn in self._datafilters.iteritems():
522 for name, filterfn in self._datafilters.iteritems():
523 if cmd.startswith(name):
523 if cmd.startswith(name):
524 fn = filterfn
524 fn = filterfn
525 params = cmd[len(name):].lstrip()
525 params = cmd[len(name):].lstrip()
526 break
526 break
527 if not fn:
527 if not fn:
528 fn = lambda s, c, **kwargs: util.filter(s, c)
528 fn = lambda s, c, **kwargs: util.filter(s, c)
529 # Wrap old filters not supporting keyword arguments
529 # Wrap old filters not supporting keyword arguments
530 if not inspect.getargspec(fn)[2]:
530 if not inspect.getargspec(fn)[2]:
531 oldfn = fn
531 oldfn = fn
532 fn = lambda s, c, **kwargs: oldfn(s, c)
532 fn = lambda s, c, **kwargs: oldfn(s, c)
533 l.append((mf, fn, params))
533 l.append((mf, fn, params))
534 self.filterpats[filter] = l
534 self.filterpats[filter] = l
535
535
536 for mf, fn, cmd in self.filterpats[filter]:
536 for mf, fn, cmd in self.filterpats[filter]:
537 if mf(filename):
537 if mf(filename):
538 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
538 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
539 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
539 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
540 break
540 break
541
541
542 return data
542 return data
543
543
544 def adddatafilter(self, name, filter):
544 def adddatafilter(self, name, filter):
545 self._datafilters[name] = filter
545 self._datafilters[name] = filter
546
546
547 def wread(self, filename):
547 def wread(self, filename):
548 if self._link(filename):
548 if self._link(filename):
549 data = os.readlink(self.wjoin(filename))
549 data = os.readlink(self.wjoin(filename))
550 else:
550 else:
551 data = self.wopener(filename, 'r').read()
551 data = self.wopener(filename, 'r').read()
552 return self._filter("encode", filename, data)
552 return self._filter("encode", filename, data)
553
553
554 def wwrite(self, filename, data, flags):
554 def wwrite(self, filename, data, flags):
555 data = self._filter("decode", filename, data)
555 data = self._filter("decode", filename, data)
556 try:
556 try:
557 os.unlink(self.wjoin(filename))
557 os.unlink(self.wjoin(filename))
558 except OSError:
558 except OSError:
559 pass
559 pass
560 if 'l' in flags:
560 if 'l' in flags:
561 self.wopener.symlink(data, filename)
561 self.wopener.symlink(data, filename)
562 else:
562 else:
563 self.wopener(filename, 'w').write(data)
563 self.wopener(filename, 'w').write(data)
564 if 'x' in flags:
564 if 'x' in flags:
565 util.set_flags(self.wjoin(filename), False, True)
565 util.set_flags(self.wjoin(filename), False, True)
566
566
567 def wwritedata(self, filename, data):
567 def wwritedata(self, filename, data):
568 return self._filter("decode", filename, data)
568 return self._filter("decode", filename, data)
569
569
570 def transaction(self, desc):
570 def transaction(self, desc):
571 tr = self._transref and self._transref() or None
571 tr = self._transref and self._transref() or None
572 if tr and tr.running():
572 if tr and tr.running():
573 return tr.nest()
573 return tr.nest()
574
574
575 # abort here if the journal already exists
575 # abort here if the journal already exists
576 if os.path.exists(self.sjoin("journal")):
576 if os.path.exists(self.sjoin("journal")):
577 raise error.RepoError(
577 raise error.RepoError(
578 _("abandoned transaction found - run hg recover"))
578 _("abandoned transaction found - run hg recover"))
579
579
580 # save dirstate for rollback
580 # save dirstate for rollback
581 try:
581 try:
582 ds = self.opener("dirstate").read()
582 ds = self.opener("dirstate").read()
583 except IOError:
583 except IOError:
584 ds = ""
584 ds = ""
585 self.opener("journal.dirstate", "w").write(ds)
585 self.opener("journal.dirstate", "w").write(ds)
586 self.opener("journal.branch", "w").write(self.dirstate.branch())
586 self.opener("journal.branch", "w").write(self.dirstate.branch())
587 self.opener("journal.desc", "w").write("%d\n%s\n" % (len(self), desc))
587 self.opener("journal.desc", "w").write("%d\n%s\n" % (len(self), desc))
588
588
589 renames = [(self.sjoin("journal"), self.sjoin("undo")),
589 renames = [(self.sjoin("journal"), self.sjoin("undo")),
590 (self.join("journal.dirstate"), self.join("undo.dirstate")),
590 (self.join("journal.dirstate"), self.join("undo.dirstate")),
591 (self.join("journal.branch"), self.join("undo.branch")),
591 (self.join("journal.branch"), self.join("undo.branch")),
592 (self.join("journal.desc"), self.join("undo.desc"))]
592 (self.join("journal.desc"), self.join("undo.desc"))]
593 tr = transaction.transaction(self.ui.warn, self.sopener,
593 tr = transaction.transaction(self.ui.warn, self.sopener,
594 self.sjoin("journal"),
594 self.sjoin("journal"),
595 aftertrans(renames),
595 aftertrans(renames),
596 self.store.createmode)
596 self.store.createmode)
597 self._transref = weakref.ref(tr)
597 self._transref = weakref.ref(tr)
598 return tr
598 return tr
599
599
600 def recover(self):
600 def recover(self):
601 lock = self.lock()
601 lock = self.lock()
602 try:
602 try:
603 if os.path.exists(self.sjoin("journal")):
603 if os.path.exists(self.sjoin("journal")):
604 self.ui.status(_("rolling back interrupted transaction\n"))
604 self.ui.status(_("rolling back interrupted transaction\n"))
605 transaction.rollback(self.sopener, self.sjoin("journal"),
605 transaction.rollback(self.sopener, self.sjoin("journal"),
606 self.ui.warn)
606 self.ui.warn)
607 self.invalidate()
607 self.invalidate()
608 return True
608 return True
609 else:
609 else:
610 self.ui.warn(_("no interrupted transaction available\n"))
610 self.ui.warn(_("no interrupted transaction available\n"))
611 return False
611 return False
612 finally:
612 finally:
613 lock.release()
613 lock.release()
614
614
615 def rollback(self, dryrun=False):
615 def rollback(self, dryrun=False):
616 wlock = lock = None
616 wlock = lock = None
617 try:
617 try:
618 wlock = self.wlock()
618 wlock = self.wlock()
619 lock = self.lock()
619 lock = self.lock()
620 if os.path.exists(self.sjoin("undo")):
620 if os.path.exists(self.sjoin("undo")):
621 try:
621 try:
622 args = self.opener("undo.desc", "r").read().splitlines()
622 args = self.opener("undo.desc", "r").read().splitlines()
623 if len(args) >= 3 and self.ui.verbose:
623 if len(args) >= 3 and self.ui.verbose:
624 desc = _("rolling back to revision %s"
624 desc = _("rolling back to revision %s"
625 " (undo %s: %s)\n") % (
625 " (undo %s: %s)\n") % (
626 int(args[0]) - 1, args[1], args[2])
626 int(args[0]) - 1, args[1], args[2])
627 elif len(args) >= 2:
627 elif len(args) >= 2:
628 desc = _("rolling back to revision %s (undo %s)\n") % (
628 desc = _("rolling back to revision %s (undo %s)\n") % (
629 int(args[0]) - 1, args[1])
629 int(args[0]) - 1, args[1])
630 except IOError:
630 except IOError:
631 desc = _("rolling back unknown transaction\n")
631 desc = _("rolling back unknown transaction\n")
632 self.ui.status(desc)
632 self.ui.status(desc)
633 if dryrun:
633 if dryrun:
634 return
634 return
635 transaction.rollback(self.sopener, self.sjoin("undo"),
635 transaction.rollback(self.sopener, self.sjoin("undo"),
636 self.ui.warn)
636 self.ui.warn)
637 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
637 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
638 try:
638 try:
639 branch = self.opener("undo.branch").read()
639 branch = self.opener("undo.branch").read()
640 self.dirstate.setbranch(branch)
640 self.dirstate.setbranch(branch)
641 except IOError:
641 except IOError:
642 self.ui.warn(_("Named branch could not be reset, "
642 self.ui.warn(_("Named branch could not be reset, "
643 "current branch still is: %s\n")
643 "current branch still is: %s\n")
644 % encoding.tolocal(self.dirstate.branch()))
644 % encoding.tolocal(self.dirstate.branch()))
645 self.invalidate()
645 self.invalidate()
646 self.dirstate.invalidate()
646 self.dirstate.invalidate()
647 self.destroyed()
647 self.destroyed()
648 else:
648 else:
649 self.ui.warn(_("no rollback information available\n"))
649 self.ui.warn(_("no rollback information available\n"))
650 return 1
650 finally:
651 finally:
651 release(lock, wlock)
652 release(lock, wlock)
652
653
653 def invalidatecaches(self):
654 def invalidatecaches(self):
654 self._tags = None
655 self._tags = None
655 self._tagtypes = None
656 self._tagtypes = None
656 self.nodetagscache = None
657 self.nodetagscache = None
657 self._branchcache = None # in UTF-8
658 self._branchcache = None # in UTF-8
658 self._branchcachetip = None
659 self._branchcachetip = None
659
660
660 def invalidate(self):
661 def invalidate(self):
661 for a in "changelog manifest".split():
662 for a in "changelog manifest".split():
662 if a in self.__dict__:
663 if a in self.__dict__:
663 delattr(self, a)
664 delattr(self, a)
664 self.invalidatecaches()
665 self.invalidatecaches()
665
666
666 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
667 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
667 try:
668 try:
668 l = lock.lock(lockname, 0, releasefn, desc=desc)
669 l = lock.lock(lockname, 0, releasefn, desc=desc)
669 except error.LockHeld, inst:
670 except error.LockHeld, inst:
670 if not wait:
671 if not wait:
671 raise
672 raise
672 self.ui.warn(_("waiting for lock on %s held by %r\n") %
673 self.ui.warn(_("waiting for lock on %s held by %r\n") %
673 (desc, inst.locker))
674 (desc, inst.locker))
674 # default to 600 seconds timeout
675 # default to 600 seconds timeout
675 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
676 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
676 releasefn, desc=desc)
677 releasefn, desc=desc)
677 if acquirefn:
678 if acquirefn:
678 acquirefn()
679 acquirefn()
679 return l
680 return l
680
681
681 def lock(self, wait=True):
682 def lock(self, wait=True):
682 '''Lock the repository store (.hg/store) and return a weak reference
683 '''Lock the repository store (.hg/store) and return a weak reference
683 to the lock. Use this before modifying the store (e.g. committing or
684 to the lock. Use this before modifying the store (e.g. committing or
684 stripping). If you are opening a transaction, get a lock as well.)'''
685 stripping). If you are opening a transaction, get a lock as well.)'''
685 l = self._lockref and self._lockref()
686 l = self._lockref and self._lockref()
686 if l is not None and l.held:
687 if l is not None and l.held:
687 l.lock()
688 l.lock()
688 return l
689 return l
689
690
690 l = self._lock(self.sjoin("lock"), wait, None, self.invalidate,
691 l = self._lock(self.sjoin("lock"), wait, None, self.invalidate,
691 _('repository %s') % self.origroot)
692 _('repository %s') % self.origroot)
692 self._lockref = weakref.ref(l)
693 self._lockref = weakref.ref(l)
693 return l
694 return l
694
695
695 def wlock(self, wait=True):
696 def wlock(self, wait=True):
696 '''Lock the non-store parts of the repository (everything under
697 '''Lock the non-store parts of the repository (everything under
697 .hg except .hg/store) and return a weak reference to the lock.
698 .hg except .hg/store) and return a weak reference to the lock.
698 Use this before modifying files in .hg.'''
699 Use this before modifying files in .hg.'''
699 l = self._wlockref and self._wlockref()
700 l = self._wlockref and self._wlockref()
700 if l is not None and l.held:
701 if l is not None and l.held:
701 l.lock()
702 l.lock()
702 return l
703 return l
703
704
704 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
705 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
705 self.dirstate.invalidate, _('working directory of %s') %
706 self.dirstate.invalidate, _('working directory of %s') %
706 self.origroot)
707 self.origroot)
707 self._wlockref = weakref.ref(l)
708 self._wlockref = weakref.ref(l)
708 return l
709 return l
709
710
710 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
711 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
711 """
712 """
712 commit an individual file as part of a larger transaction
713 commit an individual file as part of a larger transaction
713 """
714 """
714
715
715 fname = fctx.path()
716 fname = fctx.path()
716 text = fctx.data()
717 text = fctx.data()
717 flog = self.file(fname)
718 flog = self.file(fname)
718 fparent1 = manifest1.get(fname, nullid)
719 fparent1 = manifest1.get(fname, nullid)
719 fparent2 = fparent2o = manifest2.get(fname, nullid)
720 fparent2 = fparent2o = manifest2.get(fname, nullid)
720
721
721 meta = {}
722 meta = {}
722 copy = fctx.renamed()
723 copy = fctx.renamed()
723 if copy and copy[0] != fname:
724 if copy and copy[0] != fname:
724 # Mark the new revision of this file as a copy of another
725 # Mark the new revision of this file as a copy of another
725 # file. This copy data will effectively act as a parent
726 # file. This copy data will effectively act as a parent
726 # of this new revision. If this is a merge, the first
727 # of this new revision. If this is a merge, the first
727 # parent will be the nullid (meaning "look up the copy data")
728 # parent will be the nullid (meaning "look up the copy data")
728 # and the second one will be the other parent. For example:
729 # and the second one will be the other parent. For example:
729 #
730 #
730 # 0 --- 1 --- 3 rev1 changes file foo
731 # 0 --- 1 --- 3 rev1 changes file foo
731 # \ / rev2 renames foo to bar and changes it
732 # \ / rev2 renames foo to bar and changes it
732 # \- 2 -/ rev3 should have bar with all changes and
733 # \- 2 -/ rev3 should have bar with all changes and
733 # should record that bar descends from
734 # should record that bar descends from
734 # bar in rev2 and foo in rev1
735 # bar in rev2 and foo in rev1
735 #
736 #
736 # this allows this merge to succeed:
737 # this allows this merge to succeed:
737 #
738 #
738 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
739 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
739 # \ / merging rev3 and rev4 should use bar@rev2
740 # \ / merging rev3 and rev4 should use bar@rev2
740 # \- 2 --- 4 as the merge base
741 # \- 2 --- 4 as the merge base
741 #
742 #
742
743
743 cfname = copy[0]
744 cfname = copy[0]
744 crev = manifest1.get(cfname)
745 crev = manifest1.get(cfname)
745 newfparent = fparent2
746 newfparent = fparent2
746
747
747 if manifest2: # branch merge
748 if manifest2: # branch merge
748 if fparent2 == nullid or crev is None: # copied on remote side
749 if fparent2 == nullid or crev is None: # copied on remote side
749 if cfname in manifest2:
750 if cfname in manifest2:
750 crev = manifest2[cfname]
751 crev = manifest2[cfname]
751 newfparent = fparent1
752 newfparent = fparent1
752
753
753 # find source in nearest ancestor if we've lost track
754 # find source in nearest ancestor if we've lost track
754 if not crev:
755 if not crev:
755 self.ui.debug(" %s: searching for copy revision for %s\n" %
756 self.ui.debug(" %s: searching for copy revision for %s\n" %
756 (fname, cfname))
757 (fname, cfname))
757 for ancestor in self['.'].ancestors():
758 for ancestor in self['.'].ancestors():
758 if cfname in ancestor:
759 if cfname in ancestor:
759 crev = ancestor[cfname].filenode()
760 crev = ancestor[cfname].filenode()
760 break
761 break
761
762
762 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
763 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
763 meta["copy"] = cfname
764 meta["copy"] = cfname
764 meta["copyrev"] = hex(crev)
765 meta["copyrev"] = hex(crev)
765 fparent1, fparent2 = nullid, newfparent
766 fparent1, fparent2 = nullid, newfparent
766 elif fparent2 != nullid:
767 elif fparent2 != nullid:
767 # is one parent an ancestor of the other?
768 # is one parent an ancestor of the other?
768 fparentancestor = flog.ancestor(fparent1, fparent2)
769 fparentancestor = flog.ancestor(fparent1, fparent2)
769 if fparentancestor == fparent1:
770 if fparentancestor == fparent1:
770 fparent1, fparent2 = fparent2, nullid
771 fparent1, fparent2 = fparent2, nullid
771 elif fparentancestor == fparent2:
772 elif fparentancestor == fparent2:
772 fparent2 = nullid
773 fparent2 = nullid
773
774
774 # is the file changed?
775 # is the file changed?
775 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
776 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
776 changelist.append(fname)
777 changelist.append(fname)
777 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
778 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
778
779
779 # are just the flags changed during merge?
780 # are just the flags changed during merge?
780 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
781 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
781 changelist.append(fname)
782 changelist.append(fname)
782
783
783 return fparent1
784 return fparent1
784
785
785 def commit(self, text="", user=None, date=None, match=None, force=False,
786 def commit(self, text="", user=None, date=None, match=None, force=False,
786 editor=False, extra={}):
787 editor=False, extra={}):
787 """Add a new revision to current repository.
788 """Add a new revision to current repository.
788
789
789 Revision information is gathered from the working directory,
790 Revision information is gathered from the working directory,
790 match can be used to filter the committed files. If editor is
791 match can be used to filter the committed files. If editor is
791 supplied, it is called to get a commit message.
792 supplied, it is called to get a commit message.
792 """
793 """
793
794
794 def fail(f, msg):
795 def fail(f, msg):
795 raise util.Abort('%s: %s' % (f, msg))
796 raise util.Abort('%s: %s' % (f, msg))
796
797
797 if not match:
798 if not match:
798 match = matchmod.always(self.root, '')
799 match = matchmod.always(self.root, '')
799
800
800 if not force:
801 if not force:
801 vdirs = []
802 vdirs = []
802 match.dir = vdirs.append
803 match.dir = vdirs.append
803 match.bad = fail
804 match.bad = fail
804
805
805 wlock = self.wlock()
806 wlock = self.wlock()
806 try:
807 try:
807 wctx = self[None]
808 wctx = self[None]
808 merge = len(wctx.parents()) > 1
809 merge = len(wctx.parents()) > 1
809
810
810 if (not force and merge and match and
811 if (not force and merge and match and
811 (match.files() or match.anypats())):
812 (match.files() or match.anypats())):
812 raise util.Abort(_('cannot partially commit a merge '
813 raise util.Abort(_('cannot partially commit a merge '
813 '(do not specify files or patterns)'))
814 '(do not specify files or patterns)'))
814
815
815 changes = self.status(match=match, clean=force)
816 changes = self.status(match=match, clean=force)
816 if force:
817 if force:
817 changes[0].extend(changes[6]) # mq may commit unchanged files
818 changes[0].extend(changes[6]) # mq may commit unchanged files
818
819
819 # check subrepos
820 # check subrepos
820 subs = []
821 subs = []
821 removedsubs = set()
822 removedsubs = set()
822 for p in wctx.parents():
823 for p in wctx.parents():
823 removedsubs.update(s for s in p.substate if match(s))
824 removedsubs.update(s for s in p.substate if match(s))
824 for s in wctx.substate:
825 for s in wctx.substate:
825 removedsubs.discard(s)
826 removedsubs.discard(s)
826 if match(s) and wctx.sub(s).dirty():
827 if match(s) and wctx.sub(s).dirty():
827 subs.append(s)
828 subs.append(s)
828 if (subs or removedsubs) and '.hgsubstate' not in changes[0]:
829 if (subs or removedsubs) and '.hgsubstate' not in changes[0]:
829 changes[0].insert(0, '.hgsubstate')
830 changes[0].insert(0, '.hgsubstate')
830
831
831 # make sure all explicit patterns are matched
832 # make sure all explicit patterns are matched
832 if not force and match.files():
833 if not force and match.files():
833 matched = set(changes[0] + changes[1] + changes[2])
834 matched = set(changes[0] + changes[1] + changes[2])
834
835
835 for f in match.files():
836 for f in match.files():
836 if f == '.' or f in matched or f in wctx.substate:
837 if f == '.' or f in matched or f in wctx.substate:
837 continue
838 continue
838 if f in changes[3]: # missing
839 if f in changes[3]: # missing
839 fail(f, _('file not found!'))
840 fail(f, _('file not found!'))
840 if f in vdirs: # visited directory
841 if f in vdirs: # visited directory
841 d = f + '/'
842 d = f + '/'
842 for mf in matched:
843 for mf in matched:
843 if mf.startswith(d):
844 if mf.startswith(d):
844 break
845 break
845 else:
846 else:
846 fail(f, _("no match under directory!"))
847 fail(f, _("no match under directory!"))
847 elif f not in self.dirstate:
848 elif f not in self.dirstate:
848 fail(f, _("file not tracked!"))
849 fail(f, _("file not tracked!"))
849
850
850 if (not force and not extra.get("close") and not merge
851 if (not force and not extra.get("close") and not merge
851 and not (changes[0] or changes[1] or changes[2])
852 and not (changes[0] or changes[1] or changes[2])
852 and wctx.branch() == wctx.p1().branch()):
853 and wctx.branch() == wctx.p1().branch()):
853 return None
854 return None
854
855
855 ms = mergemod.mergestate(self)
856 ms = mergemod.mergestate(self)
856 for f in changes[0]:
857 for f in changes[0]:
857 if f in ms and ms[f] == 'u':
858 if f in ms and ms[f] == 'u':
858 raise util.Abort(_("unresolved merge conflicts "
859 raise util.Abort(_("unresolved merge conflicts "
859 "(see hg resolve)"))
860 "(see hg resolve)"))
860
861
861 cctx = context.workingctx(self, text, user, date, extra, changes)
862 cctx = context.workingctx(self, text, user, date, extra, changes)
862 if editor:
863 if editor:
863 cctx._text = editor(self, cctx, subs)
864 cctx._text = editor(self, cctx, subs)
864 edited = (text != cctx._text)
865 edited = (text != cctx._text)
865
866
866 # commit subs
867 # commit subs
867 if subs or removedsubs:
868 if subs or removedsubs:
868 state = wctx.substate.copy()
869 state = wctx.substate.copy()
869 for s in subs:
870 for s in subs:
870 sub = wctx.sub(s)
871 sub = wctx.sub(s)
871 self.ui.status(_('committing subrepository %s\n') %
872 self.ui.status(_('committing subrepository %s\n') %
872 subrepo.relpath(sub))
873 subrepo.relpath(sub))
873 sr = sub.commit(cctx._text, user, date)
874 sr = sub.commit(cctx._text, user, date)
874 state[s] = (state[s][0], sr)
875 state[s] = (state[s][0], sr)
875 subrepo.writestate(self, state)
876 subrepo.writestate(self, state)
876
877
877 # Save commit message in case this transaction gets rolled back
878 # Save commit message in case this transaction gets rolled back
878 # (e.g. by a pretxncommit hook). Leave the content alone on
879 # (e.g. by a pretxncommit hook). Leave the content alone on
879 # the assumption that the user will use the same editor again.
880 # the assumption that the user will use the same editor again.
880 msgfile = self.opener('last-message.txt', 'wb')
881 msgfile = self.opener('last-message.txt', 'wb')
881 msgfile.write(cctx._text)
882 msgfile.write(cctx._text)
882 msgfile.close()
883 msgfile.close()
883
884
884 p1, p2 = self.dirstate.parents()
885 p1, p2 = self.dirstate.parents()
885 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
886 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
886 try:
887 try:
887 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
888 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
888 ret = self.commitctx(cctx, True)
889 ret = self.commitctx(cctx, True)
889 except:
890 except:
890 if edited:
891 if edited:
891 msgfn = self.pathto(msgfile.name[len(self.root)+1:])
892 msgfn = self.pathto(msgfile.name[len(self.root)+1:])
892 self.ui.write(
893 self.ui.write(
893 _('note: commit message saved in %s\n') % msgfn)
894 _('note: commit message saved in %s\n') % msgfn)
894 raise
895 raise
895
896
896 # update dirstate and mergestate
897 # update dirstate and mergestate
897 for f in changes[0] + changes[1]:
898 for f in changes[0] + changes[1]:
898 self.dirstate.normal(f)
899 self.dirstate.normal(f)
899 for f in changes[2]:
900 for f in changes[2]:
900 self.dirstate.forget(f)
901 self.dirstate.forget(f)
901 self.dirstate.setparents(ret)
902 self.dirstate.setparents(ret)
902 ms.reset()
903 ms.reset()
903 finally:
904 finally:
904 wlock.release()
905 wlock.release()
905
906
906 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
907 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
907 return ret
908 return ret
908
909
909 def commitctx(self, ctx, error=False):
910 def commitctx(self, ctx, error=False):
910 """Add a new revision to current repository.
911 """Add a new revision to current repository.
911 Revision information is passed via the context argument.
912 Revision information is passed via the context argument.
912 """
913 """
913
914
914 tr = lock = None
915 tr = lock = None
915 removed = ctx.removed()
916 removed = ctx.removed()
916 p1, p2 = ctx.p1(), ctx.p2()
917 p1, p2 = ctx.p1(), ctx.p2()
917 m1 = p1.manifest().copy()
918 m1 = p1.manifest().copy()
918 m2 = p2.manifest()
919 m2 = p2.manifest()
919 user = ctx.user()
920 user = ctx.user()
920
921
921 lock = self.lock()
922 lock = self.lock()
922 try:
923 try:
923 tr = self.transaction("commit")
924 tr = self.transaction("commit")
924 trp = weakref.proxy(tr)
925 trp = weakref.proxy(tr)
925
926
926 # check in files
927 # check in files
927 new = {}
928 new = {}
928 changed = []
929 changed = []
929 linkrev = len(self)
930 linkrev = len(self)
930 for f in sorted(ctx.modified() + ctx.added()):
931 for f in sorted(ctx.modified() + ctx.added()):
931 self.ui.note(f + "\n")
932 self.ui.note(f + "\n")
932 try:
933 try:
933 fctx = ctx[f]
934 fctx = ctx[f]
934 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
935 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
935 changed)
936 changed)
936 m1.set(f, fctx.flags())
937 m1.set(f, fctx.flags())
937 except OSError, inst:
938 except OSError, inst:
938 self.ui.warn(_("trouble committing %s!\n") % f)
939 self.ui.warn(_("trouble committing %s!\n") % f)
939 raise
940 raise
940 except IOError, inst:
941 except IOError, inst:
941 errcode = getattr(inst, 'errno', errno.ENOENT)
942 errcode = getattr(inst, 'errno', errno.ENOENT)
942 if error or errcode and errcode != errno.ENOENT:
943 if error or errcode and errcode != errno.ENOENT:
943 self.ui.warn(_("trouble committing %s!\n") % f)
944 self.ui.warn(_("trouble committing %s!\n") % f)
944 raise
945 raise
945 else:
946 else:
946 removed.append(f)
947 removed.append(f)
947
948
948 # update manifest
949 # update manifest
949 m1.update(new)
950 m1.update(new)
950 removed = [f for f in sorted(removed) if f in m1 or f in m2]
951 removed = [f for f in sorted(removed) if f in m1 or f in m2]
951 drop = [f for f in removed if f in m1]
952 drop = [f for f in removed if f in m1]
952 for f in drop:
953 for f in drop:
953 del m1[f]
954 del m1[f]
954 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
955 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
955 p2.manifestnode(), (new, drop))
956 p2.manifestnode(), (new, drop))
956
957
957 # update changelog
958 # update changelog
958 self.changelog.delayupdate()
959 self.changelog.delayupdate()
959 n = self.changelog.add(mn, changed + removed, ctx.description(),
960 n = self.changelog.add(mn, changed + removed, ctx.description(),
960 trp, p1.node(), p2.node(),
961 trp, p1.node(), p2.node(),
961 user, ctx.date(), ctx.extra().copy())
962 user, ctx.date(), ctx.extra().copy())
962 p = lambda: self.changelog.writepending() and self.root or ""
963 p = lambda: self.changelog.writepending() and self.root or ""
963 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
964 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
964 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
965 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
965 parent2=xp2, pending=p)
966 parent2=xp2, pending=p)
966 self.changelog.finalize(trp)
967 self.changelog.finalize(trp)
967 tr.close()
968 tr.close()
968
969
969 if self._branchcache:
970 if self._branchcache:
970 self.branchtags()
971 self.branchtags()
971 return n
972 return n
972 finally:
973 finally:
973 del tr
974 del tr
974 lock.release()
975 lock.release()
975
976
976 def destroyed(self):
977 def destroyed(self):
977 '''Inform the repository that nodes have been destroyed.
978 '''Inform the repository that nodes have been destroyed.
978 Intended for use by strip and rollback, so there's a common
979 Intended for use by strip and rollback, so there's a common
979 place for anything that has to be done after destroying history.'''
980 place for anything that has to be done after destroying history.'''
980 # XXX it might be nice if we could take the list of destroyed
981 # XXX it might be nice if we could take the list of destroyed
981 # nodes, but I don't see an easy way for rollback() to do that
982 # nodes, but I don't see an easy way for rollback() to do that
982
983
983 # Ensure the persistent tag cache is updated. Doing it now
984 # Ensure the persistent tag cache is updated. Doing it now
984 # means that the tag cache only has to worry about destroyed
985 # means that the tag cache only has to worry about destroyed
985 # heads immediately after a strip/rollback. That in turn
986 # heads immediately after a strip/rollback. That in turn
986 # guarantees that "cachetip == currenttip" (comparing both rev
987 # guarantees that "cachetip == currenttip" (comparing both rev
987 # and node) always means no nodes have been added or destroyed.
988 # and node) always means no nodes have been added or destroyed.
988
989
989 # XXX this is suboptimal when qrefresh'ing: we strip the current
990 # XXX this is suboptimal when qrefresh'ing: we strip the current
990 # head, refresh the tag cache, then immediately add a new head.
991 # head, refresh the tag cache, then immediately add a new head.
991 # But I think doing it this way is necessary for the "instant
992 # But I think doing it this way is necessary for the "instant
992 # tag cache retrieval" case to work.
993 # tag cache retrieval" case to work.
993 self.invalidatecaches()
994 self.invalidatecaches()
994
995
995 def walk(self, match, node=None):
996 def walk(self, match, node=None):
996 '''
997 '''
997 walk recursively through the directory tree or a given
998 walk recursively through the directory tree or a given
998 changeset, finding all files matched by the match
999 changeset, finding all files matched by the match
999 function
1000 function
1000 '''
1001 '''
1001 return self[node].walk(match)
1002 return self[node].walk(match)
1002
1003
1003 def status(self, node1='.', node2=None, match=None,
1004 def status(self, node1='.', node2=None, match=None,
1004 ignored=False, clean=False, unknown=False):
1005 ignored=False, clean=False, unknown=False):
1005 """return status of files between two nodes or node and working directory
1006 """return status of files between two nodes or node and working directory
1006
1007
1007 If node1 is None, use the first dirstate parent instead.
1008 If node1 is None, use the first dirstate parent instead.
1008 If node2 is None, compare node1 with working directory.
1009 If node2 is None, compare node1 with working directory.
1009 """
1010 """
1010
1011
1011 def mfmatches(ctx):
1012 def mfmatches(ctx):
1012 mf = ctx.manifest().copy()
1013 mf = ctx.manifest().copy()
1013 for fn in mf.keys():
1014 for fn in mf.keys():
1014 if not match(fn):
1015 if not match(fn):
1015 del mf[fn]
1016 del mf[fn]
1016 return mf
1017 return mf
1017
1018
1018 if isinstance(node1, context.changectx):
1019 if isinstance(node1, context.changectx):
1019 ctx1 = node1
1020 ctx1 = node1
1020 else:
1021 else:
1021 ctx1 = self[node1]
1022 ctx1 = self[node1]
1022 if isinstance(node2, context.changectx):
1023 if isinstance(node2, context.changectx):
1023 ctx2 = node2
1024 ctx2 = node2
1024 else:
1025 else:
1025 ctx2 = self[node2]
1026 ctx2 = self[node2]
1026
1027
1027 working = ctx2.rev() is None
1028 working = ctx2.rev() is None
1028 parentworking = working and ctx1 == self['.']
1029 parentworking = working and ctx1 == self['.']
1029 match = match or matchmod.always(self.root, self.getcwd())
1030 match = match or matchmod.always(self.root, self.getcwd())
1030 listignored, listclean, listunknown = ignored, clean, unknown
1031 listignored, listclean, listunknown = ignored, clean, unknown
1031
1032
1032 # load earliest manifest first for caching reasons
1033 # load earliest manifest first for caching reasons
1033 if not working and ctx2.rev() < ctx1.rev():
1034 if not working and ctx2.rev() < ctx1.rev():
1034 ctx2.manifest()
1035 ctx2.manifest()
1035
1036
1036 if not parentworking:
1037 if not parentworking:
1037 def bad(f, msg):
1038 def bad(f, msg):
1038 if f not in ctx1:
1039 if f not in ctx1:
1039 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1040 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1040 match.bad = bad
1041 match.bad = bad
1041
1042
1042 if working: # we need to scan the working dir
1043 if working: # we need to scan the working dir
1043 subrepos = ctx1.substate.keys()
1044 subrepos = ctx1.substate.keys()
1044 s = self.dirstate.status(match, subrepos, listignored,
1045 s = self.dirstate.status(match, subrepos, listignored,
1045 listclean, listunknown)
1046 listclean, listunknown)
1046 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1047 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1047
1048
1048 # check for any possibly clean files
1049 # check for any possibly clean files
1049 if parentworking and cmp:
1050 if parentworking and cmp:
1050 fixup = []
1051 fixup = []
1051 # do a full compare of any files that might have changed
1052 # do a full compare of any files that might have changed
1052 for f in sorted(cmp):
1053 for f in sorted(cmp):
1053 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1054 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1054 or ctx1[f].cmp(ctx2[f].data())):
1055 or ctx1[f].cmp(ctx2[f].data())):
1055 modified.append(f)
1056 modified.append(f)
1056 else:
1057 else:
1057 fixup.append(f)
1058 fixup.append(f)
1058
1059
1059 if listclean:
1060 if listclean:
1060 clean += fixup
1061 clean += fixup
1061
1062
1062 # update dirstate for files that are actually clean
1063 # update dirstate for files that are actually clean
1063 if fixup:
1064 if fixup:
1064 try:
1065 try:
1065 # updating the dirstate is optional
1066 # updating the dirstate is optional
1066 # so we don't wait on the lock
1067 # so we don't wait on the lock
1067 wlock = self.wlock(False)
1068 wlock = self.wlock(False)
1068 try:
1069 try:
1069 for f in fixup:
1070 for f in fixup:
1070 self.dirstate.normal(f)
1071 self.dirstate.normal(f)
1071 finally:
1072 finally:
1072 wlock.release()
1073 wlock.release()
1073 except error.LockError:
1074 except error.LockError:
1074 pass
1075 pass
1075
1076
1076 if not parentworking:
1077 if not parentworking:
1077 mf1 = mfmatches(ctx1)
1078 mf1 = mfmatches(ctx1)
1078 if working:
1079 if working:
1079 # we are comparing working dir against non-parent
1080 # we are comparing working dir against non-parent
1080 # generate a pseudo-manifest for the working dir
1081 # generate a pseudo-manifest for the working dir
1081 mf2 = mfmatches(self['.'])
1082 mf2 = mfmatches(self['.'])
1082 for f in cmp + modified + added:
1083 for f in cmp + modified + added:
1083 mf2[f] = None
1084 mf2[f] = None
1084 mf2.set(f, ctx2.flags(f))
1085 mf2.set(f, ctx2.flags(f))
1085 for f in removed:
1086 for f in removed:
1086 if f in mf2:
1087 if f in mf2:
1087 del mf2[f]
1088 del mf2[f]
1088 else:
1089 else:
1089 # we are comparing two revisions
1090 # we are comparing two revisions
1090 deleted, unknown, ignored = [], [], []
1091 deleted, unknown, ignored = [], [], []
1091 mf2 = mfmatches(ctx2)
1092 mf2 = mfmatches(ctx2)
1092
1093
1093 modified, added, clean = [], [], []
1094 modified, added, clean = [], [], []
1094 for fn in mf2:
1095 for fn in mf2:
1095 if fn in mf1:
1096 if fn in mf1:
1096 if (mf1.flags(fn) != mf2.flags(fn) or
1097 if (mf1.flags(fn) != mf2.flags(fn) or
1097 (mf1[fn] != mf2[fn] and
1098 (mf1[fn] != mf2[fn] and
1098 (mf2[fn] or ctx1[fn].cmp(ctx2[fn].data())))):
1099 (mf2[fn] or ctx1[fn].cmp(ctx2[fn].data())))):
1099 modified.append(fn)
1100 modified.append(fn)
1100 elif listclean:
1101 elif listclean:
1101 clean.append(fn)
1102 clean.append(fn)
1102 del mf1[fn]
1103 del mf1[fn]
1103 else:
1104 else:
1104 added.append(fn)
1105 added.append(fn)
1105 removed = mf1.keys()
1106 removed = mf1.keys()
1106
1107
1107 r = modified, added, removed, deleted, unknown, ignored, clean
1108 r = modified, added, removed, deleted, unknown, ignored, clean
1108 [l.sort() for l in r]
1109 [l.sort() for l in r]
1109 return r
1110 return r
1110
1111
1111 def add(self, list):
1112 def add(self, list):
1112 wlock = self.wlock()
1113 wlock = self.wlock()
1113 try:
1114 try:
1114 rejected = []
1115 rejected = []
1115 for f in list:
1116 for f in list:
1116 p = self.wjoin(f)
1117 p = self.wjoin(f)
1117 try:
1118 try:
1118 st = os.lstat(p)
1119 st = os.lstat(p)
1119 except:
1120 except:
1120 self.ui.warn(_("%s does not exist!\n") % f)
1121 self.ui.warn(_("%s does not exist!\n") % f)
1121 rejected.append(f)
1122 rejected.append(f)
1122 continue
1123 continue
1123 if st.st_size > 10000000:
1124 if st.st_size > 10000000:
1124 self.ui.warn(_("%s: up to %d MB of RAM may be required "
1125 self.ui.warn(_("%s: up to %d MB of RAM may be required "
1125 "to manage this file\n"
1126 "to manage this file\n"
1126 "(use 'hg revert %s' to cancel the "
1127 "(use 'hg revert %s' to cancel the "
1127 "pending addition)\n")
1128 "pending addition)\n")
1128 % (f, 3 * st.st_size // 1000000, f))
1129 % (f, 3 * st.st_size // 1000000, f))
1129 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1130 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1130 self.ui.warn(_("%s not added: only files and symlinks "
1131 self.ui.warn(_("%s not added: only files and symlinks "
1131 "supported currently\n") % f)
1132 "supported currently\n") % f)
1132 rejected.append(p)
1133 rejected.append(p)
1133 elif self.dirstate[f] in 'amn':
1134 elif self.dirstate[f] in 'amn':
1134 self.ui.warn(_("%s already tracked!\n") % f)
1135 self.ui.warn(_("%s already tracked!\n") % f)
1135 elif self.dirstate[f] == 'r':
1136 elif self.dirstate[f] == 'r':
1136 self.dirstate.normallookup(f)
1137 self.dirstate.normallookup(f)
1137 else:
1138 else:
1138 self.dirstate.add(f)
1139 self.dirstate.add(f)
1139 return rejected
1140 return rejected
1140 finally:
1141 finally:
1141 wlock.release()
1142 wlock.release()
1142
1143
1143 def forget(self, list):
1144 def forget(self, list):
1144 wlock = self.wlock()
1145 wlock = self.wlock()
1145 try:
1146 try:
1146 for f in list:
1147 for f in list:
1147 if self.dirstate[f] != 'a':
1148 if self.dirstate[f] != 'a':
1148 self.ui.warn(_("%s not added!\n") % f)
1149 self.ui.warn(_("%s not added!\n") % f)
1149 else:
1150 else:
1150 self.dirstate.forget(f)
1151 self.dirstate.forget(f)
1151 finally:
1152 finally:
1152 wlock.release()
1153 wlock.release()
1153
1154
1154 def remove(self, list, unlink=False):
1155 def remove(self, list, unlink=False):
1155 if unlink:
1156 if unlink:
1156 for f in list:
1157 for f in list:
1157 try:
1158 try:
1158 util.unlink(self.wjoin(f))
1159 util.unlink(self.wjoin(f))
1159 except OSError, inst:
1160 except OSError, inst:
1160 if inst.errno != errno.ENOENT:
1161 if inst.errno != errno.ENOENT:
1161 raise
1162 raise
1162 wlock = self.wlock()
1163 wlock = self.wlock()
1163 try:
1164 try:
1164 for f in list:
1165 for f in list:
1165 if unlink and os.path.exists(self.wjoin(f)):
1166 if unlink and os.path.exists(self.wjoin(f)):
1166 self.ui.warn(_("%s still exists!\n") % f)
1167 self.ui.warn(_("%s still exists!\n") % f)
1167 elif self.dirstate[f] == 'a':
1168 elif self.dirstate[f] == 'a':
1168 self.dirstate.forget(f)
1169 self.dirstate.forget(f)
1169 elif f not in self.dirstate:
1170 elif f not in self.dirstate:
1170 self.ui.warn(_("%s not tracked!\n") % f)
1171 self.ui.warn(_("%s not tracked!\n") % f)
1171 else:
1172 else:
1172 self.dirstate.remove(f)
1173 self.dirstate.remove(f)
1173 finally:
1174 finally:
1174 wlock.release()
1175 wlock.release()
1175
1176
1176 def undelete(self, list):
1177 def undelete(self, list):
1177 manifests = [self.manifest.read(self.changelog.read(p)[0])
1178 manifests = [self.manifest.read(self.changelog.read(p)[0])
1178 for p in self.dirstate.parents() if p != nullid]
1179 for p in self.dirstate.parents() if p != nullid]
1179 wlock = self.wlock()
1180 wlock = self.wlock()
1180 try:
1181 try:
1181 for f in list:
1182 for f in list:
1182 if self.dirstate[f] != 'r':
1183 if self.dirstate[f] != 'r':
1183 self.ui.warn(_("%s not removed!\n") % f)
1184 self.ui.warn(_("%s not removed!\n") % f)
1184 else:
1185 else:
1185 m = f in manifests[0] and manifests[0] or manifests[1]
1186 m = f in manifests[0] and manifests[0] or manifests[1]
1186 t = self.file(f).read(m[f])
1187 t = self.file(f).read(m[f])
1187 self.wwrite(f, t, m.flags(f))
1188 self.wwrite(f, t, m.flags(f))
1188 self.dirstate.normal(f)
1189 self.dirstate.normal(f)
1189 finally:
1190 finally:
1190 wlock.release()
1191 wlock.release()
1191
1192
1192 def copy(self, source, dest):
1193 def copy(self, source, dest):
1193 p = self.wjoin(dest)
1194 p = self.wjoin(dest)
1194 if not (os.path.exists(p) or os.path.islink(p)):
1195 if not (os.path.exists(p) or os.path.islink(p)):
1195 self.ui.warn(_("%s does not exist!\n") % dest)
1196 self.ui.warn(_("%s does not exist!\n") % dest)
1196 elif not (os.path.isfile(p) or os.path.islink(p)):
1197 elif not (os.path.isfile(p) or os.path.islink(p)):
1197 self.ui.warn(_("copy failed: %s is not a file or a "
1198 self.ui.warn(_("copy failed: %s is not a file or a "
1198 "symbolic link\n") % dest)
1199 "symbolic link\n") % dest)
1199 else:
1200 else:
1200 wlock = self.wlock()
1201 wlock = self.wlock()
1201 try:
1202 try:
1202 if self.dirstate[dest] in '?r':
1203 if self.dirstate[dest] in '?r':
1203 self.dirstate.add(dest)
1204 self.dirstate.add(dest)
1204 self.dirstate.copy(source, dest)
1205 self.dirstate.copy(source, dest)
1205 finally:
1206 finally:
1206 wlock.release()
1207 wlock.release()
1207
1208
1208 def heads(self, start=None):
1209 def heads(self, start=None):
1209 heads = self.changelog.heads(start)
1210 heads = self.changelog.heads(start)
1210 # sort the output in rev descending order
1211 # sort the output in rev descending order
1211 heads = [(-self.changelog.rev(h), h) for h in heads]
1212 heads = [(-self.changelog.rev(h), h) for h in heads]
1212 return [n for (r, n) in sorted(heads)]
1213 return [n for (r, n) in sorted(heads)]
1213
1214
1214 def branchheads(self, branch=None, start=None, closed=False):
1215 def branchheads(self, branch=None, start=None, closed=False):
1215 '''return a (possibly filtered) list of heads for the given branch
1216 '''return a (possibly filtered) list of heads for the given branch
1216
1217
1217 Heads are returned in topological order, from newest to oldest.
1218 Heads are returned in topological order, from newest to oldest.
1218 If branch is None, use the dirstate branch.
1219 If branch is None, use the dirstate branch.
1219 If start is not None, return only heads reachable from start.
1220 If start is not None, return only heads reachable from start.
1220 If closed is True, return heads that are marked as closed as well.
1221 If closed is True, return heads that are marked as closed as well.
1221 '''
1222 '''
1222 if branch is None:
1223 if branch is None:
1223 branch = self[None].branch()
1224 branch = self[None].branch()
1224 branches = self.branchmap()
1225 branches = self.branchmap()
1225 if branch not in branches:
1226 if branch not in branches:
1226 return []
1227 return []
1227 # the cache returns heads ordered lowest to highest
1228 # the cache returns heads ordered lowest to highest
1228 bheads = list(reversed(branches[branch]))
1229 bheads = list(reversed(branches[branch]))
1229 if start is not None:
1230 if start is not None:
1230 # filter out the heads that cannot be reached from startrev
1231 # filter out the heads that cannot be reached from startrev
1231 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1232 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1232 bheads = [h for h in bheads if h in fbheads]
1233 bheads = [h for h in bheads if h in fbheads]
1233 if not closed:
1234 if not closed:
1234 bheads = [h for h in bheads if
1235 bheads = [h for h in bheads if
1235 ('close' not in self.changelog.read(h)[5])]
1236 ('close' not in self.changelog.read(h)[5])]
1236 return bheads
1237 return bheads
1237
1238
1238 def branches(self, nodes):
1239 def branches(self, nodes):
1239 if not nodes:
1240 if not nodes:
1240 nodes = [self.changelog.tip()]
1241 nodes = [self.changelog.tip()]
1241 b = []
1242 b = []
1242 for n in nodes:
1243 for n in nodes:
1243 t = n
1244 t = n
1244 while 1:
1245 while 1:
1245 p = self.changelog.parents(n)
1246 p = self.changelog.parents(n)
1246 if p[1] != nullid or p[0] == nullid:
1247 if p[1] != nullid or p[0] == nullid:
1247 b.append((t, n, p[0], p[1]))
1248 b.append((t, n, p[0], p[1]))
1248 break
1249 break
1249 n = p[0]
1250 n = p[0]
1250 return b
1251 return b
1251
1252
1252 def between(self, pairs):
1253 def between(self, pairs):
1253 r = []
1254 r = []
1254
1255
1255 for top, bottom in pairs:
1256 for top, bottom in pairs:
1256 n, l, i = top, [], 0
1257 n, l, i = top, [], 0
1257 f = 1
1258 f = 1
1258
1259
1259 while n != bottom and n != nullid:
1260 while n != bottom and n != nullid:
1260 p = self.changelog.parents(n)[0]
1261 p = self.changelog.parents(n)[0]
1261 if i == f:
1262 if i == f:
1262 l.append(n)
1263 l.append(n)
1263 f = f * 2
1264 f = f * 2
1264 n = p
1265 n = p
1265 i += 1
1266 i += 1
1266
1267
1267 r.append(l)
1268 r.append(l)
1268
1269
1269 return r
1270 return r
1270
1271
1271 def findincoming(self, remote, base=None, heads=None, force=False):
1272 def findincoming(self, remote, base=None, heads=None, force=False):
1272 """Return list of roots of the subsets of missing nodes from remote
1273 """Return list of roots of the subsets of missing nodes from remote
1273
1274
1274 If base dict is specified, assume that these nodes and their parents
1275 If base dict is specified, assume that these nodes and their parents
1275 exist on the remote side and that no child of a node of base exists
1276 exist on the remote side and that no child of a node of base exists
1276 in both remote and self.
1277 in both remote and self.
1277 Furthermore base will be updated to include the nodes that exists
1278 Furthermore base will be updated to include the nodes that exists
1278 in self and remote but no children exists in self and remote.
1279 in self and remote but no children exists in self and remote.
1279 If a list of heads is specified, return only nodes which are heads
1280 If a list of heads is specified, return only nodes which are heads
1280 or ancestors of these heads.
1281 or ancestors of these heads.
1281
1282
1282 All the ancestors of base are in self and in remote.
1283 All the ancestors of base are in self and in remote.
1283 All the descendants of the list returned are missing in self.
1284 All the descendants of the list returned are missing in self.
1284 (and so we know that the rest of the nodes are missing in remote, see
1285 (and so we know that the rest of the nodes are missing in remote, see
1285 outgoing)
1286 outgoing)
1286 """
1287 """
1287 return self.findcommonincoming(remote, base, heads, force)[1]
1288 return self.findcommonincoming(remote, base, heads, force)[1]
1288
1289
1289 def findcommonincoming(self, remote, base=None, heads=None, force=False):
1290 def findcommonincoming(self, remote, base=None, heads=None, force=False):
1290 """Return a tuple (common, missing roots, heads) used to identify
1291 """Return a tuple (common, missing roots, heads) used to identify
1291 missing nodes from remote.
1292 missing nodes from remote.
1292
1293
1293 If base dict is specified, assume that these nodes and their parents
1294 If base dict is specified, assume that these nodes and their parents
1294 exist on the remote side and that no child of a node of base exists
1295 exist on the remote side and that no child of a node of base exists
1295 in both remote and self.
1296 in both remote and self.
1296 Furthermore base will be updated to include the nodes that exists
1297 Furthermore base will be updated to include the nodes that exists
1297 in self and remote but no children exists in self and remote.
1298 in self and remote but no children exists in self and remote.
1298 If a list of heads is specified, return only nodes which are heads
1299 If a list of heads is specified, return only nodes which are heads
1299 or ancestors of these heads.
1300 or ancestors of these heads.
1300
1301
1301 All the ancestors of base are in self and in remote.
1302 All the ancestors of base are in self and in remote.
1302 """
1303 """
1303 m = self.changelog.nodemap
1304 m = self.changelog.nodemap
1304 search = []
1305 search = []
1305 fetch = set()
1306 fetch = set()
1306 seen = set()
1307 seen = set()
1307 seenbranch = set()
1308 seenbranch = set()
1308 if base is None:
1309 if base is None:
1309 base = {}
1310 base = {}
1310
1311
1311 if not heads:
1312 if not heads:
1312 heads = remote.heads()
1313 heads = remote.heads()
1313
1314
1314 if self.changelog.tip() == nullid:
1315 if self.changelog.tip() == nullid:
1315 base[nullid] = 1
1316 base[nullid] = 1
1316 if heads != [nullid]:
1317 if heads != [nullid]:
1317 return [nullid], [nullid], list(heads)
1318 return [nullid], [nullid], list(heads)
1318 return [nullid], [], []
1319 return [nullid], [], []
1319
1320
1320 # assume we're closer to the tip than the root
1321 # assume we're closer to the tip than the root
1321 # and start by examining the heads
1322 # and start by examining the heads
1322 self.ui.status(_("searching for changes\n"))
1323 self.ui.status(_("searching for changes\n"))
1323
1324
1324 unknown = []
1325 unknown = []
1325 for h in heads:
1326 for h in heads:
1326 if h not in m:
1327 if h not in m:
1327 unknown.append(h)
1328 unknown.append(h)
1328 else:
1329 else:
1329 base[h] = 1
1330 base[h] = 1
1330
1331
1331 heads = unknown
1332 heads = unknown
1332 if not unknown:
1333 if not unknown:
1333 return base.keys(), [], []
1334 return base.keys(), [], []
1334
1335
1335 req = set(unknown)
1336 req = set(unknown)
1336 reqcnt = 0
1337 reqcnt = 0
1337
1338
1338 # search through remote branches
1339 # search through remote branches
1339 # a 'branch' here is a linear segment of history, with four parts:
1340 # a 'branch' here is a linear segment of history, with four parts:
1340 # head, root, first parent, second parent
1341 # head, root, first parent, second parent
1341 # (a branch always has two parents (or none) by definition)
1342 # (a branch always has two parents (or none) by definition)
1342 unknown = remote.branches(unknown)
1343 unknown = remote.branches(unknown)
1343 while unknown:
1344 while unknown:
1344 r = []
1345 r = []
1345 while unknown:
1346 while unknown:
1346 n = unknown.pop(0)
1347 n = unknown.pop(0)
1347 if n[0] in seen:
1348 if n[0] in seen:
1348 continue
1349 continue
1349
1350
1350 self.ui.debug("examining %s:%s\n"
1351 self.ui.debug("examining %s:%s\n"
1351 % (short(n[0]), short(n[1])))
1352 % (short(n[0]), short(n[1])))
1352 if n[0] == nullid: # found the end of the branch
1353 if n[0] == nullid: # found the end of the branch
1353 pass
1354 pass
1354 elif n in seenbranch:
1355 elif n in seenbranch:
1355 self.ui.debug("branch already found\n")
1356 self.ui.debug("branch already found\n")
1356 continue
1357 continue
1357 elif n[1] and n[1] in m: # do we know the base?
1358 elif n[1] and n[1] in m: # do we know the base?
1358 self.ui.debug("found incomplete branch %s:%s\n"
1359 self.ui.debug("found incomplete branch %s:%s\n"
1359 % (short(n[0]), short(n[1])))
1360 % (short(n[0]), short(n[1])))
1360 search.append(n[0:2]) # schedule branch range for scanning
1361 search.append(n[0:2]) # schedule branch range for scanning
1361 seenbranch.add(n)
1362 seenbranch.add(n)
1362 else:
1363 else:
1363 if n[1] not in seen and n[1] not in fetch:
1364 if n[1] not in seen and n[1] not in fetch:
1364 if n[2] in m and n[3] in m:
1365 if n[2] in m and n[3] in m:
1365 self.ui.debug("found new changeset %s\n" %
1366 self.ui.debug("found new changeset %s\n" %
1366 short(n[1]))
1367 short(n[1]))
1367 fetch.add(n[1]) # earliest unknown
1368 fetch.add(n[1]) # earliest unknown
1368 for p in n[2:4]:
1369 for p in n[2:4]:
1369 if p in m:
1370 if p in m:
1370 base[p] = 1 # latest known
1371 base[p] = 1 # latest known
1371
1372
1372 for p in n[2:4]:
1373 for p in n[2:4]:
1373 if p not in req and p not in m:
1374 if p not in req and p not in m:
1374 r.append(p)
1375 r.append(p)
1375 req.add(p)
1376 req.add(p)
1376 seen.add(n[0])
1377 seen.add(n[0])
1377
1378
1378 if r:
1379 if r:
1379 reqcnt += 1
1380 reqcnt += 1
1380 self.ui.progress(_('searching'), reqcnt, unit=_('queries'))
1381 self.ui.progress(_('searching'), reqcnt, unit=_('queries'))
1381 self.ui.debug("request %d: %s\n" %
1382 self.ui.debug("request %d: %s\n" %
1382 (reqcnt, " ".join(map(short, r))))
1383 (reqcnt, " ".join(map(short, r))))
1383 for p in xrange(0, len(r), 10):
1384 for p in xrange(0, len(r), 10):
1384 for b in remote.branches(r[p:p + 10]):
1385 for b in remote.branches(r[p:p + 10]):
1385 self.ui.debug("received %s:%s\n" %
1386 self.ui.debug("received %s:%s\n" %
1386 (short(b[0]), short(b[1])))
1387 (short(b[0]), short(b[1])))
1387 unknown.append(b)
1388 unknown.append(b)
1388
1389
1389 # do binary search on the branches we found
1390 # do binary search on the branches we found
1390 while search:
1391 while search:
1391 newsearch = []
1392 newsearch = []
1392 reqcnt += 1
1393 reqcnt += 1
1393 self.ui.progress(_('searching'), reqcnt, unit=_('queries'))
1394 self.ui.progress(_('searching'), reqcnt, unit=_('queries'))
1394 for n, l in zip(search, remote.between(search)):
1395 for n, l in zip(search, remote.between(search)):
1395 l.append(n[1])
1396 l.append(n[1])
1396 p = n[0]
1397 p = n[0]
1397 f = 1
1398 f = 1
1398 for i in l:
1399 for i in l:
1399 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
1400 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
1400 if i in m:
1401 if i in m:
1401 if f <= 2:
1402 if f <= 2:
1402 self.ui.debug("found new branch changeset %s\n" %
1403 self.ui.debug("found new branch changeset %s\n" %
1403 short(p))
1404 short(p))
1404 fetch.add(p)
1405 fetch.add(p)
1405 base[i] = 1
1406 base[i] = 1
1406 else:
1407 else:
1407 self.ui.debug("narrowed branch search to %s:%s\n"
1408 self.ui.debug("narrowed branch search to %s:%s\n"
1408 % (short(p), short(i)))
1409 % (short(p), short(i)))
1409 newsearch.append((p, i))
1410 newsearch.append((p, i))
1410 break
1411 break
1411 p, f = i, f * 2
1412 p, f = i, f * 2
1412 search = newsearch
1413 search = newsearch
1413
1414
1414 # sanity check our fetch list
1415 # sanity check our fetch list
1415 for f in fetch:
1416 for f in fetch:
1416 if f in m:
1417 if f in m:
1417 raise error.RepoError(_("already have changeset ")
1418 raise error.RepoError(_("already have changeset ")
1418 + short(f[:4]))
1419 + short(f[:4]))
1419
1420
1420 if base.keys() == [nullid]:
1421 if base.keys() == [nullid]:
1421 if force:
1422 if force:
1422 self.ui.warn(_("warning: repository is unrelated\n"))
1423 self.ui.warn(_("warning: repository is unrelated\n"))
1423 else:
1424 else:
1424 raise util.Abort(_("repository is unrelated"))
1425 raise util.Abort(_("repository is unrelated"))
1425
1426
1426 self.ui.debug("found new changesets starting at " +
1427 self.ui.debug("found new changesets starting at " +
1427 " ".join([short(f) for f in fetch]) + "\n")
1428 " ".join([short(f) for f in fetch]) + "\n")
1428
1429
1429 self.ui.progress(_('searching'), None)
1430 self.ui.progress(_('searching'), None)
1430 self.ui.debug("%d total queries\n" % reqcnt)
1431 self.ui.debug("%d total queries\n" % reqcnt)
1431
1432
1432 return base.keys(), list(fetch), heads
1433 return base.keys(), list(fetch), heads
1433
1434
1434 def findoutgoing(self, remote, base=None, heads=None, force=False):
1435 def findoutgoing(self, remote, base=None, heads=None, force=False):
1435 """Return list of nodes that are roots of subsets not in remote
1436 """Return list of nodes that are roots of subsets not in remote
1436
1437
1437 If base dict is specified, assume that these nodes and their parents
1438 If base dict is specified, assume that these nodes and their parents
1438 exist on the remote side.
1439 exist on the remote side.
1439 If a list of heads is specified, return only nodes which are heads
1440 If a list of heads is specified, return only nodes which are heads
1440 or ancestors of these heads, and return a second element which
1441 or ancestors of these heads, and return a second element which
1441 contains all remote heads which get new children.
1442 contains all remote heads which get new children.
1442 """
1443 """
1443 if base is None:
1444 if base is None:
1444 base = {}
1445 base = {}
1445 self.findincoming(remote, base, heads, force=force)
1446 self.findincoming(remote, base, heads, force=force)
1446
1447
1447 self.ui.debug("common changesets up to "
1448 self.ui.debug("common changesets up to "
1448 + " ".join(map(short, base.keys())) + "\n")
1449 + " ".join(map(short, base.keys())) + "\n")
1449
1450
1450 remain = set(self.changelog.nodemap)
1451 remain = set(self.changelog.nodemap)
1451
1452
1452 # prune everything remote has from the tree
1453 # prune everything remote has from the tree
1453 remain.remove(nullid)
1454 remain.remove(nullid)
1454 remove = base.keys()
1455 remove = base.keys()
1455 while remove:
1456 while remove:
1456 n = remove.pop(0)
1457 n = remove.pop(0)
1457 if n in remain:
1458 if n in remain:
1458 remain.remove(n)
1459 remain.remove(n)
1459 for p in self.changelog.parents(n):
1460 for p in self.changelog.parents(n):
1460 remove.append(p)
1461 remove.append(p)
1461
1462
1462 # find every node whose parents have been pruned
1463 # find every node whose parents have been pruned
1463 subset = []
1464 subset = []
1464 # find every remote head that will get new children
1465 # find every remote head that will get new children
1465 updated_heads = set()
1466 updated_heads = set()
1466 for n in remain:
1467 for n in remain:
1467 p1, p2 = self.changelog.parents(n)
1468 p1, p2 = self.changelog.parents(n)
1468 if p1 not in remain and p2 not in remain:
1469 if p1 not in remain and p2 not in remain:
1469 subset.append(n)
1470 subset.append(n)
1470 if heads:
1471 if heads:
1471 if p1 in heads:
1472 if p1 in heads:
1472 updated_heads.add(p1)
1473 updated_heads.add(p1)
1473 if p2 in heads:
1474 if p2 in heads:
1474 updated_heads.add(p2)
1475 updated_heads.add(p2)
1475
1476
1476 # this is the set of all roots we have to push
1477 # this is the set of all roots we have to push
1477 if heads:
1478 if heads:
1478 return subset, list(updated_heads)
1479 return subset, list(updated_heads)
1479 else:
1480 else:
1480 return subset
1481 return subset
1481
1482
1482 def pull(self, remote, heads=None, force=False):
1483 def pull(self, remote, heads=None, force=False):
1483 lock = self.lock()
1484 lock = self.lock()
1484 try:
1485 try:
1485 common, fetch, rheads = self.findcommonincoming(remote, heads=heads,
1486 common, fetch, rheads = self.findcommonincoming(remote, heads=heads,
1486 force=force)
1487 force=force)
1487 if not fetch:
1488 if not fetch:
1488 self.ui.status(_("no changes found\n"))
1489 self.ui.status(_("no changes found\n"))
1489 return 0
1490 return 0
1490
1491
1491 if fetch == [nullid]:
1492 if fetch == [nullid]:
1492 self.ui.status(_("requesting all changes\n"))
1493 self.ui.status(_("requesting all changes\n"))
1493 elif heads is None and remote.capable('changegroupsubset'):
1494 elif heads is None and remote.capable('changegroupsubset'):
1494 # issue1320, avoid a race if remote changed after discovery
1495 # issue1320, avoid a race if remote changed after discovery
1495 heads = rheads
1496 heads = rheads
1496
1497
1497 if heads is None:
1498 if heads is None:
1498 cg = remote.changegroup(fetch, 'pull')
1499 cg = remote.changegroup(fetch, 'pull')
1499 else:
1500 else:
1500 if not remote.capable('changegroupsubset'):
1501 if not remote.capable('changegroupsubset'):
1501 raise util.Abort(_("Partial pull cannot be done because "
1502 raise util.Abort(_("Partial pull cannot be done because "
1502 "other repository doesn't support "
1503 "other repository doesn't support "
1503 "changegroupsubset."))
1504 "changegroupsubset."))
1504 cg = remote.changegroupsubset(fetch, heads, 'pull')
1505 cg = remote.changegroupsubset(fetch, heads, 'pull')
1505 return self.addchangegroup(cg, 'pull', remote.url())
1506 return self.addchangegroup(cg, 'pull', remote.url())
1506 finally:
1507 finally:
1507 lock.release()
1508 lock.release()
1508
1509
1509 def push(self, remote, force=False, revs=None):
1510 def push(self, remote, force=False, revs=None):
1510 '''Push outgoing changesets (limited by revs) from the current
1511 '''Push outgoing changesets (limited by revs) from the current
1511 repository to remote. Return an integer:
1512 repository to remote. Return an integer:
1512 - 0 means HTTP error *or* nothing to push
1513 - 0 means HTTP error *or* nothing to push
1513 - 1 means we pushed and remote head count is unchanged *or*
1514 - 1 means we pushed and remote head count is unchanged *or*
1514 we have outgoing changesets but refused to push
1515 we have outgoing changesets but refused to push
1515 - other values as described by addchangegroup()
1516 - other values as described by addchangegroup()
1516 '''
1517 '''
1517 # there are two ways to push to remote repo:
1518 # there are two ways to push to remote repo:
1518 #
1519 #
1519 # addchangegroup assumes local user can lock remote
1520 # addchangegroup assumes local user can lock remote
1520 # repo (local filesystem, old ssh servers).
1521 # repo (local filesystem, old ssh servers).
1521 #
1522 #
1522 # unbundle assumes local user cannot lock remote repo (new ssh
1523 # unbundle assumes local user cannot lock remote repo (new ssh
1523 # servers, http servers).
1524 # servers, http servers).
1524
1525
1525 if remote.capable('unbundle'):
1526 if remote.capable('unbundle'):
1526 return self.push_unbundle(remote, force, revs)
1527 return self.push_unbundle(remote, force, revs)
1527 return self.push_addchangegroup(remote, force, revs)
1528 return self.push_addchangegroup(remote, force, revs)
1528
1529
1529 def prepush(self, remote, force, revs):
1530 def prepush(self, remote, force, revs):
1530 '''Analyze the local and remote repositories and determine which
1531 '''Analyze the local and remote repositories and determine which
1531 changesets need to be pushed to the remote. Return value depends
1532 changesets need to be pushed to the remote. Return value depends
1532 on circumstances:
1533 on circumstances:
1533
1534
1534 If we are not going to push anything, return a tuple (None,
1535 If we are not going to push anything, return a tuple (None,
1535 outgoing) where outgoing is 0 if there are no outgoing
1536 outgoing) where outgoing is 0 if there are no outgoing
1536 changesets and 1 if there are, but we refuse to push them
1537 changesets and 1 if there are, but we refuse to push them
1537 (e.g. would create new remote heads).
1538 (e.g. would create new remote heads).
1538
1539
1539 Otherwise, return a tuple (changegroup, remoteheads), where
1540 Otherwise, return a tuple (changegroup, remoteheads), where
1540 changegroup is a readable file-like object whose read() returns
1541 changegroup is a readable file-like object whose read() returns
1541 successive changegroup chunks ready to be sent over the wire and
1542 successive changegroup chunks ready to be sent over the wire and
1542 remoteheads is the list of remote heads.'''
1543 remoteheads is the list of remote heads.'''
1543 common = {}
1544 common = {}
1544 remote_heads = remote.heads()
1545 remote_heads = remote.heads()
1545 inc = self.findincoming(remote, common, remote_heads, force=force)
1546 inc = self.findincoming(remote, common, remote_heads, force=force)
1546
1547
1547 cl = self.changelog
1548 cl = self.changelog
1548 update, updated_heads = self.findoutgoing(remote, common, remote_heads)
1549 update, updated_heads = self.findoutgoing(remote, common, remote_heads)
1549 outg, bases, heads = cl.nodesbetween(update, revs)
1550 outg, bases, heads = cl.nodesbetween(update, revs)
1550
1551
1551 if not bases:
1552 if not bases:
1552 self.ui.status(_("no changes found\n"))
1553 self.ui.status(_("no changes found\n"))
1553 return None, 1
1554 return None, 1
1554
1555
1555 if not force and remote_heads != [nullid]:
1556 if not force and remote_heads != [nullid]:
1556
1557
1557 def fail_multiple_heads(unsynced, branch=None):
1558 def fail_multiple_heads(unsynced, branch=None):
1558 if branch:
1559 if branch:
1559 msg = _("abort: push creates new remote heads"
1560 msg = _("abort: push creates new remote heads"
1560 " on branch '%s'!\n") % branch
1561 " on branch '%s'!\n") % branch
1561 else:
1562 else:
1562 msg = _("abort: push creates new remote heads!\n")
1563 msg = _("abort: push creates new remote heads!\n")
1563 self.ui.warn(msg)
1564 self.ui.warn(msg)
1564 if unsynced:
1565 if unsynced:
1565 self.ui.status(_("(you should pull and merge or"
1566 self.ui.status(_("(you should pull and merge or"
1566 " use push -f to force)\n"))
1567 " use push -f to force)\n"))
1567 else:
1568 else:
1568 self.ui.status(_("(did you forget to merge?"
1569 self.ui.status(_("(did you forget to merge?"
1569 " use push -f to force)\n"))
1570 " use push -f to force)\n"))
1570 return None, 0
1571 return None, 0
1571
1572
1572 if remote.capable('branchmap'):
1573 if remote.capable('branchmap'):
1573 # Check for each named branch if we're creating new remote heads.
1574 # Check for each named branch if we're creating new remote heads.
1574 # To be a remote head after push, node must be either:
1575 # To be a remote head after push, node must be either:
1575 # - unknown locally
1576 # - unknown locally
1576 # - a local outgoing head descended from update
1577 # - a local outgoing head descended from update
1577 # - a remote head that's known locally and not
1578 # - a remote head that's known locally and not
1578 # ancestral to an outgoing head
1579 # ancestral to an outgoing head
1579 #
1580 #
1580 # New named branches cannot be created without --force.
1581 # New named branches cannot be created without --force.
1581
1582
1582 # 1. Create set of branches involved in the push.
1583 # 1. Create set of branches involved in the push.
1583 branches = set(self[n].branch() for n in outg)
1584 branches = set(self[n].branch() for n in outg)
1584
1585
1585 # 2. Check for new branches on the remote.
1586 # 2. Check for new branches on the remote.
1586 remotemap = remote.branchmap()
1587 remotemap = remote.branchmap()
1587 newbranches = branches - set(remotemap)
1588 newbranches = branches - set(remotemap)
1588 if newbranches: # new branch requires --force
1589 if newbranches: # new branch requires --force
1589 branchnames = ', '.join("%s" % b for b in newbranches)
1590 branchnames = ', '.join("%s" % b for b in newbranches)
1590 self.ui.warn(_("abort: push creates "
1591 self.ui.warn(_("abort: push creates "
1591 "new remote branches: %s!\n")
1592 "new remote branches: %s!\n")
1592 % branchnames)
1593 % branchnames)
1593 self.ui.status(_("(use 'hg push -f' to force)\n"))
1594 self.ui.status(_("(use 'hg push -f' to force)\n"))
1594 return None, 0
1595 return None, 0
1595
1596
1596 # 3. Construct the initial oldmap and newmap dicts.
1597 # 3. Construct the initial oldmap and newmap dicts.
1597 # They contain information about the remote heads before and
1598 # They contain information about the remote heads before and
1598 # after the push, respectively.
1599 # after the push, respectively.
1599 # Heads not found locally are not included in either dict,
1600 # Heads not found locally are not included in either dict,
1600 # since they won't be affected by the push.
1601 # since they won't be affected by the push.
1601 # unsynced contains all branches with incoming changesets.
1602 # unsynced contains all branches with incoming changesets.
1602 oldmap = {}
1603 oldmap = {}
1603 newmap = {}
1604 newmap = {}
1604 unsynced = set()
1605 unsynced = set()
1605 for branch in branches:
1606 for branch in branches:
1606 remoteheads = remotemap[branch]
1607 remoteheads = remotemap[branch]
1607 prunedheads = [h for h in remoteheads if h in cl.nodemap]
1608 prunedheads = [h for h in remoteheads if h in cl.nodemap]
1608 oldmap[branch] = prunedheads
1609 oldmap[branch] = prunedheads
1609 newmap[branch] = list(prunedheads)
1610 newmap[branch] = list(prunedheads)
1610 if len(remoteheads) > len(prunedheads):
1611 if len(remoteheads) > len(prunedheads):
1611 unsynced.add(branch)
1612 unsynced.add(branch)
1612
1613
1613 # 4. Update newmap with outgoing changes.
1614 # 4. Update newmap with outgoing changes.
1614 # This will possibly add new heads and remove existing ones.
1615 # This will possibly add new heads and remove existing ones.
1615 ctxgen = (self[n] for n in outg)
1616 ctxgen = (self[n] for n in outg)
1616 self._updatebranchcache(newmap, ctxgen)
1617 self._updatebranchcache(newmap, ctxgen)
1617
1618
1618 # 5. Check for new heads.
1619 # 5. Check for new heads.
1619 # If there are more heads after the push than before, a suitable
1620 # If there are more heads after the push than before, a suitable
1620 # warning, depending on unsynced status, is displayed.
1621 # warning, depending on unsynced status, is displayed.
1621 for branch in branches:
1622 for branch in branches:
1622 if len(newmap[branch]) > len(oldmap[branch]):
1623 if len(newmap[branch]) > len(oldmap[branch]):
1623 return fail_multiple_heads(branch in unsynced, branch)
1624 return fail_multiple_heads(branch in unsynced, branch)
1624
1625
1625 # 6. Check for unsynced changes on involved branches.
1626 # 6. Check for unsynced changes on involved branches.
1626 if unsynced:
1627 if unsynced:
1627 self.ui.warn(_("note: unsynced remote changes!\n"))
1628 self.ui.warn(_("note: unsynced remote changes!\n"))
1628
1629
1629 else:
1630 else:
1630 # Old servers: Check for new topological heads.
1631 # Old servers: Check for new topological heads.
1631 # Code based on _updatebranchcache.
1632 # Code based on _updatebranchcache.
1632 newheads = set(h for h in remote_heads if h in cl.nodemap)
1633 newheads = set(h for h in remote_heads if h in cl.nodemap)
1633 oldheadcnt = len(newheads)
1634 oldheadcnt = len(newheads)
1634 newheads.update(outg)
1635 newheads.update(outg)
1635 if len(newheads) > 1:
1636 if len(newheads) > 1:
1636 for latest in reversed(outg):
1637 for latest in reversed(outg):
1637 if latest not in newheads:
1638 if latest not in newheads:
1638 continue
1639 continue
1639 minhrev = min(cl.rev(h) for h in newheads)
1640 minhrev = min(cl.rev(h) for h in newheads)
1640 reachable = cl.reachable(latest, cl.node(minhrev))
1641 reachable = cl.reachable(latest, cl.node(minhrev))
1641 reachable.remove(latest)
1642 reachable.remove(latest)
1642 newheads.difference_update(reachable)
1643 newheads.difference_update(reachable)
1643 if len(newheads) > oldheadcnt:
1644 if len(newheads) > oldheadcnt:
1644 return fail_multiple_heads(inc)
1645 return fail_multiple_heads(inc)
1645 if inc:
1646 if inc:
1646 self.ui.warn(_("note: unsynced remote changes!\n"))
1647 self.ui.warn(_("note: unsynced remote changes!\n"))
1647
1648
1648 if revs is None:
1649 if revs is None:
1649 # use the fast path, no race possible on push
1650 # use the fast path, no race possible on push
1650 nodes = self.changelog.findmissing(common.keys())
1651 nodes = self.changelog.findmissing(common.keys())
1651 cg = self._changegroup(nodes, 'push')
1652 cg = self._changegroup(nodes, 'push')
1652 else:
1653 else:
1653 cg = self.changegroupsubset(update, revs, 'push')
1654 cg = self.changegroupsubset(update, revs, 'push')
1654 return cg, remote_heads
1655 return cg, remote_heads
1655
1656
1656 def push_addchangegroup(self, remote, force, revs):
1657 def push_addchangegroup(self, remote, force, revs):
1657 '''Push a changegroup by locking the remote and sending the
1658 '''Push a changegroup by locking the remote and sending the
1658 addchangegroup command to it. Used for local and old SSH repos.
1659 addchangegroup command to it. Used for local and old SSH repos.
1659 Return an integer: see push().
1660 Return an integer: see push().
1660 '''
1661 '''
1661 lock = remote.lock()
1662 lock = remote.lock()
1662 try:
1663 try:
1663 ret = self.prepush(remote, force, revs)
1664 ret = self.prepush(remote, force, revs)
1664 if ret[0] is not None:
1665 if ret[0] is not None:
1665 cg, remote_heads = ret
1666 cg, remote_heads = ret
1666 # here, we return an integer indicating remote head count change
1667 # here, we return an integer indicating remote head count change
1667 return remote.addchangegroup(cg, 'push', self.url())
1668 return remote.addchangegroup(cg, 'push', self.url())
1668 # and here we return 0 for "nothing to push" or 1 for
1669 # and here we return 0 for "nothing to push" or 1 for
1669 # "something to push but I refuse"
1670 # "something to push but I refuse"
1670 return ret[1]
1671 return ret[1]
1671 finally:
1672 finally:
1672 lock.release()
1673 lock.release()
1673
1674
1674 def push_unbundle(self, remote, force, revs):
1675 def push_unbundle(self, remote, force, revs):
1675 '''Push a changegroup by unbundling it on the remote. Used for new
1676 '''Push a changegroup by unbundling it on the remote. Used for new
1676 SSH and HTTP repos. Return an integer: see push().'''
1677 SSH and HTTP repos. Return an integer: see push().'''
1677 # local repo finds heads on server, finds out what revs it
1678 # local repo finds heads on server, finds out what revs it
1678 # must push. once revs transferred, if server finds it has
1679 # must push. once revs transferred, if server finds it has
1679 # different heads (someone else won commit/push race), server
1680 # different heads (someone else won commit/push race), server
1680 # aborts.
1681 # aborts.
1681
1682
1682 ret = self.prepush(remote, force, revs)
1683 ret = self.prepush(remote, force, revs)
1683 if ret[0] is not None:
1684 if ret[0] is not None:
1684 cg, remote_heads = ret
1685 cg, remote_heads = ret
1685 if force:
1686 if force:
1686 remote_heads = ['force']
1687 remote_heads = ['force']
1687 # ssh: return remote's addchangegroup()
1688 # ssh: return remote's addchangegroup()
1688 # http: return remote's addchangegroup() or 0 for error
1689 # http: return remote's addchangegroup() or 0 for error
1689 return remote.unbundle(cg, remote_heads, 'push')
1690 return remote.unbundle(cg, remote_heads, 'push')
1690 # as in push_addchangegroup()
1691 # as in push_addchangegroup()
1691 return ret[1]
1692 return ret[1]
1692
1693
1693 def changegroupinfo(self, nodes, source):
1694 def changegroupinfo(self, nodes, source):
1694 if self.ui.verbose or source == 'bundle':
1695 if self.ui.verbose or source == 'bundle':
1695 self.ui.status(_("%d changesets found\n") % len(nodes))
1696 self.ui.status(_("%d changesets found\n") % len(nodes))
1696 if self.ui.debugflag:
1697 if self.ui.debugflag:
1697 self.ui.debug("list of changesets:\n")
1698 self.ui.debug("list of changesets:\n")
1698 for node in nodes:
1699 for node in nodes:
1699 self.ui.debug("%s\n" % hex(node))
1700 self.ui.debug("%s\n" % hex(node))
1700
1701
1701 def changegroupsubset(self, bases, heads, source, extranodes=None):
1702 def changegroupsubset(self, bases, heads, source, extranodes=None):
1702 """Compute a changegroup consisting of all the nodes that are
1703 """Compute a changegroup consisting of all the nodes that are
1703 descendents of any of the bases and ancestors of any of the heads.
1704 descendents of any of the bases and ancestors of any of the heads.
1704 Return a chunkbuffer object whose read() method will return
1705 Return a chunkbuffer object whose read() method will return
1705 successive changegroup chunks.
1706 successive changegroup chunks.
1706
1707
1707 It is fairly complex as determining which filenodes and which
1708 It is fairly complex as determining which filenodes and which
1708 manifest nodes need to be included for the changeset to be complete
1709 manifest nodes need to be included for the changeset to be complete
1709 is non-trivial.
1710 is non-trivial.
1710
1711
1711 Another wrinkle is doing the reverse, figuring out which changeset in
1712 Another wrinkle is doing the reverse, figuring out which changeset in
1712 the changegroup a particular filenode or manifestnode belongs to.
1713 the changegroup a particular filenode or manifestnode belongs to.
1713
1714
1714 The caller can specify some nodes that must be included in the
1715 The caller can specify some nodes that must be included in the
1715 changegroup using the extranodes argument. It should be a dict
1716 changegroup using the extranodes argument. It should be a dict
1716 where the keys are the filenames (or 1 for the manifest), and the
1717 where the keys are the filenames (or 1 for the manifest), and the
1717 values are lists of (node, linknode) tuples, where node is a wanted
1718 values are lists of (node, linknode) tuples, where node is a wanted
1718 node and linknode is the changelog node that should be transmitted as
1719 node and linknode is the changelog node that should be transmitted as
1719 the linkrev.
1720 the linkrev.
1720 """
1721 """
1721
1722
1722 # Set up some initial variables
1723 # Set up some initial variables
1723 # Make it easy to refer to self.changelog
1724 # Make it easy to refer to self.changelog
1724 cl = self.changelog
1725 cl = self.changelog
1725 # msng is short for missing - compute the list of changesets in this
1726 # msng is short for missing - compute the list of changesets in this
1726 # changegroup.
1727 # changegroup.
1727 if not bases:
1728 if not bases:
1728 bases = [nullid]
1729 bases = [nullid]
1729 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1730 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1730
1731
1731 if extranodes is None:
1732 if extranodes is None:
1732 # can we go through the fast path ?
1733 # can we go through the fast path ?
1733 heads.sort()
1734 heads.sort()
1734 allheads = self.heads()
1735 allheads = self.heads()
1735 allheads.sort()
1736 allheads.sort()
1736 if heads == allheads:
1737 if heads == allheads:
1737 return self._changegroup(msng_cl_lst, source)
1738 return self._changegroup(msng_cl_lst, source)
1738
1739
1739 # slow path
1740 # slow path
1740 self.hook('preoutgoing', throw=True, source=source)
1741 self.hook('preoutgoing', throw=True, source=source)
1741
1742
1742 self.changegroupinfo(msng_cl_lst, source)
1743 self.changegroupinfo(msng_cl_lst, source)
1743 # Some bases may turn out to be superfluous, and some heads may be
1744 # Some bases may turn out to be superfluous, and some heads may be
1744 # too. nodesbetween will return the minimal set of bases and heads
1745 # too. nodesbetween will return the minimal set of bases and heads
1745 # necessary to re-create the changegroup.
1746 # necessary to re-create the changegroup.
1746
1747
1747 # Known heads are the list of heads that it is assumed the recipient
1748 # Known heads are the list of heads that it is assumed the recipient
1748 # of this changegroup will know about.
1749 # of this changegroup will know about.
1749 knownheads = set()
1750 knownheads = set()
1750 # We assume that all parents of bases are known heads.
1751 # We assume that all parents of bases are known heads.
1751 for n in bases:
1752 for n in bases:
1752 knownheads.update(cl.parents(n))
1753 knownheads.update(cl.parents(n))
1753 knownheads.discard(nullid)
1754 knownheads.discard(nullid)
1754 knownheads = list(knownheads)
1755 knownheads = list(knownheads)
1755 if knownheads:
1756 if knownheads:
1756 # Now that we know what heads are known, we can compute which
1757 # Now that we know what heads are known, we can compute which
1757 # changesets are known. The recipient must know about all
1758 # changesets are known. The recipient must know about all
1758 # changesets required to reach the known heads from the null
1759 # changesets required to reach the known heads from the null
1759 # changeset.
1760 # changeset.
1760 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1761 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1761 junk = None
1762 junk = None
1762 # Transform the list into a set.
1763 # Transform the list into a set.
1763 has_cl_set = set(has_cl_set)
1764 has_cl_set = set(has_cl_set)
1764 else:
1765 else:
1765 # If there were no known heads, the recipient cannot be assumed to
1766 # If there were no known heads, the recipient cannot be assumed to
1766 # know about any changesets.
1767 # know about any changesets.
1767 has_cl_set = set()
1768 has_cl_set = set()
1768
1769
1769 # Make it easy to refer to self.manifest
1770 # Make it easy to refer to self.manifest
1770 mnfst = self.manifest
1771 mnfst = self.manifest
1771 # We don't know which manifests are missing yet
1772 # We don't know which manifests are missing yet
1772 msng_mnfst_set = {}
1773 msng_mnfst_set = {}
1773 # Nor do we know which filenodes are missing.
1774 # Nor do we know which filenodes are missing.
1774 msng_filenode_set = {}
1775 msng_filenode_set = {}
1775
1776
1776 junk = mnfst.index[len(mnfst) - 1] # Get around a bug in lazyindex
1777 junk = mnfst.index[len(mnfst) - 1] # Get around a bug in lazyindex
1777 junk = None
1778 junk = None
1778
1779
1779 # A changeset always belongs to itself, so the changenode lookup
1780 # A changeset always belongs to itself, so the changenode lookup
1780 # function for a changenode is identity.
1781 # function for a changenode is identity.
1781 def identity(x):
1782 def identity(x):
1782 return x
1783 return x
1783
1784
1784 # If we determine that a particular file or manifest node must be a
1785 # If we determine that a particular file or manifest node must be a
1785 # node that the recipient of the changegroup will already have, we can
1786 # node that the recipient of the changegroup will already have, we can
1786 # also assume the recipient will have all the parents. This function
1787 # also assume the recipient will have all the parents. This function
1787 # prunes them from the set of missing nodes.
1788 # prunes them from the set of missing nodes.
1788 def prune_parents(revlog, hasset, msngset):
1789 def prune_parents(revlog, hasset, msngset):
1789 for r in revlog.ancestors(*[revlog.rev(n) for n in hasset]):
1790 for r in revlog.ancestors(*[revlog.rev(n) for n in hasset]):
1790 msngset.pop(revlog.node(r), None)
1791 msngset.pop(revlog.node(r), None)
1791
1792
1792 # Use the information collected in collect_manifests_and_files to say
1793 # Use the information collected in collect_manifests_and_files to say
1793 # which changenode any manifestnode belongs to.
1794 # which changenode any manifestnode belongs to.
1794 def lookup_manifest_link(mnfstnode):
1795 def lookup_manifest_link(mnfstnode):
1795 return msng_mnfst_set[mnfstnode]
1796 return msng_mnfst_set[mnfstnode]
1796
1797
1797 # A function generating function that sets up the initial environment
1798 # A function generating function that sets up the initial environment
1798 # the inner function.
1799 # the inner function.
1799 def filenode_collector(changedfiles):
1800 def filenode_collector(changedfiles):
1800 # This gathers information from each manifestnode included in the
1801 # This gathers information from each manifestnode included in the
1801 # changegroup about which filenodes the manifest node references
1802 # changegroup about which filenodes the manifest node references
1802 # so we can include those in the changegroup too.
1803 # so we can include those in the changegroup too.
1803 #
1804 #
1804 # It also remembers which changenode each filenode belongs to. It
1805 # It also remembers which changenode each filenode belongs to. It
1805 # does this by assuming the a filenode belongs to the changenode
1806 # does this by assuming the a filenode belongs to the changenode
1806 # the first manifest that references it belongs to.
1807 # the first manifest that references it belongs to.
1807 def collect_msng_filenodes(mnfstnode):
1808 def collect_msng_filenodes(mnfstnode):
1808 r = mnfst.rev(mnfstnode)
1809 r = mnfst.rev(mnfstnode)
1809 if r - 1 in mnfst.parentrevs(r):
1810 if r - 1 in mnfst.parentrevs(r):
1810 # If the previous rev is one of the parents,
1811 # If the previous rev is one of the parents,
1811 # we only need to see a diff.
1812 # we only need to see a diff.
1812 deltamf = mnfst.readdelta(mnfstnode)
1813 deltamf = mnfst.readdelta(mnfstnode)
1813 # For each line in the delta
1814 # For each line in the delta
1814 for f, fnode in deltamf.iteritems():
1815 for f, fnode in deltamf.iteritems():
1815 f = changedfiles.get(f, None)
1816 f = changedfiles.get(f, None)
1816 # And if the file is in the list of files we care
1817 # And if the file is in the list of files we care
1817 # about.
1818 # about.
1818 if f is not None:
1819 if f is not None:
1819 # Get the changenode this manifest belongs to
1820 # Get the changenode this manifest belongs to
1820 clnode = msng_mnfst_set[mnfstnode]
1821 clnode = msng_mnfst_set[mnfstnode]
1821 # Create the set of filenodes for the file if
1822 # Create the set of filenodes for the file if
1822 # there isn't one already.
1823 # there isn't one already.
1823 ndset = msng_filenode_set.setdefault(f, {})
1824 ndset = msng_filenode_set.setdefault(f, {})
1824 # And set the filenode's changelog node to the
1825 # And set the filenode's changelog node to the
1825 # manifest's if it hasn't been set already.
1826 # manifest's if it hasn't been set already.
1826 ndset.setdefault(fnode, clnode)
1827 ndset.setdefault(fnode, clnode)
1827 else:
1828 else:
1828 # Otherwise we need a full manifest.
1829 # Otherwise we need a full manifest.
1829 m = mnfst.read(mnfstnode)
1830 m = mnfst.read(mnfstnode)
1830 # For every file in we care about.
1831 # For every file in we care about.
1831 for f in changedfiles:
1832 for f in changedfiles:
1832 fnode = m.get(f, None)
1833 fnode = m.get(f, None)
1833 # If it's in the manifest
1834 # If it's in the manifest
1834 if fnode is not None:
1835 if fnode is not None:
1835 # See comments above.
1836 # See comments above.
1836 clnode = msng_mnfst_set[mnfstnode]
1837 clnode = msng_mnfst_set[mnfstnode]
1837 ndset = msng_filenode_set.setdefault(f, {})
1838 ndset = msng_filenode_set.setdefault(f, {})
1838 ndset.setdefault(fnode, clnode)
1839 ndset.setdefault(fnode, clnode)
1839 return collect_msng_filenodes
1840 return collect_msng_filenodes
1840
1841
1841 # We have a list of filenodes we think we need for a file, lets remove
1842 # We have a list of filenodes we think we need for a file, lets remove
1842 # all those we know the recipient must have.
1843 # all those we know the recipient must have.
1843 def prune_filenodes(f, filerevlog):
1844 def prune_filenodes(f, filerevlog):
1844 msngset = msng_filenode_set[f]
1845 msngset = msng_filenode_set[f]
1845 hasset = set()
1846 hasset = set()
1846 # If a 'missing' filenode thinks it belongs to a changenode we
1847 # If a 'missing' filenode thinks it belongs to a changenode we
1847 # assume the recipient must have, then the recipient must have
1848 # assume the recipient must have, then the recipient must have
1848 # that filenode.
1849 # that filenode.
1849 for n in msngset:
1850 for n in msngset:
1850 clnode = cl.node(filerevlog.linkrev(filerevlog.rev(n)))
1851 clnode = cl.node(filerevlog.linkrev(filerevlog.rev(n)))
1851 if clnode in has_cl_set:
1852 if clnode in has_cl_set:
1852 hasset.add(n)
1853 hasset.add(n)
1853 prune_parents(filerevlog, hasset, msngset)
1854 prune_parents(filerevlog, hasset, msngset)
1854
1855
1855 # A function generator function that sets up the a context for the
1856 # A function generator function that sets up the a context for the
1856 # inner function.
1857 # inner function.
1857 def lookup_filenode_link_func(fname):
1858 def lookup_filenode_link_func(fname):
1858 msngset = msng_filenode_set[fname]
1859 msngset = msng_filenode_set[fname]
1859 # Lookup the changenode the filenode belongs to.
1860 # Lookup the changenode the filenode belongs to.
1860 def lookup_filenode_link(fnode):
1861 def lookup_filenode_link(fnode):
1861 return msngset[fnode]
1862 return msngset[fnode]
1862 return lookup_filenode_link
1863 return lookup_filenode_link
1863
1864
1864 # Add the nodes that were explicitly requested.
1865 # Add the nodes that were explicitly requested.
1865 def add_extra_nodes(name, nodes):
1866 def add_extra_nodes(name, nodes):
1866 if not extranodes or name not in extranodes:
1867 if not extranodes or name not in extranodes:
1867 return
1868 return
1868
1869
1869 for node, linknode in extranodes[name]:
1870 for node, linknode in extranodes[name]:
1870 if node not in nodes:
1871 if node not in nodes:
1871 nodes[node] = linknode
1872 nodes[node] = linknode
1872
1873
1873 # Now that we have all theses utility functions to help out and
1874 # Now that we have all theses utility functions to help out and
1874 # logically divide up the task, generate the group.
1875 # logically divide up the task, generate the group.
1875 def gengroup():
1876 def gengroup():
1876 # The set of changed files starts empty.
1877 # The set of changed files starts empty.
1877 changedfiles = {}
1878 changedfiles = {}
1878 collect = changegroup.collector(cl, msng_mnfst_set, changedfiles)
1879 collect = changegroup.collector(cl, msng_mnfst_set, changedfiles)
1879
1880
1880 # Create a changenode group generator that will call our functions
1881 # Create a changenode group generator that will call our functions
1881 # back to lookup the owning changenode and collect information.
1882 # back to lookup the owning changenode and collect information.
1882 group = cl.group(msng_cl_lst, identity, collect)
1883 group = cl.group(msng_cl_lst, identity, collect)
1883 cnt = 0
1884 cnt = 0
1884 for chnk in group:
1885 for chnk in group:
1885 yield chnk
1886 yield chnk
1886 self.ui.progress(_('bundling changes'), cnt, unit=_('chunks'))
1887 self.ui.progress(_('bundling changes'), cnt, unit=_('chunks'))
1887 cnt += 1
1888 cnt += 1
1888 self.ui.progress(_('bundling changes'), None)
1889 self.ui.progress(_('bundling changes'), None)
1889
1890
1890
1891
1891 # Figure out which manifest nodes (of the ones we think might be
1892 # Figure out which manifest nodes (of the ones we think might be
1892 # part of the changegroup) the recipient must know about and
1893 # part of the changegroup) the recipient must know about and
1893 # remove them from the changegroup.
1894 # remove them from the changegroup.
1894 has_mnfst_set = set()
1895 has_mnfst_set = set()
1895 for n in msng_mnfst_set:
1896 for n in msng_mnfst_set:
1896 # If a 'missing' manifest thinks it belongs to a changenode
1897 # If a 'missing' manifest thinks it belongs to a changenode
1897 # the recipient is assumed to have, obviously the recipient
1898 # the recipient is assumed to have, obviously the recipient
1898 # must have that manifest.
1899 # must have that manifest.
1899 linknode = cl.node(mnfst.linkrev(mnfst.rev(n)))
1900 linknode = cl.node(mnfst.linkrev(mnfst.rev(n)))
1900 if linknode in has_cl_set:
1901 if linknode in has_cl_set:
1901 has_mnfst_set.add(n)
1902 has_mnfst_set.add(n)
1902 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1903 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1903 add_extra_nodes(1, msng_mnfst_set)
1904 add_extra_nodes(1, msng_mnfst_set)
1904 msng_mnfst_lst = msng_mnfst_set.keys()
1905 msng_mnfst_lst = msng_mnfst_set.keys()
1905 # Sort the manifestnodes by revision number.
1906 # Sort the manifestnodes by revision number.
1906 msng_mnfst_lst.sort(key=mnfst.rev)
1907 msng_mnfst_lst.sort(key=mnfst.rev)
1907 # Create a generator for the manifestnodes that calls our lookup
1908 # Create a generator for the manifestnodes that calls our lookup
1908 # and data collection functions back.
1909 # and data collection functions back.
1909 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1910 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1910 filenode_collector(changedfiles))
1911 filenode_collector(changedfiles))
1911 cnt = 0
1912 cnt = 0
1912 for chnk in group:
1913 for chnk in group:
1913 yield chnk
1914 yield chnk
1914 self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks'))
1915 self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks'))
1915 cnt += 1
1916 cnt += 1
1916 self.ui.progress(_('bundling manifests'), None)
1917 self.ui.progress(_('bundling manifests'), None)
1917
1918
1918 # These are no longer needed, dereference and toss the memory for
1919 # These are no longer needed, dereference and toss the memory for
1919 # them.
1920 # them.
1920 msng_mnfst_lst = None
1921 msng_mnfst_lst = None
1921 msng_mnfst_set.clear()
1922 msng_mnfst_set.clear()
1922
1923
1923 if extranodes:
1924 if extranodes:
1924 for fname in extranodes:
1925 for fname in extranodes:
1925 if isinstance(fname, int):
1926 if isinstance(fname, int):
1926 continue
1927 continue
1927 msng_filenode_set.setdefault(fname, {})
1928 msng_filenode_set.setdefault(fname, {})
1928 changedfiles[fname] = 1
1929 changedfiles[fname] = 1
1929 # Go through all our files in order sorted by name.
1930 # Go through all our files in order sorted by name.
1930 cnt = 0
1931 cnt = 0
1931 for fname in sorted(changedfiles):
1932 for fname in sorted(changedfiles):
1932 filerevlog = self.file(fname)
1933 filerevlog = self.file(fname)
1933 if not len(filerevlog):
1934 if not len(filerevlog):
1934 raise util.Abort(_("empty or missing revlog for %s") % fname)
1935 raise util.Abort(_("empty or missing revlog for %s") % fname)
1935 # Toss out the filenodes that the recipient isn't really
1936 # Toss out the filenodes that the recipient isn't really
1936 # missing.
1937 # missing.
1937 if fname in msng_filenode_set:
1938 if fname in msng_filenode_set:
1938 prune_filenodes(fname, filerevlog)
1939 prune_filenodes(fname, filerevlog)
1939 add_extra_nodes(fname, msng_filenode_set[fname])
1940 add_extra_nodes(fname, msng_filenode_set[fname])
1940 msng_filenode_lst = msng_filenode_set[fname].keys()
1941 msng_filenode_lst = msng_filenode_set[fname].keys()
1941 else:
1942 else:
1942 msng_filenode_lst = []
1943 msng_filenode_lst = []
1943 # If any filenodes are left, generate the group for them,
1944 # If any filenodes are left, generate the group for them,
1944 # otherwise don't bother.
1945 # otherwise don't bother.
1945 if len(msng_filenode_lst) > 0:
1946 if len(msng_filenode_lst) > 0:
1946 yield changegroup.chunkheader(len(fname))
1947 yield changegroup.chunkheader(len(fname))
1947 yield fname
1948 yield fname
1948 # Sort the filenodes by their revision #
1949 # Sort the filenodes by their revision #
1949 msng_filenode_lst.sort(key=filerevlog.rev)
1950 msng_filenode_lst.sort(key=filerevlog.rev)
1950 # Create a group generator and only pass in a changenode
1951 # Create a group generator and only pass in a changenode
1951 # lookup function as we need to collect no information
1952 # lookup function as we need to collect no information
1952 # from filenodes.
1953 # from filenodes.
1953 group = filerevlog.group(msng_filenode_lst,
1954 group = filerevlog.group(msng_filenode_lst,
1954 lookup_filenode_link_func(fname))
1955 lookup_filenode_link_func(fname))
1955 for chnk in group:
1956 for chnk in group:
1956 self.ui.progress(
1957 self.ui.progress(
1957 _('bundling files'), cnt, item=fname, unit=_('chunks'))
1958 _('bundling files'), cnt, item=fname, unit=_('chunks'))
1958 cnt += 1
1959 cnt += 1
1959 yield chnk
1960 yield chnk
1960 if fname in msng_filenode_set:
1961 if fname in msng_filenode_set:
1961 # Don't need this anymore, toss it to free memory.
1962 # Don't need this anymore, toss it to free memory.
1962 del msng_filenode_set[fname]
1963 del msng_filenode_set[fname]
1963 # Signal that no more groups are left.
1964 # Signal that no more groups are left.
1964 yield changegroup.closechunk()
1965 yield changegroup.closechunk()
1965 self.ui.progress(_('bundling files'), None)
1966 self.ui.progress(_('bundling files'), None)
1966
1967
1967 if msng_cl_lst:
1968 if msng_cl_lst:
1968 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1969 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1969
1970
1970 return util.chunkbuffer(gengroup())
1971 return util.chunkbuffer(gengroup())
1971
1972
1972 def changegroup(self, basenodes, source):
1973 def changegroup(self, basenodes, source):
1973 # to avoid a race we use changegroupsubset() (issue1320)
1974 # to avoid a race we use changegroupsubset() (issue1320)
1974 return self.changegroupsubset(basenodes, self.heads(), source)
1975 return self.changegroupsubset(basenodes, self.heads(), source)
1975
1976
1976 def _changegroup(self, nodes, source):
1977 def _changegroup(self, nodes, source):
1977 """Compute the changegroup of all nodes that we have that a recipient
1978 """Compute the changegroup of all nodes that we have that a recipient
1978 doesn't. Return a chunkbuffer object whose read() method will return
1979 doesn't. Return a chunkbuffer object whose read() method will return
1979 successive changegroup chunks.
1980 successive changegroup chunks.
1980
1981
1981 This is much easier than the previous function as we can assume that
1982 This is much easier than the previous function as we can assume that
1982 the recipient has any changenode we aren't sending them.
1983 the recipient has any changenode we aren't sending them.
1983
1984
1984 nodes is the set of nodes to send"""
1985 nodes is the set of nodes to send"""
1985
1986
1986 self.hook('preoutgoing', throw=True, source=source)
1987 self.hook('preoutgoing', throw=True, source=source)
1987
1988
1988 cl = self.changelog
1989 cl = self.changelog
1989 revset = set([cl.rev(n) for n in nodes])
1990 revset = set([cl.rev(n) for n in nodes])
1990 self.changegroupinfo(nodes, source)
1991 self.changegroupinfo(nodes, source)
1991
1992
1992 def identity(x):
1993 def identity(x):
1993 return x
1994 return x
1994
1995
1995 def gennodelst(log):
1996 def gennodelst(log):
1996 for r in log:
1997 for r in log:
1997 if log.linkrev(r) in revset:
1998 if log.linkrev(r) in revset:
1998 yield log.node(r)
1999 yield log.node(r)
1999
2000
2000 def lookuprevlink_func(revlog):
2001 def lookuprevlink_func(revlog):
2001 def lookuprevlink(n):
2002 def lookuprevlink(n):
2002 return cl.node(revlog.linkrev(revlog.rev(n)))
2003 return cl.node(revlog.linkrev(revlog.rev(n)))
2003 return lookuprevlink
2004 return lookuprevlink
2004
2005
2005 def gengroup():
2006 def gengroup():
2006 '''yield a sequence of changegroup chunks (strings)'''
2007 '''yield a sequence of changegroup chunks (strings)'''
2007 # construct a list of all changed files
2008 # construct a list of all changed files
2008 changedfiles = {}
2009 changedfiles = {}
2009 mmfs = {}
2010 mmfs = {}
2010 collect = changegroup.collector(cl, mmfs, changedfiles)
2011 collect = changegroup.collector(cl, mmfs, changedfiles)
2011
2012
2012 cnt = 0
2013 cnt = 0
2013 for chnk in cl.group(nodes, identity, collect):
2014 for chnk in cl.group(nodes, identity, collect):
2014 self.ui.progress(_('bundling changes'), cnt, unit=_('chunks'))
2015 self.ui.progress(_('bundling changes'), cnt, unit=_('chunks'))
2015 cnt += 1
2016 cnt += 1
2016 yield chnk
2017 yield chnk
2017 self.ui.progress(_('bundling changes'), None)
2018 self.ui.progress(_('bundling changes'), None)
2018
2019
2019 mnfst = self.manifest
2020 mnfst = self.manifest
2020 nodeiter = gennodelst(mnfst)
2021 nodeiter = gennodelst(mnfst)
2021 cnt = 0
2022 cnt = 0
2022 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
2023 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
2023 self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks'))
2024 self.ui.progress(_('bundling manifests'), cnt, unit=_('chunks'))
2024 cnt += 1
2025 cnt += 1
2025 yield chnk
2026 yield chnk
2026 self.ui.progress(_('bundling manifests'), None)
2027 self.ui.progress(_('bundling manifests'), None)
2027
2028
2028 cnt = 0
2029 cnt = 0
2029 for fname in sorted(changedfiles):
2030 for fname in sorted(changedfiles):
2030 filerevlog = self.file(fname)
2031 filerevlog = self.file(fname)
2031 if not len(filerevlog):
2032 if not len(filerevlog):
2032 raise util.Abort(_("empty or missing revlog for %s") % fname)
2033 raise util.Abort(_("empty or missing revlog for %s") % fname)
2033 nodeiter = gennodelst(filerevlog)
2034 nodeiter = gennodelst(filerevlog)
2034 nodeiter = list(nodeiter)
2035 nodeiter = list(nodeiter)
2035 if nodeiter:
2036 if nodeiter:
2036 yield changegroup.chunkheader(len(fname))
2037 yield changegroup.chunkheader(len(fname))
2037 yield fname
2038 yield fname
2038 lookup = lookuprevlink_func(filerevlog)
2039 lookup = lookuprevlink_func(filerevlog)
2039 for chnk in filerevlog.group(nodeiter, lookup):
2040 for chnk in filerevlog.group(nodeiter, lookup):
2040 self.ui.progress(
2041 self.ui.progress(
2041 _('bundling files'), cnt, item=fname, unit=_('chunks'))
2042 _('bundling files'), cnt, item=fname, unit=_('chunks'))
2042 cnt += 1
2043 cnt += 1
2043 yield chnk
2044 yield chnk
2044 self.ui.progress(_('bundling files'), None)
2045 self.ui.progress(_('bundling files'), None)
2045
2046
2046 yield changegroup.closechunk()
2047 yield changegroup.closechunk()
2047
2048
2048 if nodes:
2049 if nodes:
2049 self.hook('outgoing', node=hex(nodes[0]), source=source)
2050 self.hook('outgoing', node=hex(nodes[0]), source=source)
2050
2051
2051 return util.chunkbuffer(gengroup())
2052 return util.chunkbuffer(gengroup())
2052
2053
2053 def addchangegroup(self, source, srctype, url, emptyok=False):
2054 def addchangegroup(self, source, srctype, url, emptyok=False):
2054 """Add the changegroup returned by source.read() to this repo.
2055 """Add the changegroup returned by source.read() to this repo.
2055 srctype is a string like 'push', 'pull', or 'unbundle'. url is
2056 srctype is a string like 'push', 'pull', or 'unbundle'. url is
2056 the URL of the repo where this changegroup is coming from.
2057 the URL of the repo where this changegroup is coming from.
2057
2058
2058 Return an integer summarizing the change to this repo:
2059 Return an integer summarizing the change to this repo:
2059 - nothing changed or no source: 0
2060 - nothing changed or no source: 0
2060 - more heads than before: 1+added heads (2..n)
2061 - more heads than before: 1+added heads (2..n)
2061 - fewer heads than before: -1-removed heads (-2..-n)
2062 - fewer heads than before: -1-removed heads (-2..-n)
2062 - number of heads stays the same: 1
2063 - number of heads stays the same: 1
2063 """
2064 """
2064 def csmap(x):
2065 def csmap(x):
2065 self.ui.debug("add changeset %s\n" % short(x))
2066 self.ui.debug("add changeset %s\n" % short(x))
2066 return len(cl)
2067 return len(cl)
2067
2068
2068 def revmap(x):
2069 def revmap(x):
2069 return cl.rev(x)
2070 return cl.rev(x)
2070
2071
2071 if not source:
2072 if not source:
2072 return 0
2073 return 0
2073
2074
2074 self.hook('prechangegroup', throw=True, source=srctype, url=url)
2075 self.hook('prechangegroup', throw=True, source=srctype, url=url)
2075
2076
2076 changesets = files = revisions = 0
2077 changesets = files = revisions = 0
2077 efiles = set()
2078 efiles = set()
2078
2079
2079 # write changelog data to temp files so concurrent readers will not see
2080 # write changelog data to temp files so concurrent readers will not see
2080 # inconsistent view
2081 # inconsistent view
2081 cl = self.changelog
2082 cl = self.changelog
2082 cl.delayupdate()
2083 cl.delayupdate()
2083 oldheads = len(cl.heads())
2084 oldheads = len(cl.heads())
2084
2085
2085 tr = self.transaction("\n".join([srctype, urlmod.hidepassword(url)]))
2086 tr = self.transaction("\n".join([srctype, urlmod.hidepassword(url)]))
2086 try:
2087 try:
2087 trp = weakref.proxy(tr)
2088 trp = weakref.proxy(tr)
2088 # pull off the changeset group
2089 # pull off the changeset group
2089 self.ui.status(_("adding changesets\n"))
2090 self.ui.status(_("adding changesets\n"))
2090 clstart = len(cl)
2091 clstart = len(cl)
2091 class prog(object):
2092 class prog(object):
2092 step = _('changesets')
2093 step = _('changesets')
2093 count = 1
2094 count = 1
2094 ui = self.ui
2095 ui = self.ui
2095 total = None
2096 total = None
2096 def __call__(self):
2097 def __call__(self):
2097 self.ui.progress(self.step, self.count, unit=_('chunks'),
2098 self.ui.progress(self.step, self.count, unit=_('chunks'),
2098 total=self.total)
2099 total=self.total)
2099 self.count += 1
2100 self.count += 1
2100 pr = prog()
2101 pr = prog()
2101 chunkiter = changegroup.chunkiter(source, progress=pr)
2102 chunkiter = changegroup.chunkiter(source, progress=pr)
2102 if cl.addgroup(chunkiter, csmap, trp) is None and not emptyok:
2103 if cl.addgroup(chunkiter, csmap, trp) is None and not emptyok:
2103 raise util.Abort(_("received changelog group is empty"))
2104 raise util.Abort(_("received changelog group is empty"))
2104 clend = len(cl)
2105 clend = len(cl)
2105 changesets = clend - clstart
2106 changesets = clend - clstart
2106 for c in xrange(clstart, clend):
2107 for c in xrange(clstart, clend):
2107 efiles.update(self[c].files())
2108 efiles.update(self[c].files())
2108 efiles = len(efiles)
2109 efiles = len(efiles)
2109 self.ui.progress(_('changesets'), None)
2110 self.ui.progress(_('changesets'), None)
2110
2111
2111 # pull off the manifest group
2112 # pull off the manifest group
2112 self.ui.status(_("adding manifests\n"))
2113 self.ui.status(_("adding manifests\n"))
2113 pr.step = _('manifests')
2114 pr.step = _('manifests')
2114 pr.count = 1
2115 pr.count = 1
2115 pr.total = changesets # manifests <= changesets
2116 pr.total = changesets # manifests <= changesets
2116 chunkiter = changegroup.chunkiter(source, progress=pr)
2117 chunkiter = changegroup.chunkiter(source, progress=pr)
2117 # no need to check for empty manifest group here:
2118 # no need to check for empty manifest group here:
2118 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2119 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2119 # no new manifest will be created and the manifest group will
2120 # no new manifest will be created and the manifest group will
2120 # be empty during the pull
2121 # be empty during the pull
2121 self.manifest.addgroup(chunkiter, revmap, trp)
2122 self.manifest.addgroup(chunkiter, revmap, trp)
2122 self.ui.progress(_('manifests'), None)
2123 self.ui.progress(_('manifests'), None)
2123
2124
2124 needfiles = {}
2125 needfiles = {}
2125 if self.ui.configbool('server', 'validate', default=False):
2126 if self.ui.configbool('server', 'validate', default=False):
2126 # validate incoming csets have their manifests
2127 # validate incoming csets have their manifests
2127 for cset in xrange(clstart, clend):
2128 for cset in xrange(clstart, clend):
2128 mfest = self.changelog.read(self.changelog.node(cset))[0]
2129 mfest = self.changelog.read(self.changelog.node(cset))[0]
2129 mfest = self.manifest.readdelta(mfest)
2130 mfest = self.manifest.readdelta(mfest)
2130 # store file nodes we must see
2131 # store file nodes we must see
2131 for f, n in mfest.iteritems():
2132 for f, n in mfest.iteritems():
2132 needfiles.setdefault(f, set()).add(n)
2133 needfiles.setdefault(f, set()).add(n)
2133
2134
2134 # process the files
2135 # process the files
2135 self.ui.status(_("adding file changes\n"))
2136 self.ui.status(_("adding file changes\n"))
2136 pr.step = 'files'
2137 pr.step = 'files'
2137 pr.count = 1
2138 pr.count = 1
2138 pr.total = efiles
2139 pr.total = efiles
2139 while 1:
2140 while 1:
2140 f = changegroup.getchunk(source)
2141 f = changegroup.getchunk(source)
2141 if not f:
2142 if not f:
2142 break
2143 break
2143 self.ui.debug("adding %s revisions\n" % f)
2144 self.ui.debug("adding %s revisions\n" % f)
2144 pr()
2145 pr()
2145 fl = self.file(f)
2146 fl = self.file(f)
2146 o = len(fl)
2147 o = len(fl)
2147 chunkiter = changegroup.chunkiter(source)
2148 chunkiter = changegroup.chunkiter(source)
2148 if fl.addgroup(chunkiter, revmap, trp) is None:
2149 if fl.addgroup(chunkiter, revmap, trp) is None:
2149 raise util.Abort(_("received file revlog group is empty"))
2150 raise util.Abort(_("received file revlog group is empty"))
2150 revisions += len(fl) - o
2151 revisions += len(fl) - o
2151 files += 1
2152 files += 1
2152 if f in needfiles:
2153 if f in needfiles:
2153 needs = needfiles[f]
2154 needs = needfiles[f]
2154 for new in xrange(o, len(fl)):
2155 for new in xrange(o, len(fl)):
2155 n = fl.node(new)
2156 n = fl.node(new)
2156 if n in needs:
2157 if n in needs:
2157 needs.remove(n)
2158 needs.remove(n)
2158 if not needs:
2159 if not needs:
2159 del needfiles[f]
2160 del needfiles[f]
2160 self.ui.progress(_('files'), None)
2161 self.ui.progress(_('files'), None)
2161
2162
2162 for f, needs in needfiles.iteritems():
2163 for f, needs in needfiles.iteritems():
2163 fl = self.file(f)
2164 fl = self.file(f)
2164 for n in needs:
2165 for n in needs:
2165 try:
2166 try:
2166 fl.rev(n)
2167 fl.rev(n)
2167 except error.LookupError:
2168 except error.LookupError:
2168 raise util.Abort(
2169 raise util.Abort(
2169 _('missing file data for %s:%s - run hg verify') %
2170 _('missing file data for %s:%s - run hg verify') %
2170 (f, hex(n)))
2171 (f, hex(n)))
2171
2172
2172 newheads = len(cl.heads())
2173 newheads = len(cl.heads())
2173 heads = ""
2174 heads = ""
2174 if oldheads and newheads != oldheads:
2175 if oldheads and newheads != oldheads:
2175 heads = _(" (%+d heads)") % (newheads - oldheads)
2176 heads = _(" (%+d heads)") % (newheads - oldheads)
2176
2177
2177 self.ui.status(_("added %d changesets"
2178 self.ui.status(_("added %d changesets"
2178 " with %d changes to %d files%s\n")
2179 " with %d changes to %d files%s\n")
2179 % (changesets, revisions, files, heads))
2180 % (changesets, revisions, files, heads))
2180
2181
2181 if changesets > 0:
2182 if changesets > 0:
2182 p = lambda: cl.writepending() and self.root or ""
2183 p = lambda: cl.writepending() and self.root or ""
2183 self.hook('pretxnchangegroup', throw=True,
2184 self.hook('pretxnchangegroup', throw=True,
2184 node=hex(cl.node(clstart)), source=srctype,
2185 node=hex(cl.node(clstart)), source=srctype,
2185 url=url, pending=p)
2186 url=url, pending=p)
2186
2187
2187 # make changelog see real files again
2188 # make changelog see real files again
2188 cl.finalize(trp)
2189 cl.finalize(trp)
2189
2190
2190 tr.close()
2191 tr.close()
2191 finally:
2192 finally:
2192 del tr
2193 del tr
2193
2194
2194 if changesets > 0:
2195 if changesets > 0:
2195 # forcefully update the on-disk branch cache
2196 # forcefully update the on-disk branch cache
2196 self.ui.debug("updating the branch cache\n")
2197 self.ui.debug("updating the branch cache\n")
2197 self.branchtags()
2198 self.branchtags()
2198 self.hook("changegroup", node=hex(cl.node(clstart)),
2199 self.hook("changegroup", node=hex(cl.node(clstart)),
2199 source=srctype, url=url)
2200 source=srctype, url=url)
2200
2201
2201 for i in xrange(clstart, clend):
2202 for i in xrange(clstart, clend):
2202 self.hook("incoming", node=hex(cl.node(i)),
2203 self.hook("incoming", node=hex(cl.node(i)),
2203 source=srctype, url=url)
2204 source=srctype, url=url)
2204
2205
2205 # never return 0 here:
2206 # never return 0 here:
2206 if newheads < oldheads:
2207 if newheads < oldheads:
2207 return newheads - oldheads - 1
2208 return newheads - oldheads - 1
2208 else:
2209 else:
2209 return newheads - oldheads + 1
2210 return newheads - oldheads + 1
2210
2211
2211
2212
2212 def stream_in(self, remote):
2213 def stream_in(self, remote):
2213 fp = remote.stream_out()
2214 fp = remote.stream_out()
2214 l = fp.readline()
2215 l = fp.readline()
2215 try:
2216 try:
2216 resp = int(l)
2217 resp = int(l)
2217 except ValueError:
2218 except ValueError:
2218 raise error.ResponseError(
2219 raise error.ResponseError(
2219 _('Unexpected response from remote server:'), l)
2220 _('Unexpected response from remote server:'), l)
2220 if resp == 1:
2221 if resp == 1:
2221 raise util.Abort(_('operation forbidden by server'))
2222 raise util.Abort(_('operation forbidden by server'))
2222 elif resp == 2:
2223 elif resp == 2:
2223 raise util.Abort(_('locking the remote repository failed'))
2224 raise util.Abort(_('locking the remote repository failed'))
2224 elif resp != 0:
2225 elif resp != 0:
2225 raise util.Abort(_('the server sent an unknown error code'))
2226 raise util.Abort(_('the server sent an unknown error code'))
2226 self.ui.status(_('streaming all changes\n'))
2227 self.ui.status(_('streaming all changes\n'))
2227 l = fp.readline()
2228 l = fp.readline()
2228 try:
2229 try:
2229 total_files, total_bytes = map(int, l.split(' ', 1))
2230 total_files, total_bytes = map(int, l.split(' ', 1))
2230 except (ValueError, TypeError):
2231 except (ValueError, TypeError):
2231 raise error.ResponseError(
2232 raise error.ResponseError(
2232 _('Unexpected response from remote server:'), l)
2233 _('Unexpected response from remote server:'), l)
2233 self.ui.status(_('%d files to transfer, %s of data\n') %
2234 self.ui.status(_('%d files to transfer, %s of data\n') %
2234 (total_files, util.bytecount(total_bytes)))
2235 (total_files, util.bytecount(total_bytes)))
2235 start = time.time()
2236 start = time.time()
2236 for i in xrange(total_files):
2237 for i in xrange(total_files):
2237 # XXX doesn't support '\n' or '\r' in filenames
2238 # XXX doesn't support '\n' or '\r' in filenames
2238 l = fp.readline()
2239 l = fp.readline()
2239 try:
2240 try:
2240 name, size = l.split('\0', 1)
2241 name, size = l.split('\0', 1)
2241 size = int(size)
2242 size = int(size)
2242 except (ValueError, TypeError):
2243 except (ValueError, TypeError):
2243 raise error.ResponseError(
2244 raise error.ResponseError(
2244 _('Unexpected response from remote server:'), l)
2245 _('Unexpected response from remote server:'), l)
2245 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
2246 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
2246 # for backwards compat, name was partially encoded
2247 # for backwards compat, name was partially encoded
2247 ofp = self.sopener(store.decodedir(name), 'w')
2248 ofp = self.sopener(store.decodedir(name), 'w')
2248 for chunk in util.filechunkiter(fp, limit=size):
2249 for chunk in util.filechunkiter(fp, limit=size):
2249 ofp.write(chunk)
2250 ofp.write(chunk)
2250 ofp.close()
2251 ofp.close()
2251 elapsed = time.time() - start
2252 elapsed = time.time() - start
2252 if elapsed <= 0:
2253 if elapsed <= 0:
2253 elapsed = 0.001
2254 elapsed = 0.001
2254 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2255 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2255 (util.bytecount(total_bytes), elapsed,
2256 (util.bytecount(total_bytes), elapsed,
2256 util.bytecount(total_bytes / elapsed)))
2257 util.bytecount(total_bytes / elapsed)))
2257 self.invalidate()
2258 self.invalidate()
2258 return len(self.heads()) + 1
2259 return len(self.heads()) + 1
2259
2260
2260 def clone(self, remote, heads=[], stream=False):
2261 def clone(self, remote, heads=[], stream=False):
2261 '''clone remote repository.
2262 '''clone remote repository.
2262
2263
2263 keyword arguments:
2264 keyword arguments:
2264 heads: list of revs to clone (forces use of pull)
2265 heads: list of revs to clone (forces use of pull)
2265 stream: use streaming clone if possible'''
2266 stream: use streaming clone if possible'''
2266
2267
2267 # now, all clients that can request uncompressed clones can
2268 # now, all clients that can request uncompressed clones can
2268 # read repo formats supported by all servers that can serve
2269 # read repo formats supported by all servers that can serve
2269 # them.
2270 # them.
2270
2271
2271 # if revlog format changes, client will have to check version
2272 # if revlog format changes, client will have to check version
2272 # and format flags on "stream" capability, and use
2273 # and format flags on "stream" capability, and use
2273 # uncompressed only if compatible.
2274 # uncompressed only if compatible.
2274
2275
2275 if stream and not heads and remote.capable('stream'):
2276 if stream and not heads and remote.capable('stream'):
2276 return self.stream_in(remote)
2277 return self.stream_in(remote)
2277 return self.pull(remote, heads)
2278 return self.pull(remote, heads)
2278
2279
2279 # used to avoid circular references so destructors work
2280 # used to avoid circular references so destructors work
2280 def aftertrans(files):
2281 def aftertrans(files):
2281 renamefiles = [tuple(t) for t in files]
2282 renamefiles = [tuple(t) for t in files]
2282 def a():
2283 def a():
2283 for src, dest in renamefiles:
2284 for src, dest in renamefiles:
2284 util.rename(src, dest)
2285 util.rename(src, dest)
2285 return a
2286 return a
2286
2287
2287 def instance(ui, path, create):
2288 def instance(ui, path, create):
2288 return localrepository(ui, util.drop_scheme('file', path), create)
2289 return localrepository(ui, util.drop_scheme('file', path), create)
2289
2290
2290 def islocal(path):
2291 def islocal(path):
2291 return True
2292 return True
@@ -1,33 +1,35 b''
1 adding a
1 adding a
2 # missing arg
2 # missing arg
3 hg cat: invalid arguments
3 hg cat: invalid arguments
4 hg cat [OPTION]... FILE...
4 hg cat [OPTION]... FILE...
5
5
6 output the current or given revision of files
6 output the current or given revision of files
7
7
8 Print the specified files as they were at the given revision. If no
8 Print the specified files as they were at the given revision. If no
9 revision is given, the parent of the working directory is used, or tip if
9 revision is given, the parent of the working directory is used, or tip if
10 no revision is checked out.
10 no revision is checked out.
11
11
12 Output may be to a file, in which case the name of the file is given using
12 Output may be to a file, in which case the name of the file is given using
13 a format string. The formatting rules are the same as for the export
13 a format string. The formatting rules are the same as for the export
14 command, with the following additions:
14 command, with the following additions:
15
15
16 "%s" basename of file being printed
16 "%s" basename of file being printed
17 "%d" dirname of file being printed, or '.' if in repository root
17 "%d" dirname of file being printed, or '.' if in repository root
18 "%p" root-relative path name of file being printed
18 "%p" root-relative path name of file being printed
19
19
20 Returns 0 on success.
21
20 options:
22 options:
21
23
22 -o --output print output to file with formatted name
24 -o --output print output to file with formatted name
23 -r --rev print the given revision
25 -r --rev print the given revision
24 --decode apply any matching decode filter
26 --decode apply any matching decode filter
25 -I --include include names matching the given patterns
27 -I --include include names matching the given patterns
26 -X --exclude exclude names matching the given patterns
28 -X --exclude exclude names matching the given patterns
27
29
28 use "hg -v help cat" to show global options
30 use "hg -v help cat" to show global options
29 % [defaults]
31 % [defaults]
30 a
32 a
31 a: No such file in rev 000000000000
33 a: No such file in rev 000000000000
32 % no repo
34 % no repo
33 abort: There is no Mercurial repository here (.hg not found)!
35 abort: There is no Mercurial repository here (.hg not found)!
@@ -1,640 +1,646 b''
1 Mercurial Distributed SCM
1 Mercurial Distributed SCM
2
2
3 basic commands:
3 basic commands:
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 by line for each file
6 annotate show changeset information by line for each file
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 forget forget the specified files on the next commit
11 forget forget the specified files on the next commit
12 init create a new repository in the given directory
12 init create a new repository in the given directory
13 log show revision history of entire repository or files
13 log show revision history of entire repository or files
14 merge merge working directory with another revision
14 merge merge working directory with another revision
15 pull pull changes from the specified source
15 pull pull changes from the specified source
16 push push changes to the specified destination
16 push push changes to the specified destination
17 remove remove the specified files on the next commit
17 remove remove the specified files on the next commit
18 serve start stand-alone webserver
18 serve start stand-alone webserver
19 status show changed files in the working directory
19 status show changed files in the working directory
20 summary summarize working directory state
20 summary summarize working directory state
21 update update working directory (or switch revisions)
21 update update working directory (or switch revisions)
22
22
23 use "hg help" for the full list of commands or "hg -v" for details
23 use "hg help" for the full list of commands or "hg -v" for details
24 add add the specified files on the next commit
24 add add the specified files on the next commit
25 annotate show changeset information by line for each file
25 annotate show changeset information by line for each file
26 clone make a copy of an existing repository
26 clone make a copy of an existing repository
27 commit commit the specified files or all outstanding changes
27 commit commit the specified files or all outstanding changes
28 diff diff repository (or selected files)
28 diff diff repository (or selected files)
29 export dump the header and diffs for one or more changesets
29 export dump the header and diffs for one or more changesets
30 forget forget the specified files on the next commit
30 forget forget the specified files on the next commit
31 init create a new repository in the given directory
31 init create a new repository in the given directory
32 log show revision history of entire repository or files
32 log show revision history of entire repository or files
33 merge merge working directory with another revision
33 merge merge working directory with another revision
34 pull pull changes from the specified source
34 pull pull changes from the specified source
35 push push changes to the specified destination
35 push push changes to the specified destination
36 remove remove the specified files on the next commit
36 remove remove the specified files on the next commit
37 serve start stand-alone webserver
37 serve start stand-alone webserver
38 status show changed files in the working directory
38 status show changed files in the working directory
39 summary summarize working directory state
39 summary summarize working directory state
40 update update working directory (or switch revisions)
40 update update working directory (or switch revisions)
41 Mercurial Distributed SCM
41 Mercurial Distributed SCM
42
42
43 list of commands:
43 list of commands:
44
44
45 add add the specified files on the next commit
45 add add the specified files on the next commit
46 addremove add all new files, delete all missing files
46 addremove add all new files, delete all missing files
47 annotate show changeset information by line for each file
47 annotate show changeset information by line for each file
48 archive create an unversioned archive of a repository revision
48 archive create an unversioned archive of a repository revision
49 backout reverse effect of earlier changeset
49 backout reverse effect of earlier changeset
50 bisect subdivision search of changesets
50 bisect subdivision search of changesets
51 branch set or show the current branch name
51 branch set or show the current branch name
52 branches list repository named branches
52 branches list repository named branches
53 bundle create a changegroup file
53 bundle create a changegroup file
54 cat output the current or given revision of files
54 cat output the current or given revision of files
55 clone make a copy of an existing repository
55 clone make a copy of an existing repository
56 commit commit the specified files or all outstanding changes
56 commit commit the specified files or all outstanding changes
57 copy mark files as copied for the next commit
57 copy mark files as copied for the next commit
58 diff diff repository (or selected files)
58 diff diff repository (or selected files)
59 export dump the header and diffs for one or more changesets
59 export dump the header and diffs for one or more changesets
60 forget forget the specified files on the next commit
60 forget forget the specified files on the next commit
61 grep search for a pattern in specified files and revisions
61 grep search for a pattern in specified files and revisions
62 heads show current repository heads or show branch heads
62 heads show current repository heads or show branch heads
63 help show help for a given topic or a help overview
63 help show help for a given topic or a help overview
64 identify identify the working copy or specified revision
64 identify identify the working copy or specified revision
65 import import an ordered set of patches
65 import import an ordered set of patches
66 incoming show new changesets found in source
66 incoming show new changesets found in source
67 init create a new repository in the given directory
67 init create a new repository in the given directory
68 locate locate files matching specific patterns
68 locate locate files matching specific patterns
69 log show revision history of entire repository or files
69 log show revision history of entire repository or files
70 manifest output the current or given revision of the project manifest
70 manifest output the current or given revision of the project manifest
71 merge merge working directory with another revision
71 merge merge working directory with another revision
72 outgoing show changesets not found in the destination
72 outgoing show changesets not found in the destination
73 parents show the parents of the working directory or revision
73 parents show the parents of the working directory or revision
74 paths show aliases for remote repositories
74 paths show aliases for remote repositories
75 pull pull changes from the specified source
75 pull pull changes from the specified source
76 push push changes to the specified destination
76 push push changes to the specified destination
77 recover roll back an interrupted transaction
77 recover roll back an interrupted transaction
78 remove remove the specified files on the next commit
78 remove remove the specified files on the next commit
79 rename rename files; equivalent of copy + remove
79 rename rename files; equivalent of copy + remove
80 resolve various operations to help finish a merge
80 resolve various operations to help finish a merge
81 revert restore individual files or directories to an earlier state
81 revert restore individual files or directories to an earlier state
82 rollback roll back the last transaction (dangerous)
82 rollback roll back the last transaction (dangerous)
83 root print the root (top) of the current working directory
83 root print the root (top) of the current working directory
84 serve start stand-alone webserver
84 serve start stand-alone webserver
85 showconfig show combined config settings from all hgrc files
85 showconfig show combined config settings from all hgrc files
86 status show changed files in the working directory
86 status show changed files in the working directory
87 summary summarize working directory state
87 summary summarize working directory state
88 tag add one or more tags for the current or given revision
88 tag add one or more tags for the current or given revision
89 tags list repository tags
89 tags list repository tags
90 tip show the tip revision
90 tip show the tip revision
91 unbundle apply one or more changegroup files
91 unbundle apply one or more changegroup files
92 update update working directory (or switch revisions)
92 update update working directory (or switch revisions)
93 verify verify the integrity of the repository
93 verify verify the integrity of the repository
94 version output version and copyright information
94 version output version and copyright information
95
95
96 additional help topics:
96 additional help topics:
97
97
98 config Configuration Files
98 config Configuration Files
99 dates Date Formats
99 dates Date Formats
100 patterns File Name Patterns
100 patterns File Name Patterns
101 environment Environment Variables
101 environment Environment Variables
102 revisions Specifying Single Revisions
102 revisions Specifying Single Revisions
103 multirevs Specifying Multiple Revisions
103 multirevs Specifying Multiple Revisions
104 diffs Diff Formats
104 diffs Diff Formats
105 templating Template Usage
105 templating Template Usage
106 urls URL Paths
106 urls URL Paths
107 extensions Using additional features
107 extensions Using additional features
108 hgweb Configuring hgweb
108 hgweb Configuring hgweb
109
109
110 use "hg -v help" to show aliases and global options
110 use "hg -v help" to show aliases and global options
111 add add the specified files on the next commit
111 add add the specified files on the next commit
112 addremove add all new files, delete all missing files
112 addremove add all new files, delete all missing files
113 annotate show changeset information by line for each file
113 annotate show changeset information by line for each file
114 archive create an unversioned archive of a repository revision
114 archive create an unversioned archive of a repository revision
115 backout reverse effect of earlier changeset
115 backout reverse effect of earlier changeset
116 bisect subdivision search of changesets
116 bisect subdivision search of changesets
117 branch set or show the current branch name
117 branch set or show the current branch name
118 branches list repository named branches
118 branches list repository named branches
119 bundle create a changegroup file
119 bundle create a changegroup file
120 cat output the current or given revision of files
120 cat output the current or given revision of files
121 clone make a copy of an existing repository
121 clone make a copy of an existing repository
122 commit commit the specified files or all outstanding changes
122 commit commit the specified files or all outstanding changes
123 copy mark files as copied for the next commit
123 copy mark files as copied for the next commit
124 diff diff repository (or selected files)
124 diff diff repository (or selected files)
125 export dump the header and diffs for one or more changesets
125 export dump the header and diffs for one or more changesets
126 forget forget the specified files on the next commit
126 forget forget the specified files on the next commit
127 grep search for a pattern in specified files and revisions
127 grep search for a pattern in specified files and revisions
128 heads show current repository heads or show branch heads
128 heads show current repository heads or show branch heads
129 help show help for a given topic or a help overview
129 help show help for a given topic or a help overview
130 identify identify the working copy or specified revision
130 identify identify the working copy or specified revision
131 import import an ordered set of patches
131 import import an ordered set of patches
132 incoming show new changesets found in source
132 incoming show new changesets found in source
133 init create a new repository in the given directory
133 init create a new repository in the given directory
134 locate locate files matching specific patterns
134 locate locate files matching specific patterns
135 log show revision history of entire repository or files
135 log show revision history of entire repository or files
136 manifest output the current or given revision of the project manifest
136 manifest output the current or given revision of the project manifest
137 merge merge working directory with another revision
137 merge merge working directory with another revision
138 outgoing show changesets not found in the destination
138 outgoing show changesets not found in the destination
139 parents show the parents of the working directory or revision
139 parents show the parents of the working directory or revision
140 paths show aliases for remote repositories
140 paths show aliases for remote repositories
141 pull pull changes from the specified source
141 pull pull changes from the specified source
142 push push changes to the specified destination
142 push push changes to the specified destination
143 recover roll back an interrupted transaction
143 recover roll back an interrupted transaction
144 remove remove the specified files on the next commit
144 remove remove the specified files on the next commit
145 rename rename files; equivalent of copy + remove
145 rename rename files; equivalent of copy + remove
146 resolve various operations to help finish a merge
146 resolve various operations to help finish a merge
147 revert restore individual files or directories to an earlier state
147 revert restore individual files or directories to an earlier state
148 rollback roll back the last transaction (dangerous)
148 rollback roll back the last transaction (dangerous)
149 root print the root (top) of the current working directory
149 root print the root (top) of the current working directory
150 serve start stand-alone webserver
150 serve start stand-alone webserver
151 showconfig show combined config settings from all hgrc files
151 showconfig show combined config settings from all hgrc files
152 status show changed files in the working directory
152 status show changed files in the working directory
153 summary summarize working directory state
153 summary summarize working directory state
154 tag add one or more tags for the current or given revision
154 tag add one or more tags for the current or given revision
155 tags list repository tags
155 tags list repository tags
156 tip show the tip revision
156 tip show the tip revision
157 unbundle apply one or more changegroup files
157 unbundle apply one or more changegroup files
158 update update working directory (or switch revisions)
158 update update working directory (or switch revisions)
159 verify verify the integrity of the repository
159 verify verify the integrity of the repository
160 version output version and copyright information
160 version output version and copyright information
161
161
162 additional help topics:
162 additional help topics:
163
163
164 config Configuration Files
164 config Configuration Files
165 dates Date Formats
165 dates Date Formats
166 patterns File Name Patterns
166 patterns File Name Patterns
167 environment Environment Variables
167 environment Environment Variables
168 revisions Specifying Single Revisions
168 revisions Specifying Single Revisions
169 multirevs Specifying Multiple Revisions
169 multirevs Specifying Multiple Revisions
170 diffs Diff Formats
170 diffs Diff Formats
171 templating Template Usage
171 templating Template Usage
172 urls URL Paths
172 urls URL Paths
173 extensions Using additional features
173 extensions Using additional features
174 hgweb Configuring hgweb
174 hgweb Configuring hgweb
175 %% test short command list with verbose option
175 %% test short command list with verbose option
176 Mercurial Distributed SCM (version xxx)
176 Mercurial Distributed SCM (version xxx)
177
177
178 Copyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others
178 Copyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others
179 This is free software; see the source for copying conditions. There is NO
179 This is free software; see the source for copying conditions. There is NO
180 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
180 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
181
181
182 basic commands:
182 basic commands:
183
183
184 add:
184 add:
185 add the specified files on the next commit
185 add the specified files on the next commit
186 annotate, blame:
186 annotate, blame:
187 show changeset information by line for each file
187 show changeset information by line for each file
188 clone:
188 clone:
189 make a copy of an existing repository
189 make a copy of an existing repository
190 commit, ci:
190 commit, ci:
191 commit the specified files or all outstanding changes
191 commit the specified files or all outstanding changes
192 diff:
192 diff:
193 diff repository (or selected files)
193 diff repository (or selected files)
194 export:
194 export:
195 dump the header and diffs for one or more changesets
195 dump the header and diffs for one or more changesets
196 forget:
196 forget:
197 forget the specified files on the next commit
197 forget the specified files on the next commit
198 init:
198 init:
199 create a new repository in the given directory
199 create a new repository in the given directory
200 log, history:
200 log, history:
201 show revision history of entire repository or files
201 show revision history of entire repository or files
202 merge:
202 merge:
203 merge working directory with another revision
203 merge working directory with another revision
204 pull:
204 pull:
205 pull changes from the specified source
205 pull changes from the specified source
206 push:
206 push:
207 push changes to the specified destination
207 push changes to the specified destination
208 remove, rm:
208 remove, rm:
209 remove the specified files on the next commit
209 remove the specified files on the next commit
210 serve:
210 serve:
211 start stand-alone webserver
211 start stand-alone webserver
212 status, st:
212 status, st:
213 show changed files in the working directory
213 show changed files in the working directory
214 summary, sum:
214 summary, sum:
215 summarize working directory state
215 summarize working directory state
216 update, up, checkout, co:
216 update, up, checkout, co:
217 update working directory (or switch revisions)
217 update working directory (or switch revisions)
218
218
219 global options:
219 global options:
220 -R --repository repository root directory or name of overlay bundle file
220 -R --repository repository root directory or name of overlay bundle file
221 --cwd change working directory
221 --cwd change working directory
222 -y --noninteractive do not prompt, assume 'yes' for any required answers
222 -y --noninteractive do not prompt, assume 'yes' for any required answers
223 -q --quiet suppress output
223 -q --quiet suppress output
224 -v --verbose enable additional output
224 -v --verbose enable additional output
225 --config set/override config option (use 'section.name=value')
225 --config set/override config option (use 'section.name=value')
226 --debug enable debugging output
226 --debug enable debugging output
227 --debugger start debugger
227 --debugger start debugger
228 --encoding set the charset encoding (default: ascii)
228 --encoding set the charset encoding (default: ascii)
229 --encodingmode set the charset encoding mode (default: strict)
229 --encodingmode set the charset encoding mode (default: strict)
230 --traceback always print a traceback on exception
230 --traceback always print a traceback on exception
231 --time time how long the command takes
231 --time time how long the command takes
232 --profile print command execution profile
232 --profile print command execution profile
233 --version output version information and exit
233 --version output version information and exit
234 -h --help display help and exit
234 -h --help display help and exit
235
235
236 use "hg help" for the full list of commands
236 use "hg help" for the full list of commands
237 hg add [OPTION]... [FILE]...
237 hg add [OPTION]... [FILE]...
238
238
239 add the specified files on the next commit
239 add the specified files on the next commit
240
240
241 Schedule files to be version controlled and added to the repository.
241 Schedule files to be version controlled and added to the repository.
242
242
243 The files will be added to the repository at the next commit. To undo an
243 The files will be added to the repository at the next commit. To undo an
244 add before that, see hg forget.
244 add before that, see hg forget.
245
245
246 If no names are given, add all files to the repository.
246 If no names are given, add all files to the repository.
247
247
248 use "hg -v help add" to show verbose help
248 use "hg -v help add" to show verbose help
249
249
250 options:
250 options:
251
251
252 -I --include include names matching the given patterns
252 -I --include include names matching the given patterns
253 -X --exclude exclude names matching the given patterns
253 -X --exclude exclude names matching the given patterns
254 -n --dry-run do not perform actions, just print output
254 -n --dry-run do not perform actions, just print output
255
255
256 use "hg -v help add" to show global options
256 use "hg -v help add" to show global options
257 %% verbose help for add
257 %% verbose help for add
258 hg add [OPTION]... [FILE]...
258 hg add [OPTION]... [FILE]...
259
259
260 add the specified files on the next commit
260 add the specified files on the next commit
261
261
262 Schedule files to be version controlled and added to the repository.
262 Schedule files to be version controlled and added to the repository.
263
263
264 The files will be added to the repository at the next commit. To undo an
264 The files will be added to the repository at the next commit. To undo an
265 add before that, see hg forget.
265 add before that, see hg forget.
266
266
267 If no names are given, add all files to the repository.
267 If no names are given, add all files to the repository.
268
268
269 An example showing how new (unknown) files are added automatically by "hg
269 An example showing how new (unknown) files are added automatically by "hg
270 add":
270 add":
271
271
272 $ ls
272 $ ls
273 foo.c
273 foo.c
274 $ hg status
274 $ hg status
275 ? foo.c
275 ? foo.c
276 $ hg add
276 $ hg add
277 adding foo.c
277 adding foo.c
278 $ hg status
278 $ hg status
279 A foo.c
279 A foo.c
280
280
281 options:
281 options:
282
282
283 -I --include include names matching the given patterns
283 -I --include include names matching the given patterns
284 -X --exclude exclude names matching the given patterns
284 -X --exclude exclude names matching the given patterns
285 -n --dry-run do not perform actions, just print output
285 -n --dry-run do not perform actions, just print output
286
286
287 global options:
287 global options:
288 -R --repository repository root directory or name of overlay bundle file
288 -R --repository repository root directory or name of overlay bundle file
289 --cwd change working directory
289 --cwd change working directory
290 -y --noninteractive do not prompt, assume 'yes' for any required answers
290 -y --noninteractive do not prompt, assume 'yes' for any required answers
291 -q --quiet suppress output
291 -q --quiet suppress output
292 -v --verbose enable additional output
292 -v --verbose enable additional output
293 --config set/override config option (use 'section.name=value')
293 --config set/override config option (use 'section.name=value')
294 --debug enable debugging output
294 --debug enable debugging output
295 --debugger start debugger
295 --debugger start debugger
296 --encoding set the charset encoding (default: ascii)
296 --encoding set the charset encoding (default: ascii)
297 --encodingmode set the charset encoding mode (default: strict)
297 --encodingmode set the charset encoding mode (default: strict)
298 --traceback always print a traceback on exception
298 --traceback always print a traceback on exception
299 --time time how long the command takes
299 --time time how long the command takes
300 --profile print command execution profile
300 --profile print command execution profile
301 --version output version information and exit
301 --version output version information and exit
302 -h --help display help and exit
302 -h --help display help and exit
303 %% test help option with version option
303 %% test help option with version option
304 Mercurial Distributed SCM (version xxx)
304 Mercurial Distributed SCM (version xxx)
305
305
306 Copyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others
306 Copyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others
307 This is free software; see the source for copying conditions. There is NO
307 This is free software; see the source for copying conditions. There is NO
308 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
308 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
309
309
310 hg add [OPTION]... [FILE]...
310 hg add [OPTION]... [FILE]...
311
311
312 add the specified files on the next commit
312 add the specified files on the next commit
313
313
314 Schedule files to be version controlled and added to the repository.
314 Schedule files to be version controlled and added to the repository.
315
315
316 The files will be added to the repository at the next commit. To undo an
316 The files will be added to the repository at the next commit. To undo an
317 add before that, see hg forget.
317 add before that, see hg forget.
318
318
319 If no names are given, add all files to the repository.
319 If no names are given, add all files to the repository.
320
320
321 use "hg -v help add" to show verbose help
321 use "hg -v help add" to show verbose help
322
322
323 options:
323 options:
324
324
325 -I --include include names matching the given patterns
325 -I --include include names matching the given patterns
326 -X --exclude exclude names matching the given patterns
326 -X --exclude exclude names matching the given patterns
327 -n --dry-run do not perform actions, just print output
327 -n --dry-run do not perform actions, just print output
328
328
329 use "hg -v help add" to show global options
329 use "hg -v help add" to show global options
330 hg add: option --skjdfks not recognized
330 hg add: option --skjdfks not recognized
331 hg add [OPTION]... [FILE]...
331 hg add [OPTION]... [FILE]...
332
332
333 add the specified files on the next commit
333 add the specified files on the next commit
334
334
335 Schedule files to be version controlled and added to the repository.
335 Schedule files to be version controlled and added to the repository.
336
336
337 The files will be added to the repository at the next commit. To undo an
337 The files will be added to the repository at the next commit. To undo an
338 add before that, see hg forget.
338 add before that, see hg forget.
339
339
340 If no names are given, add all files to the repository.
340 If no names are given, add all files to the repository.
341
341
342 use "hg -v help add" to show verbose help
342 use "hg -v help add" to show verbose help
343
343
344 options:
344 options:
345
345
346 -I --include include names matching the given patterns
346 -I --include include names matching the given patterns
347 -X --exclude exclude names matching the given patterns
347 -X --exclude exclude names matching the given patterns
348 -n --dry-run do not perform actions, just print output
348 -n --dry-run do not perform actions, just print output
349
349
350 use "hg -v help add" to show global options
350 use "hg -v help add" to show global options
351 %% test ambiguous command help
351 %% test ambiguous command help
352 list of commands:
352 list of commands:
353
353
354 add add the specified files on the next commit
354 add add the specified files on the next commit
355 addremove add all new files, delete all missing files
355 addremove add all new files, delete all missing files
356
356
357 use "hg -v help ad" to show aliases and global options
357 use "hg -v help ad" to show aliases and global options
358 %% test command without options
358 %% test command without options
359 hg verify
359 hg verify
360
360
361 verify the integrity of the repository
361 verify the integrity of the repository
362
362
363 Verify the integrity of the current repository.
363 Verify the integrity of the current repository.
364
364
365 This will perform an extensive check of the repository's integrity,
365 This will perform an extensive check of the repository's integrity,
366 validating the hashes and checksums of each entry in the changelog,
366 validating the hashes and checksums of each entry in the changelog,
367 manifest, and tracked files, as well as the integrity of their crosslinks
367 manifest, and tracked files, as well as the integrity of their crosslinks
368 and indices.
368 and indices.
369
369
370 Returns 0 on success, 1 if errors are encountered.
371
370 use "hg -v help verify" to show global options
372 use "hg -v help verify" to show global options
371 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
373 hg diff [OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...
372
374
373 diff repository (or selected files)
375 diff repository (or selected files)
374
376
375 Show differences between revisions for the specified files.
377 Show differences between revisions for the specified files.
376
378
377 Differences between files are shown using the unified diff format.
379 Differences between files are shown using the unified diff format.
378
380
379 NOTE: diff may generate unexpected results for merges, as it will default
381 NOTE: diff may generate unexpected results for merges, as it will default
380 to comparing against the working directory's first parent changeset if no
382 to comparing against the working directory's first parent changeset if no
381 revisions are specified.
383 revisions are specified.
382
384
383 When two revision arguments are given, then changes are shown between
385 When two revision arguments are given, then changes are shown between
384 those revisions. If only one revision is specified then that revision is
386 those revisions. If only one revision is specified then that revision is
385 compared to the working directory, and, when no revisions are specified,
387 compared to the working directory, and, when no revisions are specified,
386 the working directory files are compared to its parent.
388 the working directory files are compared to its parent.
387
389
388 Alternatively you can specify -c/--change with a revision to see the
390 Alternatively you can specify -c/--change with a revision to see the
389 changes in that changeset relative to its first parent.
391 changes in that changeset relative to its first parent.
390
392
391 Without the -a/--text option, diff will avoid generating diffs of files it
393 Without the -a/--text option, diff will avoid generating diffs of files it
392 detects as binary. With -a, diff will generate a diff anyway, probably
394 detects as binary. With -a, diff will generate a diff anyway, probably
393 with undesirable results.
395 with undesirable results.
394
396
395 Use the -g/--git option to generate diffs in the git extended diff format.
397 Use the -g/--git option to generate diffs in the git extended diff format.
396 For more information, read "hg help diffs".
398 For more information, read "hg help diffs".
397
399
400 Returns 0 on success.
401
398 options:
402 options:
399
403
400 -r --rev revision
404 -r --rev revision
401 -c --change change made by revision
405 -c --change change made by revision
402 -a --text treat all files as text
406 -a --text treat all files as text
403 -g --git use git extended diff format
407 -g --git use git extended diff format
404 --nodates omit dates from diff headers
408 --nodates omit dates from diff headers
405 -p --show-function show which function each change is in
409 -p --show-function show which function each change is in
406 --reverse produce a diff that undoes the changes
410 --reverse produce a diff that undoes the changes
407 -w --ignore-all-space ignore white space when comparing lines
411 -w --ignore-all-space ignore white space when comparing lines
408 -b --ignore-space-change ignore changes in the amount of white space
412 -b --ignore-space-change ignore changes in the amount of white space
409 -B --ignore-blank-lines ignore changes whose lines are all blank
413 -B --ignore-blank-lines ignore changes whose lines are all blank
410 -U --unified number of lines of context to show
414 -U --unified number of lines of context to show
411 --stat output diffstat-style summary of changes
415 --stat output diffstat-style summary of changes
412 -I --include include names matching the given patterns
416 -I --include include names matching the given patterns
413 -X --exclude exclude names matching the given patterns
417 -X --exclude exclude names matching the given patterns
414
418
415 use "hg -v help diff" to show global options
419 use "hg -v help diff" to show global options
416 hg status [OPTION]... [FILE]...
420 hg status [OPTION]... [FILE]...
417
421
418 aliases: st
422 aliases: st
419
423
420 show changed files in the working directory
424 show changed files in the working directory
421
425
422 Show status of files in the repository. If names are given, only files
426 Show status of files in the repository. If names are given, only files
423 that match are shown. Files that are clean or ignored or the source of a
427 that match are shown. Files that are clean or ignored or the source of a
424 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
428 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
425 -C/--copies or -A/--all are given. Unless options described with "show
429 -C/--copies or -A/--all are given. Unless options described with "show
426 only ..." are given, the options -mardu are used.
430 only ..." are given, the options -mardu are used.
427
431
428 Option -q/--quiet hides untracked (unknown and ignored) files unless
432 Option -q/--quiet hides untracked (unknown and ignored) files unless
429 explicitly requested with -u/--unknown or -i/--ignored.
433 explicitly requested with -u/--unknown or -i/--ignored.
430
434
431 NOTE: status may appear to disagree with diff if permissions have changed
435 NOTE: status may appear to disagree with diff if permissions have changed
432 or a merge has occurred. The standard diff format does not report
436 or a merge has occurred. The standard diff format does not report
433 permission changes and diff only reports changes relative to one merge
437 permission changes and diff only reports changes relative to one merge
434 parent.
438 parent.
435
439
436 If one revision is given, it is used as the base revision. If two
440 If one revision is given, it is used as the base revision. If two
437 revisions are given, the differences between them are shown. The --change
441 revisions are given, the differences between them are shown. The --change
438 option can also be used as a shortcut to list the changed files of a
442 option can also be used as a shortcut to list the changed files of a
439 revision from its first parent.
443 revision from its first parent.
440
444
441 The codes used to show the status of files are:
445 The codes used to show the status of files are:
442
446
443 M = modified
447 M = modified
444 A = added
448 A = added
445 R = removed
449 R = removed
446 C = clean
450 C = clean
447 ! = missing (deleted by non-hg command, but still tracked)
451 ! = missing (deleted by non-hg command, but still tracked)
448 ? = not tracked
452 ? = not tracked
449 I = ignored
453 I = ignored
450 = origin of the previous file listed as A (added)
454 = origin of the previous file listed as A (added)
451
455
456 Returns 0 on success.
457
452 options:
458 options:
453
459
454 -A --all show status of all files
460 -A --all show status of all files
455 -m --modified show only modified files
461 -m --modified show only modified files
456 -a --added show only added files
462 -a --added show only added files
457 -r --removed show only removed files
463 -r --removed show only removed files
458 -d --deleted show only deleted (but tracked) files
464 -d --deleted show only deleted (but tracked) files
459 -c --clean show only files without changes
465 -c --clean show only files without changes
460 -u --unknown show only unknown (not tracked) files
466 -u --unknown show only unknown (not tracked) files
461 -i --ignored show only ignored files
467 -i --ignored show only ignored files
462 -n --no-status hide status prefix
468 -n --no-status hide status prefix
463 -C --copies show source of copied files
469 -C --copies show source of copied files
464 -0 --print0 end filenames with NUL, for use with xargs
470 -0 --print0 end filenames with NUL, for use with xargs
465 --rev show difference from revision
471 --rev show difference from revision
466 --change list the changed files of a revision
472 --change list the changed files of a revision
467 -I --include include names matching the given patterns
473 -I --include include names matching the given patterns
468 -X --exclude exclude names matching the given patterns
474 -X --exclude exclude names matching the given patterns
469
475
470 use "hg -v help status" to show global options
476 use "hg -v help status" to show global options
471 hg status [OPTION]... [FILE]...
477 hg status [OPTION]... [FILE]...
472
478
473 show changed files in the working directory
479 show changed files in the working directory
474 hg: unknown command 'foo'
480 hg: unknown command 'foo'
475 Mercurial Distributed SCM
481 Mercurial Distributed SCM
476
482
477 basic commands:
483 basic commands:
478
484
479 add add the specified files on the next commit
485 add add the specified files on the next commit
480 annotate show changeset information by line for each file
486 annotate show changeset information by line for each file
481 clone make a copy of an existing repository
487 clone make a copy of an existing repository
482 commit commit the specified files or all outstanding changes
488 commit commit the specified files or all outstanding changes
483 diff diff repository (or selected files)
489 diff diff repository (or selected files)
484 export dump the header and diffs for one or more changesets
490 export dump the header and diffs for one or more changesets
485 forget forget the specified files on the next commit
491 forget forget the specified files on the next commit
486 init create a new repository in the given directory
492 init create a new repository in the given directory
487 log show revision history of entire repository or files
493 log show revision history of entire repository or files
488 merge merge working directory with another revision
494 merge merge working directory with another revision
489 pull pull changes from the specified source
495 pull pull changes from the specified source
490 push push changes to the specified destination
496 push push changes to the specified destination
491 remove remove the specified files on the next commit
497 remove remove the specified files on the next commit
492 serve start stand-alone webserver
498 serve start stand-alone webserver
493 status show changed files in the working directory
499 status show changed files in the working directory
494 summary summarize working directory state
500 summary summarize working directory state
495 update update working directory (or switch revisions)
501 update update working directory (or switch revisions)
496
502
497 use "hg help" for the full list of commands or "hg -v" for details
503 use "hg help" for the full list of commands or "hg -v" for details
498 hg: unknown command 'skjdfks'
504 hg: unknown command 'skjdfks'
499 Mercurial Distributed SCM
505 Mercurial Distributed SCM
500
506
501 basic commands:
507 basic commands:
502
508
503 add add the specified files on the next commit
509 add add the specified files on the next commit
504 annotate show changeset information by line for each file
510 annotate show changeset information by line for each file
505 clone make a copy of an existing repository
511 clone make a copy of an existing repository
506 commit commit the specified files or all outstanding changes
512 commit commit the specified files or all outstanding changes
507 diff diff repository (or selected files)
513 diff diff repository (or selected files)
508 export dump the header and diffs for one or more changesets
514 export dump the header and diffs for one or more changesets
509 forget forget the specified files on the next commit
515 forget forget the specified files on the next commit
510 init create a new repository in the given directory
516 init create a new repository in the given directory
511 log show revision history of entire repository or files
517 log show revision history of entire repository or files
512 merge merge working directory with another revision
518 merge merge working directory with another revision
513 pull pull changes from the specified source
519 pull pull changes from the specified source
514 push push changes to the specified destination
520 push push changes to the specified destination
515 remove remove the specified files on the next commit
521 remove remove the specified files on the next commit
516 serve start stand-alone webserver
522 serve start stand-alone webserver
517 status show changed files in the working directory
523 status show changed files in the working directory
518 summary summarize working directory state
524 summary summarize working directory state
519 update update working directory (or switch revisions)
525 update update working directory (or switch revisions)
520
526
521 use "hg help" for the full list of commands or "hg -v" for details
527 use "hg help" for the full list of commands or "hg -v" for details
522 %% test command with no help text
528 %% test command with no help text
523 hg nohelp
529 hg nohelp
524
530
525 (no help text available)
531 (no help text available)
526
532
527 use "hg -v help nohelp" to show global options
533 use "hg -v help nohelp" to show global options
528 %% test that default list of commands omits extension commands
534 %% test that default list of commands omits extension commands
529 Mercurial Distributed SCM
535 Mercurial Distributed SCM
530
536
531 list of commands:
537 list of commands:
532
538
533 add add the specified files on the next commit
539 add add the specified files on the next commit
534 addremove add all new files, delete all missing files
540 addremove add all new files, delete all missing files
535 annotate show changeset information by line for each file
541 annotate show changeset information by line for each file
536 archive create an unversioned archive of a repository revision
542 archive create an unversioned archive of a repository revision
537 backout reverse effect of earlier changeset
543 backout reverse effect of earlier changeset
538 bisect subdivision search of changesets
544 bisect subdivision search of changesets
539 branch set or show the current branch name
545 branch set or show the current branch name
540 branches list repository named branches
546 branches list repository named branches
541 bundle create a changegroup file
547 bundle create a changegroup file
542 cat output the current or given revision of files
548 cat output the current or given revision of files
543 clone make a copy of an existing repository
549 clone make a copy of an existing repository
544 commit commit the specified files or all outstanding changes
550 commit commit the specified files or all outstanding changes
545 copy mark files as copied for the next commit
551 copy mark files as copied for the next commit
546 diff diff repository (or selected files)
552 diff diff repository (or selected files)
547 export dump the header and diffs for one or more changesets
553 export dump the header and diffs for one or more changesets
548 forget forget the specified files on the next commit
554 forget forget the specified files on the next commit
549 grep search for a pattern in specified files and revisions
555 grep search for a pattern in specified files and revisions
550 heads show current repository heads or show branch heads
556 heads show current repository heads or show branch heads
551 help show help for a given topic or a help overview
557 help show help for a given topic or a help overview
552 identify identify the working copy or specified revision
558 identify identify the working copy or specified revision
553 import import an ordered set of patches
559 import import an ordered set of patches
554 incoming show new changesets found in source
560 incoming show new changesets found in source
555 init create a new repository in the given directory
561 init create a new repository in the given directory
556 locate locate files matching specific patterns
562 locate locate files matching specific patterns
557 log show revision history of entire repository or files
563 log show revision history of entire repository or files
558 manifest output the current or given revision of the project manifest
564 manifest output the current or given revision of the project manifest
559 merge merge working directory with another revision
565 merge merge working directory with another revision
560 outgoing show changesets not found in the destination
566 outgoing show changesets not found in the destination
561 parents show the parents of the working directory or revision
567 parents show the parents of the working directory or revision
562 paths show aliases for remote repositories
568 paths show aliases for remote repositories
563 pull pull changes from the specified source
569 pull pull changes from the specified source
564 push push changes to the specified destination
570 push push changes to the specified destination
565 recover roll back an interrupted transaction
571 recover roll back an interrupted transaction
566 remove remove the specified files on the next commit
572 remove remove the specified files on the next commit
567 rename rename files; equivalent of copy + remove
573 rename rename files; equivalent of copy + remove
568 resolve various operations to help finish a merge
574 resolve various operations to help finish a merge
569 revert restore individual files or directories to an earlier state
575 revert restore individual files or directories to an earlier state
570 rollback roll back the last transaction (dangerous)
576 rollback roll back the last transaction (dangerous)
571 root print the root (top) of the current working directory
577 root print the root (top) of the current working directory
572 serve start stand-alone webserver
578 serve start stand-alone webserver
573 showconfig show combined config settings from all hgrc files
579 showconfig show combined config settings from all hgrc files
574 status show changed files in the working directory
580 status show changed files in the working directory
575 summary summarize working directory state
581 summary summarize working directory state
576 tag add one or more tags for the current or given revision
582 tag add one or more tags for the current or given revision
577 tags list repository tags
583 tags list repository tags
578 tip show the tip revision
584 tip show the tip revision
579 unbundle apply one or more changegroup files
585 unbundle apply one or more changegroup files
580 update update working directory (or switch revisions)
586 update update working directory (or switch revisions)
581 verify verify the integrity of the repository
587 verify verify the integrity of the repository
582 version output version and copyright information
588 version output version and copyright information
583
589
584 enabled extensions:
590 enabled extensions:
585
591
586 helpext (no help text available)
592 helpext (no help text available)
587
593
588 additional help topics:
594 additional help topics:
589
595
590 config Configuration Files
596 config Configuration Files
591 dates Date Formats
597 dates Date Formats
592 patterns File Name Patterns
598 patterns File Name Patterns
593 environment Environment Variables
599 environment Environment Variables
594 revisions Specifying Single Revisions
600 revisions Specifying Single Revisions
595 multirevs Specifying Multiple Revisions
601 multirevs Specifying Multiple Revisions
596 diffs Diff Formats
602 diffs Diff Formats
597 templating Template Usage
603 templating Template Usage
598 urls URL Paths
604 urls URL Paths
599 extensions Using additional features
605 extensions Using additional features
600 hgweb Configuring hgweb
606 hgweb Configuring hgweb
601
607
602 use "hg -v help" to show aliases and global options
608 use "hg -v help" to show aliases and global options
603 %% test list of commands with command with no help text
609 %% test list of commands with command with no help text
604 helpext extension - no help text available
610 helpext extension - no help text available
605
611
606 list of commands:
612 list of commands:
607
613
608 nohelp (no help text available)
614 nohelp (no help text available)
609
615
610 use "hg -v help helpext" to show aliases and global options
616 use "hg -v help helpext" to show aliases and global options
611 %% test a help topic
617 %% test a help topic
612 Specifying Single Revisions
618 Specifying Single Revisions
613
619
614 Mercurial supports several ways to specify individual revisions.
620 Mercurial supports several ways to specify individual revisions.
615
621
616 A plain integer is treated as a revision number. Negative integers are
622 A plain integer is treated as a revision number. Negative integers are
617 treated as sequential offsets from the tip, with -1 denoting the tip, -2
623 treated as sequential offsets from the tip, with -1 denoting the tip, -2
618 denoting the revision prior to the tip, and so forth.
624 denoting the revision prior to the tip, and so forth.
619
625
620 A 40-digit hexadecimal string is treated as a unique revision identifier.
626 A 40-digit hexadecimal string is treated as a unique revision identifier.
621
627
622 A hexadecimal string less than 40 characters long is treated as a unique
628 A hexadecimal string less than 40 characters long is treated as a unique
623 revision identifier and is referred to as a short-form identifier. A
629 revision identifier and is referred to as a short-form identifier. A
624 short-form identifier is only valid if it is the prefix of exactly one
630 short-form identifier is only valid if it is the prefix of exactly one
625 full-length identifier.
631 full-length identifier.
626
632
627 Any other string is treated as a tag or branch name. A tag name is a
633 Any other string is treated as a tag or branch name. A tag name is a
628 symbolic name associated with a revision identifier. A branch name denotes
634 symbolic name associated with a revision identifier. A branch name denotes
629 the tipmost revision of that branch. Tag and branch names must not contain
635 the tipmost revision of that branch. Tag and branch names must not contain
630 the ":" character.
636 the ":" character.
631
637
632 The reserved name "tip" is a special tag that always identifies the most
638 The reserved name "tip" is a special tag that always identifies the most
633 recent revision.
639 recent revision.
634
640
635 The reserved name "null" indicates the null revision. This is the revision
641 The reserved name "null" indicates the null revision. This is the revision
636 of an empty repository, and the parent of revision 0.
642 of an empty repository, and the parent of revision 0.
637
643
638 The reserved name "." indicates the working directory parent. If no
644 The reserved name "." indicates the working directory parent. If no
639 working directory is checked out, it is equivalent to null. If an
645 working directory is checked out, it is equivalent to null. If an
640 uncommitted merge is in progress, "." is the revision of the first parent.
646 uncommitted merge is in progress, "." is the revision of the first parent.
@@ -1,10 +1,11 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init
3 hg init
4 echo This is file a1 > a
4 echo This is file a1 > a
5 hg add a
5 hg add a
6 hg commit -m "commit #0" -d "1000000 0"
6 hg commit -m "commit #0" -d "1000000 0"
7 touch b
7 touch b
8 hg add b
8 hg add b
9 rm b
9 rm b
10 hg commit -A -m"comment #1" -d "1000000 0"
10 hg commit -A -m"comment #1" -d "1000000 0"
11 exit 0
General Comments 0
You need to be logged in to leave comments. Login now