##// END OF EJS Templates
walk: remove cmdutil.walk
Matt Mackall -
r6585:d3d1d39d default
parent child Browse files
Show More
@@ -1,1187 +1,1183
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, bisect, stat
10 import os, sys, bisect, stat
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno
12 import match as _match
12 import match as _match
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 class UnknownCommand(Exception):
16 class UnknownCommand(Exception):
17 """Exception raised if command is not in the command table."""
17 """Exception raised if command is not in the command table."""
18 class AmbiguousCommand(Exception):
18 class AmbiguousCommand(Exception):
19 """Exception raised if command shortcut matches more than one command."""
19 """Exception raised if command shortcut matches more than one command."""
20
20
21 def findpossible(ui, cmd, table):
21 def findpossible(ui, cmd, table):
22 """
22 """
23 Return cmd -> (aliases, command table entry)
23 Return cmd -> (aliases, command table entry)
24 for each matching command.
24 for each matching command.
25 Return debug commands (or their aliases) only if no normal command matches.
25 Return debug commands (or their aliases) only if no normal command matches.
26 """
26 """
27 choice = {}
27 choice = {}
28 debugchoice = {}
28 debugchoice = {}
29 for e in table.keys():
29 for e in table.keys():
30 aliases = e.lstrip("^").split("|")
30 aliases = e.lstrip("^").split("|")
31 found = None
31 found = None
32 if cmd in aliases:
32 if cmd in aliases:
33 found = cmd
33 found = cmd
34 elif not ui.config("ui", "strict"):
34 elif not ui.config("ui", "strict"):
35 for a in aliases:
35 for a in aliases:
36 if a.startswith(cmd):
36 if a.startswith(cmd):
37 found = a
37 found = a
38 break
38 break
39 if found is not None:
39 if found is not None:
40 if aliases[0].startswith("debug") or found.startswith("debug"):
40 if aliases[0].startswith("debug") or found.startswith("debug"):
41 debugchoice[found] = (aliases, table[e])
41 debugchoice[found] = (aliases, table[e])
42 else:
42 else:
43 choice[found] = (aliases, table[e])
43 choice[found] = (aliases, table[e])
44
44
45 if not choice and debugchoice:
45 if not choice and debugchoice:
46 choice = debugchoice
46 choice = debugchoice
47
47
48 return choice
48 return choice
49
49
50 def findcmd(ui, cmd, table):
50 def findcmd(ui, cmd, table):
51 """Return (aliases, command table entry) for command string."""
51 """Return (aliases, command table entry) for command string."""
52 choice = findpossible(ui, cmd, table)
52 choice = findpossible(ui, cmd, table)
53
53
54 if cmd in choice:
54 if cmd in choice:
55 return choice[cmd]
55 return choice[cmd]
56
56
57 if len(choice) > 1:
57 if len(choice) > 1:
58 clist = choice.keys()
58 clist = choice.keys()
59 clist.sort()
59 clist.sort()
60 raise AmbiguousCommand(cmd, clist)
60 raise AmbiguousCommand(cmd, clist)
61
61
62 if choice:
62 if choice:
63 return choice.values()[0]
63 return choice.values()[0]
64
64
65 raise UnknownCommand(cmd)
65 raise UnknownCommand(cmd)
66
66
67 def bail_if_changed(repo):
67 def bail_if_changed(repo):
68 if repo.dirstate.parents()[1] != nullid:
68 if repo.dirstate.parents()[1] != nullid:
69 raise util.Abort(_('outstanding uncommitted merge'))
69 raise util.Abort(_('outstanding uncommitted merge'))
70 modified, added, removed, deleted = repo.status()[:4]
70 modified, added, removed, deleted = repo.status()[:4]
71 if modified or added or removed or deleted:
71 if modified or added or removed or deleted:
72 raise util.Abort(_("outstanding uncommitted changes"))
72 raise util.Abort(_("outstanding uncommitted changes"))
73
73
74 def logmessage(opts):
74 def logmessage(opts):
75 """ get the log message according to -m and -l option """
75 """ get the log message according to -m and -l option """
76 message = opts['message']
76 message = opts['message']
77 logfile = opts['logfile']
77 logfile = opts['logfile']
78
78
79 if message and logfile:
79 if message and logfile:
80 raise util.Abort(_('options --message and --logfile are mutually '
80 raise util.Abort(_('options --message and --logfile are mutually '
81 'exclusive'))
81 'exclusive'))
82 if not message and logfile:
82 if not message and logfile:
83 try:
83 try:
84 if logfile == '-':
84 if logfile == '-':
85 message = sys.stdin.read()
85 message = sys.stdin.read()
86 else:
86 else:
87 message = open(logfile).read()
87 message = open(logfile).read()
88 except IOError, inst:
88 except IOError, inst:
89 raise util.Abort(_("can't read commit message '%s': %s") %
89 raise util.Abort(_("can't read commit message '%s': %s") %
90 (logfile, inst.strerror))
90 (logfile, inst.strerror))
91 return message
91 return message
92
92
93 def loglimit(opts):
93 def loglimit(opts):
94 """get the log limit according to option -l/--limit"""
94 """get the log limit according to option -l/--limit"""
95 limit = opts.get('limit')
95 limit = opts.get('limit')
96 if limit:
96 if limit:
97 try:
97 try:
98 limit = int(limit)
98 limit = int(limit)
99 except ValueError:
99 except ValueError:
100 raise util.Abort(_('limit must be a positive integer'))
100 raise util.Abort(_('limit must be a positive integer'))
101 if limit <= 0: raise util.Abort(_('limit must be positive'))
101 if limit <= 0: raise util.Abort(_('limit must be positive'))
102 else:
102 else:
103 limit = sys.maxint
103 limit = sys.maxint
104 return limit
104 return limit
105
105
106 def setremoteconfig(ui, opts):
106 def setremoteconfig(ui, opts):
107 "copy remote options to ui tree"
107 "copy remote options to ui tree"
108 if opts.get('ssh'):
108 if opts.get('ssh'):
109 ui.setconfig("ui", "ssh", opts['ssh'])
109 ui.setconfig("ui", "ssh", opts['ssh'])
110 if opts.get('remotecmd'):
110 if opts.get('remotecmd'):
111 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
111 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
112
112
113 def revpair(repo, revs):
113 def revpair(repo, revs):
114 '''return pair of nodes, given list of revisions. second item can
114 '''return pair of nodes, given list of revisions. second item can
115 be None, meaning use working dir.'''
115 be None, meaning use working dir.'''
116
116
117 def revfix(repo, val, defval):
117 def revfix(repo, val, defval):
118 if not val and val != 0 and defval is not None:
118 if not val and val != 0 and defval is not None:
119 val = defval
119 val = defval
120 return repo.lookup(val)
120 return repo.lookup(val)
121
121
122 if not revs:
122 if not revs:
123 return repo.dirstate.parents()[0], None
123 return repo.dirstate.parents()[0], None
124 end = None
124 end = None
125 if len(revs) == 1:
125 if len(revs) == 1:
126 if revrangesep in revs[0]:
126 if revrangesep in revs[0]:
127 start, end = revs[0].split(revrangesep, 1)
127 start, end = revs[0].split(revrangesep, 1)
128 start = revfix(repo, start, 0)
128 start = revfix(repo, start, 0)
129 end = revfix(repo, end, repo.changelog.count() - 1)
129 end = revfix(repo, end, repo.changelog.count() - 1)
130 else:
130 else:
131 start = revfix(repo, revs[0], None)
131 start = revfix(repo, revs[0], None)
132 elif len(revs) == 2:
132 elif len(revs) == 2:
133 if revrangesep in revs[0] or revrangesep in revs[1]:
133 if revrangesep in revs[0] or revrangesep in revs[1]:
134 raise util.Abort(_('too many revisions specified'))
134 raise util.Abort(_('too many revisions specified'))
135 start = revfix(repo, revs[0], None)
135 start = revfix(repo, revs[0], None)
136 end = revfix(repo, revs[1], None)
136 end = revfix(repo, revs[1], None)
137 else:
137 else:
138 raise util.Abort(_('too many revisions specified'))
138 raise util.Abort(_('too many revisions specified'))
139 return start, end
139 return start, end
140
140
141 def revrange(repo, revs):
141 def revrange(repo, revs):
142 """Yield revision as strings from a list of revision specifications."""
142 """Yield revision as strings from a list of revision specifications."""
143
143
144 def revfix(repo, val, defval):
144 def revfix(repo, val, defval):
145 if not val and val != 0 and defval is not None:
145 if not val and val != 0 and defval is not None:
146 return defval
146 return defval
147 return repo.changelog.rev(repo.lookup(val))
147 return repo.changelog.rev(repo.lookup(val))
148
148
149 seen, l = {}, []
149 seen, l = {}, []
150 for spec in revs:
150 for spec in revs:
151 if revrangesep in spec:
151 if revrangesep in spec:
152 start, end = spec.split(revrangesep, 1)
152 start, end = spec.split(revrangesep, 1)
153 start = revfix(repo, start, 0)
153 start = revfix(repo, start, 0)
154 end = revfix(repo, end, repo.changelog.count() - 1)
154 end = revfix(repo, end, repo.changelog.count() - 1)
155 step = start > end and -1 or 1
155 step = start > end and -1 or 1
156 for rev in xrange(start, end+step, step):
156 for rev in xrange(start, end+step, step):
157 if rev in seen:
157 if rev in seen:
158 continue
158 continue
159 seen[rev] = 1
159 seen[rev] = 1
160 l.append(rev)
160 l.append(rev)
161 else:
161 else:
162 rev = revfix(repo, spec, None)
162 rev = revfix(repo, spec, None)
163 if rev in seen:
163 if rev in seen:
164 continue
164 continue
165 seen[rev] = 1
165 seen[rev] = 1
166 l.append(rev)
166 l.append(rev)
167
167
168 return l
168 return l
169
169
170 def make_filename(repo, pat, node,
170 def make_filename(repo, pat, node,
171 total=None, seqno=None, revwidth=None, pathname=None):
171 total=None, seqno=None, revwidth=None, pathname=None):
172 node_expander = {
172 node_expander = {
173 'H': lambda: hex(node),
173 'H': lambda: hex(node),
174 'R': lambda: str(repo.changelog.rev(node)),
174 'R': lambda: str(repo.changelog.rev(node)),
175 'h': lambda: short(node),
175 'h': lambda: short(node),
176 }
176 }
177 expander = {
177 expander = {
178 '%': lambda: '%',
178 '%': lambda: '%',
179 'b': lambda: os.path.basename(repo.root),
179 'b': lambda: os.path.basename(repo.root),
180 }
180 }
181
181
182 try:
182 try:
183 if node:
183 if node:
184 expander.update(node_expander)
184 expander.update(node_expander)
185 if node:
185 if node:
186 expander['r'] = (lambda:
186 expander['r'] = (lambda:
187 str(repo.changelog.rev(node)).zfill(revwidth or 0))
187 str(repo.changelog.rev(node)).zfill(revwidth or 0))
188 if total is not None:
188 if total is not None:
189 expander['N'] = lambda: str(total)
189 expander['N'] = lambda: str(total)
190 if seqno is not None:
190 if seqno is not None:
191 expander['n'] = lambda: str(seqno)
191 expander['n'] = lambda: str(seqno)
192 if total is not None and seqno is not None:
192 if total is not None and seqno is not None:
193 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
193 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
194 if pathname is not None:
194 if pathname is not None:
195 expander['s'] = lambda: os.path.basename(pathname)
195 expander['s'] = lambda: os.path.basename(pathname)
196 expander['d'] = lambda: os.path.dirname(pathname) or '.'
196 expander['d'] = lambda: os.path.dirname(pathname) or '.'
197 expander['p'] = lambda: pathname
197 expander['p'] = lambda: pathname
198
198
199 newname = []
199 newname = []
200 patlen = len(pat)
200 patlen = len(pat)
201 i = 0
201 i = 0
202 while i < patlen:
202 while i < patlen:
203 c = pat[i]
203 c = pat[i]
204 if c == '%':
204 if c == '%':
205 i += 1
205 i += 1
206 c = pat[i]
206 c = pat[i]
207 c = expander[c]()
207 c = expander[c]()
208 newname.append(c)
208 newname.append(c)
209 i += 1
209 i += 1
210 return ''.join(newname)
210 return ''.join(newname)
211 except KeyError, inst:
211 except KeyError, inst:
212 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
212 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
213 inst.args[0])
213 inst.args[0])
214
214
215 def make_file(repo, pat, node=None,
215 def make_file(repo, pat, node=None,
216 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
216 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
217 if not pat or pat == '-':
217 if not pat or pat == '-':
218 return 'w' in mode and sys.stdout or sys.stdin
218 return 'w' in mode and sys.stdout or sys.stdin
219 if hasattr(pat, 'write') and 'w' in mode:
219 if hasattr(pat, 'write') and 'w' in mode:
220 return pat
220 return pat
221 if hasattr(pat, 'read') and 'r' in mode:
221 if hasattr(pat, 'read') and 'r' in mode:
222 return pat
222 return pat
223 return open(make_filename(repo, pat, node, total, seqno, revwidth,
223 return open(make_filename(repo, pat, node, total, seqno, revwidth,
224 pathname),
224 pathname),
225 mode)
225 mode)
226
226
227 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
227 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
228 if not globbed and default == 'relpath':
228 if not globbed and default == 'relpath':
229 pats = util.expand_glob(pats or [])
229 pats = util.expand_glob(pats or [])
230 m = _match.match(repo.root, repo.getcwd(), pats,
230 m = _match.match(repo.root, repo.getcwd(), pats,
231 opts.get('include'), opts.get('exclude'), default)
231 opts.get('include'), opts.get('exclude'), default)
232 def badfn(f, msg):
232 def badfn(f, msg):
233 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
233 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
234 return False
234 return False
235 m.bad = badfn
235 m.bad = badfn
236 return m
236 return m
237
237
238 def walk(repo, match, node=None):
239 for src, fn in repo.walk(node, match):
240 yield src, fn
241
242 def findrenames(repo, added=None, removed=None, threshold=0.5):
238 def findrenames(repo, added=None, removed=None, threshold=0.5):
243 '''find renamed files -- yields (before, after, score) tuples'''
239 '''find renamed files -- yields (before, after, score) tuples'''
244 if added is None or removed is None:
240 if added is None or removed is None:
245 added, removed = repo.status()[1:3]
241 added, removed = repo.status()[1:3]
246 ctx = repo.changectx()
242 ctx = repo.changectx()
247 for a in added:
243 for a in added:
248 aa = repo.wread(a)
244 aa = repo.wread(a)
249 bestname, bestscore = None, threshold
245 bestname, bestscore = None, threshold
250 for r in removed:
246 for r in removed:
251 rr = ctx.filectx(r).data()
247 rr = ctx.filectx(r).data()
252
248
253 # bdiff.blocks() returns blocks of matching lines
249 # bdiff.blocks() returns blocks of matching lines
254 # count the number of bytes in each
250 # count the number of bytes in each
255 equal = 0
251 equal = 0
256 alines = mdiff.splitnewlines(aa)
252 alines = mdiff.splitnewlines(aa)
257 matches = bdiff.blocks(aa, rr)
253 matches = bdiff.blocks(aa, rr)
258 for x1,x2,y1,y2 in matches:
254 for x1,x2,y1,y2 in matches:
259 for line in alines[x1:x2]:
255 for line in alines[x1:x2]:
260 equal += len(line)
256 equal += len(line)
261
257
262 lengths = len(aa) + len(rr)
258 lengths = len(aa) + len(rr)
263 if lengths:
259 if lengths:
264 myscore = equal*2.0 / lengths
260 myscore = equal*2.0 / lengths
265 if myscore >= bestscore:
261 if myscore >= bestscore:
266 bestname, bestscore = r, myscore
262 bestname, bestscore = r, myscore
267 if bestname:
263 if bestname:
268 yield bestname, a, bestscore
264 yield bestname, a, bestscore
269
265
270 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
266 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
271 if dry_run is None:
267 if dry_run is None:
272 dry_run = opts.get('dry_run')
268 dry_run = opts.get('dry_run')
273 if similarity is None:
269 if similarity is None:
274 similarity = float(opts.get('similarity') or 0)
270 similarity = float(opts.get('similarity') or 0)
275 add, remove = [], []
271 add, remove = [], []
276 mapping = {}
272 mapping = {}
277 m = match(repo, pats, opts)
273 m = match(repo, pats, opts)
278 for src, abs in walk(repo, m):
274 for src, abs in repo.walk(m):
279 target = repo.wjoin(abs)
275 target = repo.wjoin(abs)
280 rel = m.rel(abs)
276 rel = m.rel(abs)
281 exact = m.exact(abs)
277 exact = m.exact(abs)
282 if src == 'f' and abs not in repo.dirstate:
278 if src == 'f' and abs not in repo.dirstate:
283 add.append(abs)
279 add.append(abs)
284 mapping[abs] = rel, m.exact(abs)
280 mapping[abs] = rel, m.exact(abs)
285 if repo.ui.verbose or not exact:
281 if repo.ui.verbose or not exact:
286 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
282 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
287 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
283 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
288 or (os.path.isdir(target) and not os.path.islink(target))):
284 or (os.path.isdir(target) and not os.path.islink(target))):
289 remove.append(abs)
285 remove.append(abs)
290 mapping[abs] = rel, exact
286 mapping[abs] = rel, exact
291 if repo.ui.verbose or not exact:
287 if repo.ui.verbose or not exact:
292 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
288 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
293 if not dry_run:
289 if not dry_run:
294 repo.remove(remove)
290 repo.remove(remove)
295 repo.add(add)
291 repo.add(add)
296 if similarity > 0:
292 if similarity > 0:
297 for old, new, score in findrenames(repo, add, remove, similarity):
293 for old, new, score in findrenames(repo, add, remove, similarity):
298 oldrel, oldexact = mapping[old]
294 oldrel, oldexact = mapping[old]
299 newrel, newexact = mapping[new]
295 newrel, newexact = mapping[new]
300 if repo.ui.verbose or not oldexact or not newexact:
296 if repo.ui.verbose or not oldexact or not newexact:
301 repo.ui.status(_('recording removal of %s as rename to %s '
297 repo.ui.status(_('recording removal of %s as rename to %s '
302 '(%d%% similar)\n') %
298 '(%d%% similar)\n') %
303 (oldrel, newrel, score * 100))
299 (oldrel, newrel, score * 100))
304 if not dry_run:
300 if not dry_run:
305 repo.copy(old, new)
301 repo.copy(old, new)
306
302
307 def copy(ui, repo, pats, opts, rename=False):
303 def copy(ui, repo, pats, opts, rename=False):
308 # called with the repo lock held
304 # called with the repo lock held
309 #
305 #
310 # hgsep => pathname that uses "/" to separate directories
306 # hgsep => pathname that uses "/" to separate directories
311 # ossep => pathname that uses os.sep to separate directories
307 # ossep => pathname that uses os.sep to separate directories
312 cwd = repo.getcwd()
308 cwd = repo.getcwd()
313 targets = {}
309 targets = {}
314 after = opts.get("after")
310 after = opts.get("after")
315 dryrun = opts.get("dry_run")
311 dryrun = opts.get("dry_run")
316
312
317 def walkpat(pat):
313 def walkpat(pat):
318 srcs = []
314 srcs = []
319 m = match(repo, [pat], opts, globbed=True)
315 m = match(repo, [pat], opts, globbed=True)
320 for tag, abs in walk(repo, m):
316 for tag, abs in repo.walk(m):
321 state = repo.dirstate[abs]
317 state = repo.dirstate[abs]
322 rel = m.rel(abs)
318 rel = m.rel(abs)
323 exact = m.exact(abs)
319 exact = m.exact(abs)
324 if state in '?r':
320 if state in '?r':
325 if exact and state == '?':
321 if exact and state == '?':
326 ui.warn(_('%s: not copying - file is not managed\n') % rel)
322 ui.warn(_('%s: not copying - file is not managed\n') % rel)
327 if exact and state == 'r':
323 if exact and state == 'r':
328 ui.warn(_('%s: not copying - file has been marked for'
324 ui.warn(_('%s: not copying - file has been marked for'
329 ' remove\n') % rel)
325 ' remove\n') % rel)
330 continue
326 continue
331 # abs: hgsep
327 # abs: hgsep
332 # rel: ossep
328 # rel: ossep
333 srcs.append((abs, rel, exact))
329 srcs.append((abs, rel, exact))
334 return srcs
330 return srcs
335
331
336 # abssrc: hgsep
332 # abssrc: hgsep
337 # relsrc: ossep
333 # relsrc: ossep
338 # otarget: ossep
334 # otarget: ossep
339 def copyfile(abssrc, relsrc, otarget, exact):
335 def copyfile(abssrc, relsrc, otarget, exact):
340 abstarget = util.canonpath(repo.root, cwd, otarget)
336 abstarget = util.canonpath(repo.root, cwd, otarget)
341 reltarget = repo.pathto(abstarget, cwd)
337 reltarget = repo.pathto(abstarget, cwd)
342 target = repo.wjoin(abstarget)
338 target = repo.wjoin(abstarget)
343 src = repo.wjoin(abssrc)
339 src = repo.wjoin(abssrc)
344 state = repo.dirstate[abstarget]
340 state = repo.dirstate[abstarget]
345
341
346 # check for collisions
342 # check for collisions
347 prevsrc = targets.get(abstarget)
343 prevsrc = targets.get(abstarget)
348 if prevsrc is not None:
344 if prevsrc is not None:
349 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
345 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
350 (reltarget, repo.pathto(abssrc, cwd),
346 (reltarget, repo.pathto(abssrc, cwd),
351 repo.pathto(prevsrc, cwd)))
347 repo.pathto(prevsrc, cwd)))
352 return
348 return
353
349
354 # check for overwrites
350 # check for overwrites
355 exists = os.path.exists(target)
351 exists = os.path.exists(target)
356 if (not after and exists or after and state in 'mn'):
352 if (not after and exists or after and state in 'mn'):
357 if not opts['force']:
353 if not opts['force']:
358 ui.warn(_('%s: not overwriting - file exists\n') %
354 ui.warn(_('%s: not overwriting - file exists\n') %
359 reltarget)
355 reltarget)
360 return
356 return
361
357
362 if after:
358 if after:
363 if not exists:
359 if not exists:
364 return
360 return
365 elif not dryrun:
361 elif not dryrun:
366 try:
362 try:
367 if exists:
363 if exists:
368 os.unlink(target)
364 os.unlink(target)
369 targetdir = os.path.dirname(target) or '.'
365 targetdir = os.path.dirname(target) or '.'
370 if not os.path.isdir(targetdir):
366 if not os.path.isdir(targetdir):
371 os.makedirs(targetdir)
367 os.makedirs(targetdir)
372 util.copyfile(src, target)
368 util.copyfile(src, target)
373 except IOError, inst:
369 except IOError, inst:
374 if inst.errno == errno.ENOENT:
370 if inst.errno == errno.ENOENT:
375 ui.warn(_('%s: deleted in working copy\n') % relsrc)
371 ui.warn(_('%s: deleted in working copy\n') % relsrc)
376 else:
372 else:
377 ui.warn(_('%s: cannot copy - %s\n') %
373 ui.warn(_('%s: cannot copy - %s\n') %
378 (relsrc, inst.strerror))
374 (relsrc, inst.strerror))
379 return True # report a failure
375 return True # report a failure
380
376
381 if ui.verbose or not exact:
377 if ui.verbose or not exact:
382 action = rename and "moving" or "copying"
378 action = rename and "moving" or "copying"
383 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
379 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
384
380
385 targets[abstarget] = abssrc
381 targets[abstarget] = abssrc
386
382
387 # fix up dirstate
383 # fix up dirstate
388 origsrc = repo.dirstate.copied(abssrc) or abssrc
384 origsrc = repo.dirstate.copied(abssrc) or abssrc
389 if abstarget == origsrc: # copying back a copy?
385 if abstarget == origsrc: # copying back a copy?
390 if state not in 'mn' and not dryrun:
386 if state not in 'mn' and not dryrun:
391 repo.dirstate.normallookup(abstarget)
387 repo.dirstate.normallookup(abstarget)
392 else:
388 else:
393 if repo.dirstate[origsrc] == 'a':
389 if repo.dirstate[origsrc] == 'a':
394 if not ui.quiet:
390 if not ui.quiet:
395 ui.warn(_("%s has not been committed yet, so no copy "
391 ui.warn(_("%s has not been committed yet, so no copy "
396 "data will be stored for %s.\n")
392 "data will be stored for %s.\n")
397 % (repo.pathto(origsrc, cwd), reltarget))
393 % (repo.pathto(origsrc, cwd), reltarget))
398 if abstarget not in repo.dirstate and not dryrun:
394 if abstarget not in repo.dirstate and not dryrun:
399 repo.add([abstarget])
395 repo.add([abstarget])
400 elif not dryrun:
396 elif not dryrun:
401 repo.copy(origsrc, abstarget)
397 repo.copy(origsrc, abstarget)
402
398
403 if rename and not dryrun:
399 if rename and not dryrun:
404 repo.remove([abssrc], not after)
400 repo.remove([abssrc], not after)
405
401
406 # pat: ossep
402 # pat: ossep
407 # dest ossep
403 # dest ossep
408 # srcs: list of (hgsep, hgsep, ossep, bool)
404 # srcs: list of (hgsep, hgsep, ossep, bool)
409 # return: function that takes hgsep and returns ossep
405 # return: function that takes hgsep and returns ossep
410 def targetpathfn(pat, dest, srcs):
406 def targetpathfn(pat, dest, srcs):
411 if os.path.isdir(pat):
407 if os.path.isdir(pat):
412 abspfx = util.canonpath(repo.root, cwd, pat)
408 abspfx = util.canonpath(repo.root, cwd, pat)
413 abspfx = util.localpath(abspfx)
409 abspfx = util.localpath(abspfx)
414 if destdirexists:
410 if destdirexists:
415 striplen = len(os.path.split(abspfx)[0])
411 striplen = len(os.path.split(abspfx)[0])
416 else:
412 else:
417 striplen = len(abspfx)
413 striplen = len(abspfx)
418 if striplen:
414 if striplen:
419 striplen += len(os.sep)
415 striplen += len(os.sep)
420 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
416 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
421 elif destdirexists:
417 elif destdirexists:
422 res = lambda p: os.path.join(dest,
418 res = lambda p: os.path.join(dest,
423 os.path.basename(util.localpath(p)))
419 os.path.basename(util.localpath(p)))
424 else:
420 else:
425 res = lambda p: dest
421 res = lambda p: dest
426 return res
422 return res
427
423
428 # pat: ossep
424 # pat: ossep
429 # dest ossep
425 # dest ossep
430 # srcs: list of (hgsep, hgsep, ossep, bool)
426 # srcs: list of (hgsep, hgsep, ossep, bool)
431 # return: function that takes hgsep and returns ossep
427 # return: function that takes hgsep and returns ossep
432 def targetpathafterfn(pat, dest, srcs):
428 def targetpathafterfn(pat, dest, srcs):
433 if util.patkind(pat, None)[0]:
429 if util.patkind(pat, None)[0]:
434 # a mercurial pattern
430 # a mercurial pattern
435 res = lambda p: os.path.join(dest,
431 res = lambda p: os.path.join(dest,
436 os.path.basename(util.localpath(p)))
432 os.path.basename(util.localpath(p)))
437 else:
433 else:
438 abspfx = util.canonpath(repo.root, cwd, pat)
434 abspfx = util.canonpath(repo.root, cwd, pat)
439 if len(abspfx) < len(srcs[0][0]):
435 if len(abspfx) < len(srcs[0][0]):
440 # A directory. Either the target path contains the last
436 # A directory. Either the target path contains the last
441 # component of the source path or it does not.
437 # component of the source path or it does not.
442 def evalpath(striplen):
438 def evalpath(striplen):
443 score = 0
439 score = 0
444 for s in srcs:
440 for s in srcs:
445 t = os.path.join(dest, util.localpath(s[0])[striplen:])
441 t = os.path.join(dest, util.localpath(s[0])[striplen:])
446 if os.path.exists(t):
442 if os.path.exists(t):
447 score += 1
443 score += 1
448 return score
444 return score
449
445
450 abspfx = util.localpath(abspfx)
446 abspfx = util.localpath(abspfx)
451 striplen = len(abspfx)
447 striplen = len(abspfx)
452 if striplen:
448 if striplen:
453 striplen += len(os.sep)
449 striplen += len(os.sep)
454 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
450 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
455 score = evalpath(striplen)
451 score = evalpath(striplen)
456 striplen1 = len(os.path.split(abspfx)[0])
452 striplen1 = len(os.path.split(abspfx)[0])
457 if striplen1:
453 if striplen1:
458 striplen1 += len(os.sep)
454 striplen1 += len(os.sep)
459 if evalpath(striplen1) > score:
455 if evalpath(striplen1) > score:
460 striplen = striplen1
456 striplen = striplen1
461 res = lambda p: os.path.join(dest,
457 res = lambda p: os.path.join(dest,
462 util.localpath(p)[striplen:])
458 util.localpath(p)[striplen:])
463 else:
459 else:
464 # a file
460 # a file
465 if destdirexists:
461 if destdirexists:
466 res = lambda p: os.path.join(dest,
462 res = lambda p: os.path.join(dest,
467 os.path.basename(util.localpath(p)))
463 os.path.basename(util.localpath(p)))
468 else:
464 else:
469 res = lambda p: dest
465 res = lambda p: dest
470 return res
466 return res
471
467
472
468
473 pats = util.expand_glob(pats)
469 pats = util.expand_glob(pats)
474 if not pats:
470 if not pats:
475 raise util.Abort(_('no source or destination specified'))
471 raise util.Abort(_('no source or destination specified'))
476 if len(pats) == 1:
472 if len(pats) == 1:
477 raise util.Abort(_('no destination specified'))
473 raise util.Abort(_('no destination specified'))
478 dest = pats.pop()
474 dest = pats.pop()
479 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
475 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
480 if not destdirexists:
476 if not destdirexists:
481 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
477 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
482 raise util.Abort(_('with multiple sources, destination must be an '
478 raise util.Abort(_('with multiple sources, destination must be an '
483 'existing directory'))
479 'existing directory'))
484 if util.endswithsep(dest):
480 if util.endswithsep(dest):
485 raise util.Abort(_('destination %s is not a directory') % dest)
481 raise util.Abort(_('destination %s is not a directory') % dest)
486
482
487 tfn = targetpathfn
483 tfn = targetpathfn
488 if after:
484 if after:
489 tfn = targetpathafterfn
485 tfn = targetpathafterfn
490 copylist = []
486 copylist = []
491 for pat in pats:
487 for pat in pats:
492 srcs = walkpat(pat)
488 srcs = walkpat(pat)
493 if not srcs:
489 if not srcs:
494 continue
490 continue
495 copylist.append((tfn(pat, dest, srcs), srcs))
491 copylist.append((tfn(pat, dest, srcs), srcs))
496 if not copylist:
492 if not copylist:
497 raise util.Abort(_('no files to copy'))
493 raise util.Abort(_('no files to copy'))
498
494
499 errors = 0
495 errors = 0
500 for targetpath, srcs in copylist:
496 for targetpath, srcs in copylist:
501 for abssrc, relsrc, exact in srcs:
497 for abssrc, relsrc, exact in srcs:
502 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
498 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
503 errors += 1
499 errors += 1
504
500
505 if errors:
501 if errors:
506 ui.warn(_('(consider using --after)\n'))
502 ui.warn(_('(consider using --after)\n'))
507
503
508 return errors
504 return errors
509
505
510 def service(opts, parentfn=None, initfn=None, runfn=None):
506 def service(opts, parentfn=None, initfn=None, runfn=None):
511 '''Run a command as a service.'''
507 '''Run a command as a service.'''
512
508
513 if opts['daemon'] and not opts['daemon_pipefds']:
509 if opts['daemon'] and not opts['daemon_pipefds']:
514 rfd, wfd = os.pipe()
510 rfd, wfd = os.pipe()
515 args = sys.argv[:]
511 args = sys.argv[:]
516 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
512 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
517 # Don't pass --cwd to the child process, because we've already
513 # Don't pass --cwd to the child process, because we've already
518 # changed directory.
514 # changed directory.
519 for i in xrange(1,len(args)):
515 for i in xrange(1,len(args)):
520 if args[i].startswith('--cwd='):
516 if args[i].startswith('--cwd='):
521 del args[i]
517 del args[i]
522 break
518 break
523 elif args[i].startswith('--cwd'):
519 elif args[i].startswith('--cwd'):
524 del args[i:i+2]
520 del args[i:i+2]
525 break
521 break
526 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
522 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
527 args[0], args)
523 args[0], args)
528 os.close(wfd)
524 os.close(wfd)
529 os.read(rfd, 1)
525 os.read(rfd, 1)
530 if parentfn:
526 if parentfn:
531 return parentfn(pid)
527 return parentfn(pid)
532 else:
528 else:
533 os._exit(0)
529 os._exit(0)
534
530
535 if initfn:
531 if initfn:
536 initfn()
532 initfn()
537
533
538 if opts['pid_file']:
534 if opts['pid_file']:
539 fp = open(opts['pid_file'], 'w')
535 fp = open(opts['pid_file'], 'w')
540 fp.write(str(os.getpid()) + '\n')
536 fp.write(str(os.getpid()) + '\n')
541 fp.close()
537 fp.close()
542
538
543 if opts['daemon_pipefds']:
539 if opts['daemon_pipefds']:
544 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
540 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
545 os.close(rfd)
541 os.close(rfd)
546 try:
542 try:
547 os.setsid()
543 os.setsid()
548 except AttributeError:
544 except AttributeError:
549 pass
545 pass
550 os.write(wfd, 'y')
546 os.write(wfd, 'y')
551 os.close(wfd)
547 os.close(wfd)
552 sys.stdout.flush()
548 sys.stdout.flush()
553 sys.stderr.flush()
549 sys.stderr.flush()
554 fd = os.open(util.nulldev, os.O_RDWR)
550 fd = os.open(util.nulldev, os.O_RDWR)
555 if fd != 0: os.dup2(fd, 0)
551 if fd != 0: os.dup2(fd, 0)
556 if fd != 1: os.dup2(fd, 1)
552 if fd != 1: os.dup2(fd, 1)
557 if fd != 2: os.dup2(fd, 2)
553 if fd != 2: os.dup2(fd, 2)
558 if fd not in (0, 1, 2): os.close(fd)
554 if fd not in (0, 1, 2): os.close(fd)
559
555
560 if runfn:
556 if runfn:
561 return runfn()
557 return runfn()
562
558
563 class changeset_printer(object):
559 class changeset_printer(object):
564 '''show changeset information when templating not requested.'''
560 '''show changeset information when templating not requested.'''
565
561
566 def __init__(self, ui, repo, patch, buffered):
562 def __init__(self, ui, repo, patch, buffered):
567 self.ui = ui
563 self.ui = ui
568 self.repo = repo
564 self.repo = repo
569 self.buffered = buffered
565 self.buffered = buffered
570 self.patch = patch
566 self.patch = patch
571 self.header = {}
567 self.header = {}
572 self.hunk = {}
568 self.hunk = {}
573 self.lastheader = None
569 self.lastheader = None
574
570
575 def flush(self, rev):
571 def flush(self, rev):
576 if rev in self.header:
572 if rev in self.header:
577 h = self.header[rev]
573 h = self.header[rev]
578 if h != self.lastheader:
574 if h != self.lastheader:
579 self.lastheader = h
575 self.lastheader = h
580 self.ui.write(h)
576 self.ui.write(h)
581 del self.header[rev]
577 del self.header[rev]
582 if rev in self.hunk:
578 if rev in self.hunk:
583 self.ui.write(self.hunk[rev])
579 self.ui.write(self.hunk[rev])
584 del self.hunk[rev]
580 del self.hunk[rev]
585 return 1
581 return 1
586 return 0
582 return 0
587
583
588 def show(self, rev=0, changenode=None, copies=(), **props):
584 def show(self, rev=0, changenode=None, copies=(), **props):
589 if self.buffered:
585 if self.buffered:
590 self.ui.pushbuffer()
586 self.ui.pushbuffer()
591 self._show(rev, changenode, copies, props)
587 self._show(rev, changenode, copies, props)
592 self.hunk[rev] = self.ui.popbuffer()
588 self.hunk[rev] = self.ui.popbuffer()
593 else:
589 else:
594 self._show(rev, changenode, copies, props)
590 self._show(rev, changenode, copies, props)
595
591
596 def _show(self, rev, changenode, copies, props):
592 def _show(self, rev, changenode, copies, props):
597 '''show a single changeset or file revision'''
593 '''show a single changeset or file revision'''
598 log = self.repo.changelog
594 log = self.repo.changelog
599 if changenode is None:
595 if changenode is None:
600 changenode = log.node(rev)
596 changenode = log.node(rev)
601 elif not rev:
597 elif not rev:
602 rev = log.rev(changenode)
598 rev = log.rev(changenode)
603
599
604 if self.ui.quiet:
600 if self.ui.quiet:
605 self.ui.write("%d:%s\n" % (rev, short(changenode)))
601 self.ui.write("%d:%s\n" % (rev, short(changenode)))
606 return
602 return
607
603
608 changes = log.read(changenode)
604 changes = log.read(changenode)
609 date = util.datestr(changes[2])
605 date = util.datestr(changes[2])
610 extra = changes[5]
606 extra = changes[5]
611 branch = extra.get("branch")
607 branch = extra.get("branch")
612
608
613 hexfunc = self.ui.debugflag and hex or short
609 hexfunc = self.ui.debugflag and hex or short
614
610
615 parents = [(p, hexfunc(log.node(p)))
611 parents = [(p, hexfunc(log.node(p)))
616 for p in self._meaningful_parentrevs(log, rev)]
612 for p in self._meaningful_parentrevs(log, rev)]
617
613
618 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
614 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
619
615
620 # don't show the default branch name
616 # don't show the default branch name
621 if branch != 'default':
617 if branch != 'default':
622 branch = util.tolocal(branch)
618 branch = util.tolocal(branch)
623 self.ui.write(_("branch: %s\n") % branch)
619 self.ui.write(_("branch: %s\n") % branch)
624 for tag in self.repo.nodetags(changenode):
620 for tag in self.repo.nodetags(changenode):
625 self.ui.write(_("tag: %s\n") % tag)
621 self.ui.write(_("tag: %s\n") % tag)
626 for parent in parents:
622 for parent in parents:
627 self.ui.write(_("parent: %d:%s\n") % parent)
623 self.ui.write(_("parent: %d:%s\n") % parent)
628
624
629 if self.ui.debugflag:
625 if self.ui.debugflag:
630 self.ui.write(_("manifest: %d:%s\n") %
626 self.ui.write(_("manifest: %d:%s\n") %
631 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
627 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
632 self.ui.write(_("user: %s\n") % changes[1])
628 self.ui.write(_("user: %s\n") % changes[1])
633 self.ui.write(_("date: %s\n") % date)
629 self.ui.write(_("date: %s\n") % date)
634
630
635 if self.ui.debugflag:
631 if self.ui.debugflag:
636 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
632 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
637 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
633 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
638 files):
634 files):
639 if value:
635 if value:
640 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
636 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
641 elif changes[3] and self.ui.verbose:
637 elif changes[3] and self.ui.verbose:
642 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
638 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
643 if copies and self.ui.verbose:
639 if copies and self.ui.verbose:
644 copies = ['%s (%s)' % c for c in copies]
640 copies = ['%s (%s)' % c for c in copies]
645 self.ui.write(_("copies: %s\n") % ' '.join(copies))
641 self.ui.write(_("copies: %s\n") % ' '.join(copies))
646
642
647 if extra and self.ui.debugflag:
643 if extra and self.ui.debugflag:
648 extraitems = extra.items()
644 extraitems = extra.items()
649 extraitems.sort()
645 extraitems.sort()
650 for key, value in extraitems:
646 for key, value in extraitems:
651 self.ui.write(_("extra: %s=%s\n")
647 self.ui.write(_("extra: %s=%s\n")
652 % (key, value.encode('string_escape')))
648 % (key, value.encode('string_escape')))
653
649
654 description = changes[4].strip()
650 description = changes[4].strip()
655 if description:
651 if description:
656 if self.ui.verbose:
652 if self.ui.verbose:
657 self.ui.write(_("description:\n"))
653 self.ui.write(_("description:\n"))
658 self.ui.write(description)
654 self.ui.write(description)
659 self.ui.write("\n\n")
655 self.ui.write("\n\n")
660 else:
656 else:
661 self.ui.write(_("summary: %s\n") %
657 self.ui.write(_("summary: %s\n") %
662 description.splitlines()[0])
658 description.splitlines()[0])
663 self.ui.write("\n")
659 self.ui.write("\n")
664
660
665 self.showpatch(changenode)
661 self.showpatch(changenode)
666
662
667 def showpatch(self, node):
663 def showpatch(self, node):
668 if self.patch:
664 if self.patch:
669 prev = self.repo.changelog.parents(node)[0]
665 prev = self.repo.changelog.parents(node)[0]
670 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
666 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
671 opts=patch.diffopts(self.ui))
667 opts=patch.diffopts(self.ui))
672 self.ui.write("\n")
668 self.ui.write("\n")
673
669
674 def _meaningful_parentrevs(self, log, rev):
670 def _meaningful_parentrevs(self, log, rev):
675 """Return list of meaningful (or all if debug) parentrevs for rev.
671 """Return list of meaningful (or all if debug) parentrevs for rev.
676
672
677 For merges (two non-nullrev revisions) both parents are meaningful.
673 For merges (two non-nullrev revisions) both parents are meaningful.
678 Otherwise the first parent revision is considered meaningful if it
674 Otherwise the first parent revision is considered meaningful if it
679 is not the preceding revision.
675 is not the preceding revision.
680 """
676 """
681 parents = log.parentrevs(rev)
677 parents = log.parentrevs(rev)
682 if not self.ui.debugflag and parents[1] == nullrev:
678 if not self.ui.debugflag and parents[1] == nullrev:
683 if parents[0] >= rev - 1:
679 if parents[0] >= rev - 1:
684 parents = []
680 parents = []
685 else:
681 else:
686 parents = [parents[0]]
682 parents = [parents[0]]
687 return parents
683 return parents
688
684
689
685
690 class changeset_templater(changeset_printer):
686 class changeset_templater(changeset_printer):
691 '''format changeset information.'''
687 '''format changeset information.'''
692
688
693 def __init__(self, ui, repo, patch, mapfile, buffered):
689 def __init__(self, ui, repo, patch, mapfile, buffered):
694 changeset_printer.__init__(self, ui, repo, patch, buffered)
690 changeset_printer.__init__(self, ui, repo, patch, buffered)
695 filters = templatefilters.filters.copy()
691 filters = templatefilters.filters.copy()
696 filters['formatnode'] = (ui.debugflag and (lambda x: x)
692 filters['formatnode'] = (ui.debugflag and (lambda x: x)
697 or (lambda x: x[:12]))
693 or (lambda x: x[:12]))
698 self.t = templater.templater(mapfile, filters,
694 self.t = templater.templater(mapfile, filters,
699 cache={
695 cache={
700 'parent': '{rev}:{node|formatnode} ',
696 'parent': '{rev}:{node|formatnode} ',
701 'manifest': '{rev}:{node|formatnode}',
697 'manifest': '{rev}:{node|formatnode}',
702 'filecopy': '{name} ({source})'})
698 'filecopy': '{name} ({source})'})
703
699
704 def use_template(self, t):
700 def use_template(self, t):
705 '''set template string to use'''
701 '''set template string to use'''
706 self.t.cache['changeset'] = t
702 self.t.cache['changeset'] = t
707
703
708 def _show(self, rev, changenode, copies, props):
704 def _show(self, rev, changenode, copies, props):
709 '''show a single changeset or file revision'''
705 '''show a single changeset or file revision'''
710 log = self.repo.changelog
706 log = self.repo.changelog
711 if changenode is None:
707 if changenode is None:
712 changenode = log.node(rev)
708 changenode = log.node(rev)
713 elif not rev:
709 elif not rev:
714 rev = log.rev(changenode)
710 rev = log.rev(changenode)
715
711
716 changes = log.read(changenode)
712 changes = log.read(changenode)
717
713
718 def showlist(name, values, plural=None, **args):
714 def showlist(name, values, plural=None, **args):
719 '''expand set of values.
715 '''expand set of values.
720 name is name of key in template map.
716 name is name of key in template map.
721 values is list of strings or dicts.
717 values is list of strings or dicts.
722 plural is plural of name, if not simply name + 's'.
718 plural is plural of name, if not simply name + 's'.
723
719
724 expansion works like this, given name 'foo'.
720 expansion works like this, given name 'foo'.
725
721
726 if values is empty, expand 'no_foos'.
722 if values is empty, expand 'no_foos'.
727
723
728 if 'foo' not in template map, return values as a string,
724 if 'foo' not in template map, return values as a string,
729 joined by space.
725 joined by space.
730
726
731 expand 'start_foos'.
727 expand 'start_foos'.
732
728
733 for each value, expand 'foo'. if 'last_foo' in template
729 for each value, expand 'foo'. if 'last_foo' in template
734 map, expand it instead of 'foo' for last key.
730 map, expand it instead of 'foo' for last key.
735
731
736 expand 'end_foos'.
732 expand 'end_foos'.
737 '''
733 '''
738 if plural: names = plural
734 if plural: names = plural
739 else: names = name + 's'
735 else: names = name + 's'
740 if not values:
736 if not values:
741 noname = 'no_' + names
737 noname = 'no_' + names
742 if noname in self.t:
738 if noname in self.t:
743 yield self.t(noname, **args)
739 yield self.t(noname, **args)
744 return
740 return
745 if name not in self.t:
741 if name not in self.t:
746 if isinstance(values[0], str):
742 if isinstance(values[0], str):
747 yield ' '.join(values)
743 yield ' '.join(values)
748 else:
744 else:
749 for v in values:
745 for v in values:
750 yield dict(v, **args)
746 yield dict(v, **args)
751 return
747 return
752 startname = 'start_' + names
748 startname = 'start_' + names
753 if startname in self.t:
749 if startname in self.t:
754 yield self.t(startname, **args)
750 yield self.t(startname, **args)
755 vargs = args.copy()
751 vargs = args.copy()
756 def one(v, tag=name):
752 def one(v, tag=name):
757 try:
753 try:
758 vargs.update(v)
754 vargs.update(v)
759 except (AttributeError, ValueError):
755 except (AttributeError, ValueError):
760 try:
756 try:
761 for a, b in v:
757 for a, b in v:
762 vargs[a] = b
758 vargs[a] = b
763 except ValueError:
759 except ValueError:
764 vargs[name] = v
760 vargs[name] = v
765 return self.t(tag, **vargs)
761 return self.t(tag, **vargs)
766 lastname = 'last_' + name
762 lastname = 'last_' + name
767 if lastname in self.t:
763 if lastname in self.t:
768 last = values.pop()
764 last = values.pop()
769 else:
765 else:
770 last = None
766 last = None
771 for v in values:
767 for v in values:
772 yield one(v)
768 yield one(v)
773 if last is not None:
769 if last is not None:
774 yield one(last, tag=lastname)
770 yield one(last, tag=lastname)
775 endname = 'end_' + names
771 endname = 'end_' + names
776 if endname in self.t:
772 if endname in self.t:
777 yield self.t(endname, **args)
773 yield self.t(endname, **args)
778
774
779 def showbranches(**args):
775 def showbranches(**args):
780 branch = changes[5].get("branch")
776 branch = changes[5].get("branch")
781 if branch != 'default':
777 if branch != 'default':
782 branch = util.tolocal(branch)
778 branch = util.tolocal(branch)
783 return showlist('branch', [branch], plural='branches', **args)
779 return showlist('branch', [branch], plural='branches', **args)
784
780
785 def showparents(**args):
781 def showparents(**args):
786 parents = [[('rev', p), ('node', hex(log.node(p)))]
782 parents = [[('rev', p), ('node', hex(log.node(p)))]
787 for p in self._meaningful_parentrevs(log, rev)]
783 for p in self._meaningful_parentrevs(log, rev)]
788 return showlist('parent', parents, **args)
784 return showlist('parent', parents, **args)
789
785
790 def showtags(**args):
786 def showtags(**args):
791 return showlist('tag', self.repo.nodetags(changenode), **args)
787 return showlist('tag', self.repo.nodetags(changenode), **args)
792
788
793 def showextras(**args):
789 def showextras(**args):
794 extras = changes[5].items()
790 extras = changes[5].items()
795 extras.sort()
791 extras.sort()
796 for key, value in extras:
792 for key, value in extras:
797 args = args.copy()
793 args = args.copy()
798 args.update(dict(key=key, value=value))
794 args.update(dict(key=key, value=value))
799 yield self.t('extra', **args)
795 yield self.t('extra', **args)
800
796
801 def showcopies(**args):
797 def showcopies(**args):
802 c = [{'name': x[0], 'source': x[1]} for x in copies]
798 c = [{'name': x[0], 'source': x[1]} for x in copies]
803 return showlist('file_copy', c, plural='file_copies', **args)
799 return showlist('file_copy', c, plural='file_copies', **args)
804
800
805 files = []
801 files = []
806 def getfiles():
802 def getfiles():
807 if not files:
803 if not files:
808 files[:] = self.repo.status(
804 files[:] = self.repo.status(
809 log.parents(changenode)[0], changenode)[:3]
805 log.parents(changenode)[0], changenode)[:3]
810 return files
806 return files
811 def showfiles(**args):
807 def showfiles(**args):
812 return showlist('file', changes[3], **args)
808 return showlist('file', changes[3], **args)
813 def showmods(**args):
809 def showmods(**args):
814 return showlist('file_mod', getfiles()[0], **args)
810 return showlist('file_mod', getfiles()[0], **args)
815 def showadds(**args):
811 def showadds(**args):
816 return showlist('file_add', getfiles()[1], **args)
812 return showlist('file_add', getfiles()[1], **args)
817 def showdels(**args):
813 def showdels(**args):
818 return showlist('file_del', getfiles()[2], **args)
814 return showlist('file_del', getfiles()[2], **args)
819 def showmanifest(**args):
815 def showmanifest(**args):
820 args = args.copy()
816 args = args.copy()
821 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
817 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
822 node=hex(changes[0])))
818 node=hex(changes[0])))
823 return self.t('manifest', **args)
819 return self.t('manifest', **args)
824
820
825 defprops = {
821 defprops = {
826 'author': changes[1],
822 'author': changes[1],
827 'branches': showbranches,
823 'branches': showbranches,
828 'date': changes[2],
824 'date': changes[2],
829 'desc': changes[4].strip(),
825 'desc': changes[4].strip(),
830 'file_adds': showadds,
826 'file_adds': showadds,
831 'file_dels': showdels,
827 'file_dels': showdels,
832 'file_mods': showmods,
828 'file_mods': showmods,
833 'files': showfiles,
829 'files': showfiles,
834 'file_copies': showcopies,
830 'file_copies': showcopies,
835 'manifest': showmanifest,
831 'manifest': showmanifest,
836 'node': hex(changenode),
832 'node': hex(changenode),
837 'parents': showparents,
833 'parents': showparents,
838 'rev': rev,
834 'rev': rev,
839 'tags': showtags,
835 'tags': showtags,
840 'extras': showextras,
836 'extras': showextras,
841 }
837 }
842 props = props.copy()
838 props = props.copy()
843 props.update(defprops)
839 props.update(defprops)
844
840
845 try:
841 try:
846 if self.ui.debugflag and 'header_debug' in self.t:
842 if self.ui.debugflag and 'header_debug' in self.t:
847 key = 'header_debug'
843 key = 'header_debug'
848 elif self.ui.quiet and 'header_quiet' in self.t:
844 elif self.ui.quiet and 'header_quiet' in self.t:
849 key = 'header_quiet'
845 key = 'header_quiet'
850 elif self.ui.verbose and 'header_verbose' in self.t:
846 elif self.ui.verbose and 'header_verbose' in self.t:
851 key = 'header_verbose'
847 key = 'header_verbose'
852 elif 'header' in self.t:
848 elif 'header' in self.t:
853 key = 'header'
849 key = 'header'
854 else:
850 else:
855 key = ''
851 key = ''
856 if key:
852 if key:
857 h = templater.stringify(self.t(key, **props))
853 h = templater.stringify(self.t(key, **props))
858 if self.buffered:
854 if self.buffered:
859 self.header[rev] = h
855 self.header[rev] = h
860 else:
856 else:
861 self.ui.write(h)
857 self.ui.write(h)
862 if self.ui.debugflag and 'changeset_debug' in self.t:
858 if self.ui.debugflag and 'changeset_debug' in self.t:
863 key = 'changeset_debug'
859 key = 'changeset_debug'
864 elif self.ui.quiet and 'changeset_quiet' in self.t:
860 elif self.ui.quiet and 'changeset_quiet' in self.t:
865 key = 'changeset_quiet'
861 key = 'changeset_quiet'
866 elif self.ui.verbose and 'changeset_verbose' in self.t:
862 elif self.ui.verbose and 'changeset_verbose' in self.t:
867 key = 'changeset_verbose'
863 key = 'changeset_verbose'
868 else:
864 else:
869 key = 'changeset'
865 key = 'changeset'
870 self.ui.write(templater.stringify(self.t(key, **props)))
866 self.ui.write(templater.stringify(self.t(key, **props)))
871 self.showpatch(changenode)
867 self.showpatch(changenode)
872 except KeyError, inst:
868 except KeyError, inst:
873 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
869 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
874 inst.args[0]))
870 inst.args[0]))
875 except SyntaxError, inst:
871 except SyntaxError, inst:
876 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
872 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
877
873
878 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
874 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
879 """show one changeset using template or regular display.
875 """show one changeset using template or regular display.
880
876
881 Display format will be the first non-empty hit of:
877 Display format will be the first non-empty hit of:
882 1. option 'template'
878 1. option 'template'
883 2. option 'style'
879 2. option 'style'
884 3. [ui] setting 'logtemplate'
880 3. [ui] setting 'logtemplate'
885 4. [ui] setting 'style'
881 4. [ui] setting 'style'
886 If all of these values are either the unset or the empty string,
882 If all of these values are either the unset or the empty string,
887 regular display via changeset_printer() is done.
883 regular display via changeset_printer() is done.
888 """
884 """
889 # options
885 # options
890 patch = False
886 patch = False
891 if opts.get('patch'):
887 if opts.get('patch'):
892 patch = matchfn or util.always
888 patch = matchfn or util.always
893
889
894 tmpl = opts.get('template')
890 tmpl = opts.get('template')
895 mapfile = None
891 mapfile = None
896 if tmpl:
892 if tmpl:
897 tmpl = templater.parsestring(tmpl, quoted=False)
893 tmpl = templater.parsestring(tmpl, quoted=False)
898 else:
894 else:
899 mapfile = opts.get('style')
895 mapfile = opts.get('style')
900 # ui settings
896 # ui settings
901 if not mapfile:
897 if not mapfile:
902 tmpl = ui.config('ui', 'logtemplate')
898 tmpl = ui.config('ui', 'logtemplate')
903 if tmpl:
899 if tmpl:
904 tmpl = templater.parsestring(tmpl)
900 tmpl = templater.parsestring(tmpl)
905 else:
901 else:
906 mapfile = ui.config('ui', 'style')
902 mapfile = ui.config('ui', 'style')
907
903
908 if tmpl or mapfile:
904 if tmpl or mapfile:
909 if mapfile:
905 if mapfile:
910 if not os.path.split(mapfile)[0]:
906 if not os.path.split(mapfile)[0]:
911 mapname = (templater.templatepath('map-cmdline.' + mapfile)
907 mapname = (templater.templatepath('map-cmdline.' + mapfile)
912 or templater.templatepath(mapfile))
908 or templater.templatepath(mapfile))
913 if mapname: mapfile = mapname
909 if mapname: mapfile = mapname
914 try:
910 try:
915 t = changeset_templater(ui, repo, patch, mapfile, buffered)
911 t = changeset_templater(ui, repo, patch, mapfile, buffered)
916 except SyntaxError, inst:
912 except SyntaxError, inst:
917 raise util.Abort(inst.args[0])
913 raise util.Abort(inst.args[0])
918 if tmpl: t.use_template(tmpl)
914 if tmpl: t.use_template(tmpl)
919 return t
915 return t
920 return changeset_printer(ui, repo, patch, buffered)
916 return changeset_printer(ui, repo, patch, buffered)
921
917
922 def finddate(ui, repo, date):
918 def finddate(ui, repo, date):
923 """Find the tipmost changeset that matches the given date spec"""
919 """Find the tipmost changeset that matches the given date spec"""
924 df = util.matchdate(date)
920 df = util.matchdate(date)
925 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
921 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
926 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
922 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
927 results = {}
923 results = {}
928 for st, rev, fns in changeiter:
924 for st, rev, fns in changeiter:
929 if st == 'add':
925 if st == 'add':
930 d = get(rev)[2]
926 d = get(rev)[2]
931 if df(d[0]):
927 if df(d[0]):
932 results[rev] = d
928 results[rev] = d
933 elif st == 'iter':
929 elif st == 'iter':
934 if rev in results:
930 if rev in results:
935 ui.status("Found revision %s from %s\n" %
931 ui.status("Found revision %s from %s\n" %
936 (rev, util.datestr(results[rev])))
932 (rev, util.datestr(results[rev])))
937 return str(rev)
933 return str(rev)
938
934
939 raise util.Abort(_("revision matching date not found"))
935 raise util.Abort(_("revision matching date not found"))
940
936
941 def walkchangerevs(ui, repo, pats, change, opts):
937 def walkchangerevs(ui, repo, pats, change, opts):
942 '''Iterate over files and the revs they changed in.
938 '''Iterate over files and the revs they changed in.
943
939
944 Callers most commonly need to iterate backwards over the history
940 Callers most commonly need to iterate backwards over the history
945 it is interested in. Doing so has awful (quadratic-looking)
941 it is interested in. Doing so has awful (quadratic-looking)
946 performance, so we use iterators in a "windowed" way.
942 performance, so we use iterators in a "windowed" way.
947
943
948 We walk a window of revisions in the desired order. Within the
944 We walk a window of revisions in the desired order. Within the
949 window, we first walk forwards to gather data, then in the desired
945 window, we first walk forwards to gather data, then in the desired
950 order (usually backwards) to display it.
946 order (usually backwards) to display it.
951
947
952 This function returns an (iterator, matchfn) tuple. The iterator
948 This function returns an (iterator, matchfn) tuple. The iterator
953 yields 3-tuples. They will be of one of the following forms:
949 yields 3-tuples. They will be of one of the following forms:
954
950
955 "window", incrementing, lastrev: stepping through a window,
951 "window", incrementing, lastrev: stepping through a window,
956 positive if walking forwards through revs, last rev in the
952 positive if walking forwards through revs, last rev in the
957 sequence iterated over - use to reset state for the current window
953 sequence iterated over - use to reset state for the current window
958
954
959 "add", rev, fns: out-of-order traversal of the given file names
955 "add", rev, fns: out-of-order traversal of the given file names
960 fns, which changed during revision rev - use to gather data for
956 fns, which changed during revision rev - use to gather data for
961 possible display
957 possible display
962
958
963 "iter", rev, None: in-order traversal of the revs earlier iterated
959 "iter", rev, None: in-order traversal of the revs earlier iterated
964 over with "add" - use to display data'''
960 over with "add" - use to display data'''
965
961
966 def increasing_windows(start, end, windowsize=8, sizelimit=512):
962 def increasing_windows(start, end, windowsize=8, sizelimit=512):
967 if start < end:
963 if start < end:
968 while start < end:
964 while start < end:
969 yield start, min(windowsize, end-start)
965 yield start, min(windowsize, end-start)
970 start += windowsize
966 start += windowsize
971 if windowsize < sizelimit:
967 if windowsize < sizelimit:
972 windowsize *= 2
968 windowsize *= 2
973 else:
969 else:
974 while start > end:
970 while start > end:
975 yield start, min(windowsize, start-end-1)
971 yield start, min(windowsize, start-end-1)
976 start -= windowsize
972 start -= windowsize
977 if windowsize < sizelimit:
973 if windowsize < sizelimit:
978 windowsize *= 2
974 windowsize *= 2
979
975
980 m = match(repo, pats, opts)
976 m = match(repo, pats, opts)
981 follow = opts.get('follow') or opts.get('follow_first')
977 follow = opts.get('follow') or opts.get('follow_first')
982
978
983 if repo.changelog.count() == 0:
979 if repo.changelog.count() == 0:
984 return [], m
980 return [], m
985
981
986 if follow:
982 if follow:
987 defrange = '%s:0' % repo.changectx().rev()
983 defrange = '%s:0' % repo.changectx().rev()
988 else:
984 else:
989 defrange = '-1:0'
985 defrange = '-1:0'
990 revs = revrange(repo, opts['rev'] or [defrange])
986 revs = revrange(repo, opts['rev'] or [defrange])
991 wanted = {}
987 wanted = {}
992 slowpath = m.anypats() or opts.get('removed')
988 slowpath = m.anypats() or opts.get('removed')
993 fncache = {}
989 fncache = {}
994
990
995 if not slowpath and not m.files():
991 if not slowpath and not m.files():
996 # No files, no patterns. Display all revs.
992 # No files, no patterns. Display all revs.
997 wanted = dict.fromkeys(revs)
993 wanted = dict.fromkeys(revs)
998 copies = []
994 copies = []
999 if not slowpath:
995 if not slowpath:
1000 # Only files, no patterns. Check the history of each file.
996 # Only files, no patterns. Check the history of each file.
1001 def filerevgen(filelog, node):
997 def filerevgen(filelog, node):
1002 cl_count = repo.changelog.count()
998 cl_count = repo.changelog.count()
1003 if node is None:
999 if node is None:
1004 last = filelog.count() - 1
1000 last = filelog.count() - 1
1005 else:
1001 else:
1006 last = filelog.rev(node)
1002 last = filelog.rev(node)
1007 for i, window in increasing_windows(last, nullrev):
1003 for i, window in increasing_windows(last, nullrev):
1008 revs = []
1004 revs = []
1009 for j in xrange(i - window, i + 1):
1005 for j in xrange(i - window, i + 1):
1010 n = filelog.node(j)
1006 n = filelog.node(j)
1011 revs.append((filelog.linkrev(n),
1007 revs.append((filelog.linkrev(n),
1012 follow and filelog.renamed(n)))
1008 follow and filelog.renamed(n)))
1013 revs.reverse()
1009 revs.reverse()
1014 for rev in revs:
1010 for rev in revs:
1015 # only yield rev for which we have the changelog, it can
1011 # only yield rev for which we have the changelog, it can
1016 # happen while doing "hg log" during a pull or commit
1012 # happen while doing "hg log" during a pull or commit
1017 if rev[0] < cl_count:
1013 if rev[0] < cl_count:
1018 yield rev
1014 yield rev
1019 def iterfiles():
1015 def iterfiles():
1020 for filename in m.files():
1016 for filename in m.files():
1021 yield filename, None
1017 yield filename, None
1022 for filename_node in copies:
1018 for filename_node in copies:
1023 yield filename_node
1019 yield filename_node
1024 minrev, maxrev = min(revs), max(revs)
1020 minrev, maxrev = min(revs), max(revs)
1025 for file_, node in iterfiles():
1021 for file_, node in iterfiles():
1026 filelog = repo.file(file_)
1022 filelog = repo.file(file_)
1027 if filelog.count() == 0:
1023 if filelog.count() == 0:
1028 if node is None:
1024 if node is None:
1029 # A zero count may be a directory or deleted file, so
1025 # A zero count may be a directory or deleted file, so
1030 # try to find matching entries on the slow path.
1026 # try to find matching entries on the slow path.
1031 slowpath = True
1027 slowpath = True
1032 break
1028 break
1033 else:
1029 else:
1034 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1030 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1035 % (file_, short(node)))
1031 % (file_, short(node)))
1036 continue
1032 continue
1037 for rev, copied in filerevgen(filelog, node):
1033 for rev, copied in filerevgen(filelog, node):
1038 if rev <= maxrev:
1034 if rev <= maxrev:
1039 if rev < minrev:
1035 if rev < minrev:
1040 break
1036 break
1041 fncache.setdefault(rev, [])
1037 fncache.setdefault(rev, [])
1042 fncache[rev].append(file_)
1038 fncache[rev].append(file_)
1043 wanted[rev] = 1
1039 wanted[rev] = 1
1044 if follow and copied:
1040 if follow and copied:
1045 copies.append(copied)
1041 copies.append(copied)
1046 if slowpath:
1042 if slowpath:
1047 if follow:
1043 if follow:
1048 raise util.Abort(_('can only follow copies/renames for explicit '
1044 raise util.Abort(_('can only follow copies/renames for explicit '
1049 'file names'))
1045 'file names'))
1050
1046
1051 # The slow path checks files modified in every changeset.
1047 # The slow path checks files modified in every changeset.
1052 def changerevgen():
1048 def changerevgen():
1053 for i, window in increasing_windows(repo.changelog.count()-1,
1049 for i, window in increasing_windows(repo.changelog.count()-1,
1054 nullrev):
1050 nullrev):
1055 for j in xrange(i - window, i + 1):
1051 for j in xrange(i - window, i + 1):
1056 yield j, change(j)[3]
1052 yield j, change(j)[3]
1057
1053
1058 for rev, changefiles in changerevgen():
1054 for rev, changefiles in changerevgen():
1059 matches = filter(m, changefiles)
1055 matches = filter(m, changefiles)
1060 if matches:
1056 if matches:
1061 fncache[rev] = matches
1057 fncache[rev] = matches
1062 wanted[rev] = 1
1058 wanted[rev] = 1
1063
1059
1064 class followfilter:
1060 class followfilter:
1065 def __init__(self, onlyfirst=False):
1061 def __init__(self, onlyfirst=False):
1066 self.startrev = nullrev
1062 self.startrev = nullrev
1067 self.roots = []
1063 self.roots = []
1068 self.onlyfirst = onlyfirst
1064 self.onlyfirst = onlyfirst
1069
1065
1070 def match(self, rev):
1066 def match(self, rev):
1071 def realparents(rev):
1067 def realparents(rev):
1072 if self.onlyfirst:
1068 if self.onlyfirst:
1073 return repo.changelog.parentrevs(rev)[0:1]
1069 return repo.changelog.parentrevs(rev)[0:1]
1074 else:
1070 else:
1075 return filter(lambda x: x != nullrev,
1071 return filter(lambda x: x != nullrev,
1076 repo.changelog.parentrevs(rev))
1072 repo.changelog.parentrevs(rev))
1077
1073
1078 if self.startrev == nullrev:
1074 if self.startrev == nullrev:
1079 self.startrev = rev
1075 self.startrev = rev
1080 return True
1076 return True
1081
1077
1082 if rev > self.startrev:
1078 if rev > self.startrev:
1083 # forward: all descendants
1079 # forward: all descendants
1084 if not self.roots:
1080 if not self.roots:
1085 self.roots.append(self.startrev)
1081 self.roots.append(self.startrev)
1086 for parent in realparents(rev):
1082 for parent in realparents(rev):
1087 if parent in self.roots:
1083 if parent in self.roots:
1088 self.roots.append(rev)
1084 self.roots.append(rev)
1089 return True
1085 return True
1090 else:
1086 else:
1091 # backwards: all parents
1087 # backwards: all parents
1092 if not self.roots:
1088 if not self.roots:
1093 self.roots.extend(realparents(self.startrev))
1089 self.roots.extend(realparents(self.startrev))
1094 if rev in self.roots:
1090 if rev in self.roots:
1095 self.roots.remove(rev)
1091 self.roots.remove(rev)
1096 self.roots.extend(realparents(rev))
1092 self.roots.extend(realparents(rev))
1097 return True
1093 return True
1098
1094
1099 return False
1095 return False
1100
1096
1101 # it might be worthwhile to do this in the iterator if the rev range
1097 # it might be worthwhile to do this in the iterator if the rev range
1102 # is descending and the prune args are all within that range
1098 # is descending and the prune args are all within that range
1103 for rev in opts.get('prune', ()):
1099 for rev in opts.get('prune', ()):
1104 rev = repo.changelog.rev(repo.lookup(rev))
1100 rev = repo.changelog.rev(repo.lookup(rev))
1105 ff = followfilter()
1101 ff = followfilter()
1106 stop = min(revs[0], revs[-1])
1102 stop = min(revs[0], revs[-1])
1107 for x in xrange(rev, stop-1, -1):
1103 for x in xrange(rev, stop-1, -1):
1108 if ff.match(x) and x in wanted:
1104 if ff.match(x) and x in wanted:
1109 del wanted[x]
1105 del wanted[x]
1110
1106
1111 def iterate():
1107 def iterate():
1112 if follow and not m.files():
1108 if follow and not m.files():
1113 ff = followfilter(onlyfirst=opts.get('follow_first'))
1109 ff = followfilter(onlyfirst=opts.get('follow_first'))
1114 def want(rev):
1110 def want(rev):
1115 if ff.match(rev) and rev in wanted:
1111 if ff.match(rev) and rev in wanted:
1116 return True
1112 return True
1117 return False
1113 return False
1118 else:
1114 else:
1119 def want(rev):
1115 def want(rev):
1120 return rev in wanted
1116 return rev in wanted
1121
1117
1122 for i, window in increasing_windows(0, len(revs)):
1118 for i, window in increasing_windows(0, len(revs)):
1123 yield 'window', revs[0] < revs[-1], revs[-1]
1119 yield 'window', revs[0] < revs[-1], revs[-1]
1124 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1120 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1125 srevs = list(nrevs)
1121 srevs = list(nrevs)
1126 srevs.sort()
1122 srevs.sort()
1127 for rev in srevs:
1123 for rev in srevs:
1128 fns = fncache.get(rev)
1124 fns = fncache.get(rev)
1129 if not fns:
1125 if not fns:
1130 def fns_generator():
1126 def fns_generator():
1131 for f in change(rev)[3]:
1127 for f in change(rev)[3]:
1132 if m(f):
1128 if m(f):
1133 yield f
1129 yield f
1134 fns = fns_generator()
1130 fns = fns_generator()
1135 yield 'add', rev, fns
1131 yield 'add', rev, fns
1136 for rev in nrevs:
1132 for rev in nrevs:
1137 yield 'iter', rev, None
1133 yield 'iter', rev, None
1138 return iterate(), m
1134 return iterate(), m
1139
1135
1140 def commit(ui, repo, commitfunc, pats, opts):
1136 def commit(ui, repo, commitfunc, pats, opts):
1141 '''commit the specified files or all outstanding changes'''
1137 '''commit the specified files or all outstanding changes'''
1142 date = opts.get('date')
1138 date = opts.get('date')
1143 if date:
1139 if date:
1144 opts['date'] = util.parsedate(date)
1140 opts['date'] = util.parsedate(date)
1145 message = logmessage(opts)
1141 message = logmessage(opts)
1146
1142
1147 # extract addremove carefully -- this function can be called from a command
1143 # extract addremove carefully -- this function can be called from a command
1148 # that doesn't support addremove
1144 # that doesn't support addremove
1149 if opts.get('addremove'):
1145 if opts.get('addremove'):
1150 addremove(repo, pats, opts)
1146 addremove(repo, pats, opts)
1151
1147
1152 m = match(repo, pats, opts)
1148 m = match(repo, pats, opts)
1153 if pats:
1149 if pats:
1154 status = repo.status(files=m.files(), match=m)
1150 status = repo.status(files=m.files(), match=m)
1155 modified, added, removed, deleted, unknown = status[:5]
1151 modified, added, removed, deleted, unknown = status[:5]
1156 files = modified + added + removed
1152 files = modified + added + removed
1157 slist = None
1153 slist = None
1158 for f in m.files():
1154 for f in m.files():
1159 if f == '.':
1155 if f == '.':
1160 continue
1156 continue
1161 if f not in files:
1157 if f not in files:
1162 rf = repo.wjoin(f)
1158 rf = repo.wjoin(f)
1163 rel = repo.pathto(f)
1159 rel = repo.pathto(f)
1164 try:
1160 try:
1165 mode = os.lstat(rf)[stat.ST_MODE]
1161 mode = os.lstat(rf)[stat.ST_MODE]
1166 except OSError:
1162 except OSError:
1167 raise util.Abort(_("file %s not found!") % rel)
1163 raise util.Abort(_("file %s not found!") % rel)
1168 if stat.S_ISDIR(mode):
1164 if stat.S_ISDIR(mode):
1169 name = f + '/'
1165 name = f + '/'
1170 if slist is None:
1166 if slist is None:
1171 slist = list(files)
1167 slist = list(files)
1172 slist.sort()
1168 slist.sort()
1173 i = bisect.bisect(slist, name)
1169 i = bisect.bisect(slist, name)
1174 if i >= len(slist) or not slist[i].startswith(name):
1170 if i >= len(slist) or not slist[i].startswith(name):
1175 raise util.Abort(_("no match under directory %s!")
1171 raise util.Abort(_("no match under directory %s!")
1176 % rel)
1172 % rel)
1177 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1173 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1178 raise util.Abort(_("can't commit %s: "
1174 raise util.Abort(_("can't commit %s: "
1179 "unsupported file type!") % rel)
1175 "unsupported file type!") % rel)
1180 elif f not in repo.dirstate:
1176 elif f not in repo.dirstate:
1181 raise util.Abort(_("file %s not tracked!") % rel)
1177 raise util.Abort(_("file %s not tracked!") % rel)
1182 else:
1178 else:
1183 files = []
1179 files = []
1184 try:
1180 try:
1185 return commitfunc(ui, repo, files, message, m, opts)
1181 return commitfunc(ui, repo, files, message, m, opts)
1186 except ValueError, inst:
1182 except ValueError, inst:
1187 raise util.Abort(str(inst))
1183 raise util.Abort(str(inst))
@@ -1,3327 +1,3327
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from repo import RepoError, NoCapability
9 from repo import RepoError, NoCapability
10 from i18n import _
10 from i18n import _
11 import os, re, sys, urllib
11 import os, re, sys, urllib
12 import hg, util, revlog, bundlerepo, extensions, copies
12 import hg, util, revlog, bundlerepo, extensions, copies
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import version, socket
14 import version, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
16 import merge as merge_
16 import merge as merge_
17
17
18 # Commands start here, listed alphabetically
18 # Commands start here, listed alphabetically
19
19
20 def add(ui, repo, *pats, **opts):
20 def add(ui, repo, *pats, **opts):
21 """add the specified files on the next commit
21 """add the specified files on the next commit
22
22
23 Schedule files to be version controlled and added to the repository.
23 Schedule files to be version controlled and added to the repository.
24
24
25 The files will be added to the repository at the next commit. To
25 The files will be added to the repository at the next commit. To
26 undo an add before that, see hg revert.
26 undo an add before that, see hg revert.
27
27
28 If no names are given, add all files in the repository.
28 If no names are given, add all files in the repository.
29 """
29 """
30
30
31 rejected = None
31 rejected = None
32 exacts = {}
32 exacts = {}
33 names = []
33 names = []
34 m = cmdutil.match(repo, pats, opts)
34 m = cmdutil.match(repo, pats, opts)
35 m.bad = lambda x,y: True
35 m.bad = lambda x,y: True
36 for src, abs in cmdutil.walk(repo, m):
36 for src, abs in repo.walk(m):
37 if m.exact(abs):
37 if m.exact(abs):
38 if ui.verbose:
38 if ui.verbose:
39 ui.status(_('adding %s\n') % m.rel(abs))
39 ui.status(_('adding %s\n') % m.rel(abs))
40 names.append(abs)
40 names.append(abs)
41 exacts[abs] = 1
41 exacts[abs] = 1
42 elif abs not in repo.dirstate:
42 elif abs not in repo.dirstate:
43 ui.status(_('adding %s\n') % m.rel(abs))
43 ui.status(_('adding %s\n') % m.rel(abs))
44 names.append(abs)
44 names.append(abs)
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 rejected = repo.add(names)
46 rejected = repo.add(names)
47 rejected = [p for p in rejected if p in exacts]
47 rejected = [p for p in rejected if p in exacts]
48 return rejected and 1 or 0
48 return rejected and 1 or 0
49
49
50 def addremove(ui, repo, *pats, **opts):
50 def addremove(ui, repo, *pats, **opts):
51 """add all new files, delete all missing files
51 """add all new files, delete all missing files
52
52
53 Add all new files and remove all missing files from the repository.
53 Add all new files and remove all missing files from the repository.
54
54
55 New files are ignored if they match any of the patterns in .hgignore. As
55 New files are ignored if they match any of the patterns in .hgignore. As
56 with add, these changes take effect at the next commit.
56 with add, these changes take effect at the next commit.
57
57
58 Use the -s option to detect renamed files. With a parameter > 0,
58 Use the -s option to detect renamed files. With a parameter > 0,
59 this compares every removed file with every added file and records
59 this compares every removed file with every added file and records
60 those similar enough as renames. This option takes a percentage
60 those similar enough as renames. This option takes a percentage
61 between 0 (disabled) and 100 (files must be identical) as its
61 between 0 (disabled) and 100 (files must be identical) as its
62 parameter. Detecting renamed files this way can be expensive.
62 parameter. Detecting renamed files this way can be expensive.
63 """
63 """
64 try:
64 try:
65 sim = float(opts.get('similarity') or 0)
65 sim = float(opts.get('similarity') or 0)
66 except ValueError:
66 except ValueError:
67 raise util.Abort(_('similarity must be a number'))
67 raise util.Abort(_('similarity must be a number'))
68 if sim < 0 or sim > 100:
68 if sim < 0 or sim > 100:
69 raise util.Abort(_('similarity must be between 0 and 100'))
69 raise util.Abort(_('similarity must be between 0 and 100'))
70 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
70 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
71
71
72 def annotate(ui, repo, *pats, **opts):
72 def annotate(ui, repo, *pats, **opts):
73 """show changeset information per file line
73 """show changeset information per file line
74
74
75 List changes in files, showing the revision id responsible for each line
75 List changes in files, showing the revision id responsible for each line
76
76
77 This command is useful to discover who did a change or when a change took
77 This command is useful to discover who did a change or when a change took
78 place.
78 place.
79
79
80 Without the -a option, annotate will avoid processing files it
80 Without the -a option, annotate will avoid processing files it
81 detects as binary. With -a, annotate will generate an annotation
81 detects as binary. With -a, annotate will generate an annotation
82 anyway, probably with undesirable results.
82 anyway, probably with undesirable results.
83 """
83 """
84 datefunc = ui.quiet and util.shortdate or util.datestr
84 datefunc = ui.quiet and util.shortdate or util.datestr
85 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
85 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
86
86
87 if not pats:
87 if not pats:
88 raise util.Abort(_('at least one file name or pattern required'))
88 raise util.Abort(_('at least one file name or pattern required'))
89
89
90 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
90 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
91 ('number', lambda x: str(x[0].rev())),
91 ('number', lambda x: str(x[0].rev())),
92 ('changeset', lambda x: short(x[0].node())),
92 ('changeset', lambda x: short(x[0].node())),
93 ('date', getdate),
93 ('date', getdate),
94 ('follow', lambda x: x[0].path()),
94 ('follow', lambda x: x[0].path()),
95 ]
95 ]
96
96
97 if (not opts['user'] and not opts['changeset'] and not opts['date']
97 if (not opts['user'] and not opts['changeset'] and not opts['date']
98 and not opts['follow']):
98 and not opts['follow']):
99 opts['number'] = 1
99 opts['number'] = 1
100
100
101 linenumber = opts.get('line_number') is not None
101 linenumber = opts.get('line_number') is not None
102 if (linenumber and (not opts['changeset']) and (not opts['number'])):
102 if (linenumber and (not opts['changeset']) and (not opts['number'])):
103 raise util.Abort(_('at least one of -n/-c is required for -l'))
103 raise util.Abort(_('at least one of -n/-c is required for -l'))
104
104
105 funcmap = [func for op, func in opmap if opts.get(op)]
105 funcmap = [func for op, func in opmap if opts.get(op)]
106 if linenumber:
106 if linenumber:
107 lastfunc = funcmap[-1]
107 lastfunc = funcmap[-1]
108 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
108 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
109
109
110 ctx = repo.changectx(opts['rev'])
110 ctx = repo.changectx(opts['rev'])
111
111
112 m = cmdutil.match(repo, pats, opts)
112 m = cmdutil.match(repo, pats, opts)
113 for src, abs in cmdutil.walk(repo, m, ctx.node()):
113 for src, abs in repo.walk(m, ctx.node()):
114 fctx = ctx.filectx(abs)
114 fctx = ctx.filectx(abs)
115 if not opts['text'] and util.binary(fctx.data()):
115 if not opts['text'] and util.binary(fctx.data()):
116 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
116 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
117 continue
117 continue
118
118
119 lines = fctx.annotate(follow=opts.get('follow'),
119 lines = fctx.annotate(follow=opts.get('follow'),
120 linenumber=linenumber)
120 linenumber=linenumber)
121 pieces = []
121 pieces = []
122
122
123 for f in funcmap:
123 for f in funcmap:
124 l = [f(n) for n, dummy in lines]
124 l = [f(n) for n, dummy in lines]
125 if l:
125 if l:
126 m = max(map(len, l))
126 m = max(map(len, l))
127 pieces.append(["%*s" % (m, x) for x in l])
127 pieces.append(["%*s" % (m, x) for x in l])
128
128
129 if pieces:
129 if pieces:
130 for p, l in zip(zip(*pieces), lines):
130 for p, l in zip(zip(*pieces), lines):
131 ui.write("%s: %s" % (" ".join(p), l[1]))
131 ui.write("%s: %s" % (" ".join(p), l[1]))
132
132
133 def archive(ui, repo, dest, **opts):
133 def archive(ui, repo, dest, **opts):
134 '''create unversioned archive of a repository revision
134 '''create unversioned archive of a repository revision
135
135
136 By default, the revision used is the parent of the working
136 By default, the revision used is the parent of the working
137 directory; use "-r" to specify a different revision.
137 directory; use "-r" to specify a different revision.
138
138
139 To specify the type of archive to create, use "-t". Valid
139 To specify the type of archive to create, use "-t". Valid
140 types are:
140 types are:
141
141
142 "files" (default): a directory full of files
142 "files" (default): a directory full of files
143 "tar": tar archive, uncompressed
143 "tar": tar archive, uncompressed
144 "tbz2": tar archive, compressed using bzip2
144 "tbz2": tar archive, compressed using bzip2
145 "tgz": tar archive, compressed using gzip
145 "tgz": tar archive, compressed using gzip
146 "uzip": zip archive, uncompressed
146 "uzip": zip archive, uncompressed
147 "zip": zip archive, compressed using deflate
147 "zip": zip archive, compressed using deflate
148
148
149 The exact name of the destination archive or directory is given
149 The exact name of the destination archive or directory is given
150 using a format string; see "hg help export" for details.
150 using a format string; see "hg help export" for details.
151
151
152 Each member added to an archive file has a directory prefix
152 Each member added to an archive file has a directory prefix
153 prepended. Use "-p" to specify a format string for the prefix.
153 prepended. Use "-p" to specify a format string for the prefix.
154 The default is the basename of the archive, with suffixes removed.
154 The default is the basename of the archive, with suffixes removed.
155 '''
155 '''
156
156
157 ctx = repo.changectx(opts['rev'])
157 ctx = repo.changectx(opts['rev'])
158 if not ctx:
158 if not ctx:
159 raise util.Abort(_('repository has no revisions'))
159 raise util.Abort(_('repository has no revisions'))
160 node = ctx.node()
160 node = ctx.node()
161 dest = cmdutil.make_filename(repo, dest, node)
161 dest = cmdutil.make_filename(repo, dest, node)
162 if os.path.realpath(dest) == repo.root:
162 if os.path.realpath(dest) == repo.root:
163 raise util.Abort(_('repository root cannot be destination'))
163 raise util.Abort(_('repository root cannot be destination'))
164 matchfn = cmdutil.match(repo, [], opts)
164 matchfn = cmdutil.match(repo, [], opts)
165 kind = opts.get('type') or 'files'
165 kind = opts.get('type') or 'files'
166 prefix = opts['prefix']
166 prefix = opts['prefix']
167 if dest == '-':
167 if dest == '-':
168 if kind == 'files':
168 if kind == 'files':
169 raise util.Abort(_('cannot archive plain files to stdout'))
169 raise util.Abort(_('cannot archive plain files to stdout'))
170 dest = sys.stdout
170 dest = sys.stdout
171 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
171 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
172 prefix = cmdutil.make_filename(repo, prefix, node)
172 prefix = cmdutil.make_filename(repo, prefix, node)
173 archival.archive(repo, dest, node, kind, not opts['no_decode'],
173 archival.archive(repo, dest, node, kind, not opts['no_decode'],
174 matchfn, prefix)
174 matchfn, prefix)
175
175
176 def backout(ui, repo, node=None, rev=None, **opts):
176 def backout(ui, repo, node=None, rev=None, **opts):
177 '''reverse effect of earlier changeset
177 '''reverse effect of earlier changeset
178
178
179 Commit the backed out changes as a new changeset. The new
179 Commit the backed out changes as a new changeset. The new
180 changeset is a child of the backed out changeset.
180 changeset is a child of the backed out changeset.
181
181
182 If you back out a changeset other than the tip, a new head is
182 If you back out a changeset other than the tip, a new head is
183 created. This head will be the new tip and you should merge this
183 created. This head will be the new tip and you should merge this
184 backout changeset with another head (current one by default).
184 backout changeset with another head (current one by default).
185
185
186 The --merge option remembers the parent of the working directory
186 The --merge option remembers the parent of the working directory
187 before starting the backout, then merges the new head with that
187 before starting the backout, then merges the new head with that
188 changeset afterwards. This saves you from doing the merge by
188 changeset afterwards. This saves you from doing the merge by
189 hand. The result of this merge is not committed, as for a normal
189 hand. The result of this merge is not committed, as for a normal
190 merge.
190 merge.
191
191
192 See 'hg help dates' for a list of formats valid for -d/--date.
192 See 'hg help dates' for a list of formats valid for -d/--date.
193 '''
193 '''
194 if rev and node:
194 if rev and node:
195 raise util.Abort(_("please specify just one revision"))
195 raise util.Abort(_("please specify just one revision"))
196
196
197 if not rev:
197 if not rev:
198 rev = node
198 rev = node
199
199
200 if not rev:
200 if not rev:
201 raise util.Abort(_("please specify a revision to backout"))
201 raise util.Abort(_("please specify a revision to backout"))
202
202
203 date = opts.get('date')
203 date = opts.get('date')
204 if date:
204 if date:
205 opts['date'] = util.parsedate(date)
205 opts['date'] = util.parsedate(date)
206
206
207 cmdutil.bail_if_changed(repo)
207 cmdutil.bail_if_changed(repo)
208 node = repo.lookup(rev)
208 node = repo.lookup(rev)
209
209
210 op1, op2 = repo.dirstate.parents()
210 op1, op2 = repo.dirstate.parents()
211 a = repo.changelog.ancestor(op1, node)
211 a = repo.changelog.ancestor(op1, node)
212 if a != node:
212 if a != node:
213 raise util.Abort(_('cannot back out change on a different branch'))
213 raise util.Abort(_('cannot back out change on a different branch'))
214
214
215 p1, p2 = repo.changelog.parents(node)
215 p1, p2 = repo.changelog.parents(node)
216 if p1 == nullid:
216 if p1 == nullid:
217 raise util.Abort(_('cannot back out a change with no parents'))
217 raise util.Abort(_('cannot back out a change with no parents'))
218 if p2 != nullid:
218 if p2 != nullid:
219 if not opts['parent']:
219 if not opts['parent']:
220 raise util.Abort(_('cannot back out a merge changeset without '
220 raise util.Abort(_('cannot back out a merge changeset without '
221 '--parent'))
221 '--parent'))
222 p = repo.lookup(opts['parent'])
222 p = repo.lookup(opts['parent'])
223 if p not in (p1, p2):
223 if p not in (p1, p2):
224 raise util.Abort(_('%s is not a parent of %s') %
224 raise util.Abort(_('%s is not a parent of %s') %
225 (short(p), short(node)))
225 (short(p), short(node)))
226 parent = p
226 parent = p
227 else:
227 else:
228 if opts['parent']:
228 if opts['parent']:
229 raise util.Abort(_('cannot use --parent on non-merge changeset'))
229 raise util.Abort(_('cannot use --parent on non-merge changeset'))
230 parent = p1
230 parent = p1
231
231
232 # the backout should appear on the same branch
232 # the backout should appear on the same branch
233 branch = repo.dirstate.branch()
233 branch = repo.dirstate.branch()
234 hg.clean(repo, node, show_stats=False)
234 hg.clean(repo, node, show_stats=False)
235 repo.dirstate.setbranch(branch)
235 repo.dirstate.setbranch(branch)
236 revert_opts = opts.copy()
236 revert_opts = opts.copy()
237 revert_opts['date'] = None
237 revert_opts['date'] = None
238 revert_opts['all'] = True
238 revert_opts['all'] = True
239 revert_opts['rev'] = hex(parent)
239 revert_opts['rev'] = hex(parent)
240 revert_opts['no_backup'] = None
240 revert_opts['no_backup'] = None
241 revert(ui, repo, **revert_opts)
241 revert(ui, repo, **revert_opts)
242 commit_opts = opts.copy()
242 commit_opts = opts.copy()
243 commit_opts['addremove'] = False
243 commit_opts['addremove'] = False
244 if not commit_opts['message'] and not commit_opts['logfile']:
244 if not commit_opts['message'] and not commit_opts['logfile']:
245 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
245 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
246 commit_opts['force_editor'] = True
246 commit_opts['force_editor'] = True
247 commit(ui, repo, **commit_opts)
247 commit(ui, repo, **commit_opts)
248 def nice(node):
248 def nice(node):
249 return '%d:%s' % (repo.changelog.rev(node), short(node))
249 return '%d:%s' % (repo.changelog.rev(node), short(node))
250 ui.status(_('changeset %s backs out changeset %s\n') %
250 ui.status(_('changeset %s backs out changeset %s\n') %
251 (nice(repo.changelog.tip()), nice(node)))
251 (nice(repo.changelog.tip()), nice(node)))
252 if op1 != node:
252 if op1 != node:
253 hg.clean(repo, op1, show_stats=False)
253 hg.clean(repo, op1, show_stats=False)
254 if opts['merge']:
254 if opts['merge']:
255 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
255 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
256 hg.merge(repo, hex(repo.changelog.tip()))
256 hg.merge(repo, hex(repo.changelog.tip()))
257 else:
257 else:
258 ui.status(_('the backout changeset is a new head - '
258 ui.status(_('the backout changeset is a new head - '
259 'do not forget to merge\n'))
259 'do not forget to merge\n'))
260 ui.status(_('(use "backout --merge" '
260 ui.status(_('(use "backout --merge" '
261 'if you want to auto-merge)\n'))
261 'if you want to auto-merge)\n'))
262
262
263 def bisect(ui, repo, rev=None, extra=None,
263 def bisect(ui, repo, rev=None, extra=None,
264 reset=None, good=None, bad=None, skip=None, noupdate=None):
264 reset=None, good=None, bad=None, skip=None, noupdate=None):
265 """subdivision search of changesets
265 """subdivision search of changesets
266
266
267 This command helps to find changesets which introduce problems.
267 This command helps to find changesets which introduce problems.
268 To use, mark the earliest changeset you know exhibits the problem
268 To use, mark the earliest changeset you know exhibits the problem
269 as bad, then mark the latest changeset which is free from the
269 as bad, then mark the latest changeset which is free from the
270 problem as good. Bisect will update your working directory to a
270 problem as good. Bisect will update your working directory to a
271 revision for testing. Once you have performed tests, mark the
271 revision for testing. Once you have performed tests, mark the
272 working directory as bad or good and bisect will either update to
272 working directory as bad or good and bisect will either update to
273 another candidate changeset or announce that it has found the bad
273 another candidate changeset or announce that it has found the bad
274 revision.
274 revision.
275 """
275 """
276 # backward compatibility
276 # backward compatibility
277 if rev in "good bad reset init".split():
277 if rev in "good bad reset init".split():
278 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
278 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
279 cmd, rev, extra = rev, extra, None
279 cmd, rev, extra = rev, extra, None
280 if cmd == "good":
280 if cmd == "good":
281 good = True
281 good = True
282 elif cmd == "bad":
282 elif cmd == "bad":
283 bad = True
283 bad = True
284 else:
284 else:
285 reset = True
285 reset = True
286 elif extra or good + bad + skip + reset > 1:
286 elif extra or good + bad + skip + reset > 1:
287 raise util.Abort("Incompatible arguments")
287 raise util.Abort("Incompatible arguments")
288
288
289 if reset:
289 if reset:
290 p = repo.join("bisect.state")
290 p = repo.join("bisect.state")
291 if os.path.exists(p):
291 if os.path.exists(p):
292 os.unlink(p)
292 os.unlink(p)
293 return
293 return
294
294
295 # load state
295 # load state
296 state = {'good': [], 'bad': [], 'skip': []}
296 state = {'good': [], 'bad': [], 'skip': []}
297 if os.path.exists(repo.join("bisect.state")):
297 if os.path.exists(repo.join("bisect.state")):
298 for l in repo.opener("bisect.state"):
298 for l in repo.opener("bisect.state"):
299 kind, node = l[:-1].split()
299 kind, node = l[:-1].split()
300 node = repo.lookup(node)
300 node = repo.lookup(node)
301 if kind not in state:
301 if kind not in state:
302 raise util.Abort(_("unknown bisect kind %s") % kind)
302 raise util.Abort(_("unknown bisect kind %s") % kind)
303 state[kind].append(node)
303 state[kind].append(node)
304
304
305 # update state
305 # update state
306 node = repo.lookup(rev or '.')
306 node = repo.lookup(rev or '.')
307 if good:
307 if good:
308 state['good'].append(node)
308 state['good'].append(node)
309 elif bad:
309 elif bad:
310 state['bad'].append(node)
310 state['bad'].append(node)
311 elif skip:
311 elif skip:
312 state['skip'].append(node)
312 state['skip'].append(node)
313
313
314 # save state
314 # save state
315 f = repo.opener("bisect.state", "w", atomictemp=True)
315 f = repo.opener("bisect.state", "w", atomictemp=True)
316 wlock = repo.wlock()
316 wlock = repo.wlock()
317 try:
317 try:
318 for kind in state:
318 for kind in state:
319 for node in state[kind]:
319 for node in state[kind]:
320 f.write("%s %s\n" % (kind, hex(node)))
320 f.write("%s %s\n" % (kind, hex(node)))
321 f.rename()
321 f.rename()
322 finally:
322 finally:
323 del wlock
323 del wlock
324
324
325 if not state['good'] or not state['bad']:
325 if not state['good'] or not state['bad']:
326 return
326 return
327
327
328 # actually bisect
328 # actually bisect
329 node, changesets, good = hbisect.bisect(repo.changelog, state)
329 node, changesets, good = hbisect.bisect(repo.changelog, state)
330 if changesets == 0:
330 if changesets == 0:
331 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
331 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
332 displayer = cmdutil.show_changeset(ui, repo, {})
332 displayer = cmdutil.show_changeset(ui, repo, {})
333 displayer.show(changenode=node)
333 displayer.show(changenode=node)
334 elif node is not None:
334 elif node is not None:
335 # compute the approximate number of remaining tests
335 # compute the approximate number of remaining tests
336 tests, size = 0, 2
336 tests, size = 0, 2
337 while size <= changesets:
337 while size <= changesets:
338 tests, size = tests + 1, size * 2
338 tests, size = tests + 1, size * 2
339 rev = repo.changelog.rev(node)
339 rev = repo.changelog.rev(node)
340 ui.write(_("Testing changeset %s:%s "
340 ui.write(_("Testing changeset %s:%s "
341 "(%s changesets remaining, ~%s tests)\n")
341 "(%s changesets remaining, ~%s tests)\n")
342 % (rev, short(node), changesets, tests))
342 % (rev, short(node), changesets, tests))
343 if not noupdate:
343 if not noupdate:
344 cmdutil.bail_if_changed(repo)
344 cmdutil.bail_if_changed(repo)
345 return hg.clean(repo, node)
345 return hg.clean(repo, node)
346
346
347 def branch(ui, repo, label=None, **opts):
347 def branch(ui, repo, label=None, **opts):
348 """set or show the current branch name
348 """set or show the current branch name
349
349
350 With no argument, show the current branch name. With one argument,
350 With no argument, show the current branch name. With one argument,
351 set the working directory branch name (the branch does not exist in
351 set the working directory branch name (the branch does not exist in
352 the repository until the next commit).
352 the repository until the next commit).
353
353
354 Unless --force is specified, branch will not let you set a
354 Unless --force is specified, branch will not let you set a
355 branch name that shadows an existing branch.
355 branch name that shadows an existing branch.
356
356
357 Use the command 'hg update' to switch to an existing branch.
357 Use the command 'hg update' to switch to an existing branch.
358 """
358 """
359
359
360 if label:
360 if label:
361 if not opts.get('force') and label in repo.branchtags():
361 if not opts.get('force') and label in repo.branchtags():
362 if label not in [p.branch() for p in repo.workingctx().parents()]:
362 if label not in [p.branch() for p in repo.workingctx().parents()]:
363 raise util.Abort(_('a branch of the same name already exists'
363 raise util.Abort(_('a branch of the same name already exists'
364 ' (use --force to override)'))
364 ' (use --force to override)'))
365 repo.dirstate.setbranch(util.fromlocal(label))
365 repo.dirstate.setbranch(util.fromlocal(label))
366 ui.status(_('marked working directory as branch %s\n') % label)
366 ui.status(_('marked working directory as branch %s\n') % label)
367 else:
367 else:
368 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
368 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
369
369
370 def branches(ui, repo, active=False):
370 def branches(ui, repo, active=False):
371 """list repository named branches
371 """list repository named branches
372
372
373 List the repository's named branches, indicating which ones are
373 List the repository's named branches, indicating which ones are
374 inactive. If active is specified, only show active branches.
374 inactive. If active is specified, only show active branches.
375
375
376 A branch is considered active if it contains unmerged heads.
376 A branch is considered active if it contains unmerged heads.
377
377
378 Use the command 'hg update' to switch to an existing branch.
378 Use the command 'hg update' to switch to an existing branch.
379 """
379 """
380 b = repo.branchtags()
380 b = repo.branchtags()
381 heads = dict.fromkeys(repo.heads(), 1)
381 heads = dict.fromkeys(repo.heads(), 1)
382 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
382 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
383 l.sort()
383 l.sort()
384 l.reverse()
384 l.reverse()
385 for ishead, r, n, t in l:
385 for ishead, r, n, t in l:
386 if active and not ishead:
386 if active and not ishead:
387 # If we're only displaying active branches, abort the loop on
387 # If we're only displaying active branches, abort the loop on
388 # encountering the first inactive head
388 # encountering the first inactive head
389 break
389 break
390 else:
390 else:
391 hexfunc = ui.debugflag and hex or short
391 hexfunc = ui.debugflag and hex or short
392 if ui.quiet:
392 if ui.quiet:
393 ui.write("%s\n" % t)
393 ui.write("%s\n" % t)
394 else:
394 else:
395 spaces = " " * (30 - util.locallen(t))
395 spaces = " " * (30 - util.locallen(t))
396 # The code only gets here if inactive branches are being
396 # The code only gets here if inactive branches are being
397 # displayed or the branch is active.
397 # displayed or the branch is active.
398 isinactive = ((not ishead) and " (inactive)") or ''
398 isinactive = ((not ishead) and " (inactive)") or ''
399 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
399 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
400
400
401 def bundle(ui, repo, fname, dest=None, **opts):
401 def bundle(ui, repo, fname, dest=None, **opts):
402 """create a changegroup file
402 """create a changegroup file
403
403
404 Generate a compressed changegroup file collecting changesets not
404 Generate a compressed changegroup file collecting changesets not
405 found in the other repository.
405 found in the other repository.
406
406
407 If no destination repository is specified the destination is
407 If no destination repository is specified the destination is
408 assumed to have all the nodes specified by one or more --base
408 assumed to have all the nodes specified by one or more --base
409 parameters. To create a bundle containing all changesets, use
409 parameters. To create a bundle containing all changesets, use
410 --all (or --base null). To change the compression method applied,
410 --all (or --base null). To change the compression method applied,
411 use the -t option (by default, bundles are compressed using bz2).
411 use the -t option (by default, bundles are compressed using bz2).
412
412
413 The bundle file can then be transferred using conventional means and
413 The bundle file can then be transferred using conventional means and
414 applied to another repository with the unbundle or pull command.
414 applied to another repository with the unbundle or pull command.
415 This is useful when direct push and pull are not available or when
415 This is useful when direct push and pull are not available or when
416 exporting an entire repository is undesirable.
416 exporting an entire repository is undesirable.
417
417
418 Applying bundles preserves all changeset contents including
418 Applying bundles preserves all changeset contents including
419 permissions, copy/rename information, and revision history.
419 permissions, copy/rename information, and revision history.
420 """
420 """
421 revs = opts.get('rev') or None
421 revs = opts.get('rev') or None
422 if revs:
422 if revs:
423 revs = [repo.lookup(rev) for rev in revs]
423 revs = [repo.lookup(rev) for rev in revs]
424 if opts.get('all'):
424 if opts.get('all'):
425 base = ['null']
425 base = ['null']
426 else:
426 else:
427 base = opts.get('base')
427 base = opts.get('base')
428 if base:
428 if base:
429 if dest:
429 if dest:
430 raise util.Abort(_("--base is incompatible with specifiying "
430 raise util.Abort(_("--base is incompatible with specifiying "
431 "a destination"))
431 "a destination"))
432 base = [repo.lookup(rev) for rev in base]
432 base = [repo.lookup(rev) for rev in base]
433 # create the right base
433 # create the right base
434 # XXX: nodesbetween / changegroup* should be "fixed" instead
434 # XXX: nodesbetween / changegroup* should be "fixed" instead
435 o = []
435 o = []
436 has = {nullid: None}
436 has = {nullid: None}
437 for n in base:
437 for n in base:
438 has.update(repo.changelog.reachable(n))
438 has.update(repo.changelog.reachable(n))
439 if revs:
439 if revs:
440 visit = list(revs)
440 visit = list(revs)
441 else:
441 else:
442 visit = repo.changelog.heads()
442 visit = repo.changelog.heads()
443 seen = {}
443 seen = {}
444 while visit:
444 while visit:
445 n = visit.pop(0)
445 n = visit.pop(0)
446 parents = [p for p in repo.changelog.parents(n) if p not in has]
446 parents = [p for p in repo.changelog.parents(n) if p not in has]
447 if len(parents) == 0:
447 if len(parents) == 0:
448 o.insert(0, n)
448 o.insert(0, n)
449 else:
449 else:
450 for p in parents:
450 for p in parents:
451 if p not in seen:
451 if p not in seen:
452 seen[p] = 1
452 seen[p] = 1
453 visit.append(p)
453 visit.append(p)
454 else:
454 else:
455 cmdutil.setremoteconfig(ui, opts)
455 cmdutil.setremoteconfig(ui, opts)
456 dest, revs, checkout = hg.parseurl(
456 dest, revs, checkout = hg.parseurl(
457 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
457 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
458 other = hg.repository(ui, dest)
458 other = hg.repository(ui, dest)
459 o = repo.findoutgoing(other, force=opts['force'])
459 o = repo.findoutgoing(other, force=opts['force'])
460
460
461 if revs:
461 if revs:
462 cg = repo.changegroupsubset(o, revs, 'bundle')
462 cg = repo.changegroupsubset(o, revs, 'bundle')
463 else:
463 else:
464 cg = repo.changegroup(o, 'bundle')
464 cg = repo.changegroup(o, 'bundle')
465
465
466 bundletype = opts.get('type', 'bzip2').lower()
466 bundletype = opts.get('type', 'bzip2').lower()
467 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
467 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
468 bundletype = btypes.get(bundletype)
468 bundletype = btypes.get(bundletype)
469 if bundletype not in changegroup.bundletypes:
469 if bundletype not in changegroup.bundletypes:
470 raise util.Abort(_('unknown bundle type specified with --type'))
470 raise util.Abort(_('unknown bundle type specified with --type'))
471
471
472 changegroup.writebundle(cg, fname, bundletype)
472 changegroup.writebundle(cg, fname, bundletype)
473
473
474 def cat(ui, repo, file1, *pats, **opts):
474 def cat(ui, repo, file1, *pats, **opts):
475 """output the current or given revision of files
475 """output the current or given revision of files
476
476
477 Print the specified files as they were at the given revision.
477 Print the specified files as they were at the given revision.
478 If no revision is given, the parent of the working directory is used,
478 If no revision is given, the parent of the working directory is used,
479 or tip if no revision is checked out.
479 or tip if no revision is checked out.
480
480
481 Output may be to a file, in which case the name of the file is
481 Output may be to a file, in which case the name of the file is
482 given using a format string. The formatting rules are the same as
482 given using a format string. The formatting rules are the same as
483 for the export command, with the following additions:
483 for the export command, with the following additions:
484
484
485 %s basename of file being printed
485 %s basename of file being printed
486 %d dirname of file being printed, or '.' if in repo root
486 %d dirname of file being printed, or '.' if in repo root
487 %p root-relative path name of file being printed
487 %p root-relative path name of file being printed
488 """
488 """
489 ctx = repo.changectx(opts['rev'])
489 ctx = repo.changectx(opts['rev'])
490 err = 1
490 err = 1
491 m = cmdutil.match(repo, (file1,) + pats, opts)
491 m = cmdutil.match(repo, (file1,) + pats, opts)
492 for src, abs in cmdutil.walk(repo, m, ctx.node()):
492 for src, abs in repo.walk(m, ctx.node()):
493 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
493 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
494 data = ctx.filectx(abs).data()
494 data = ctx.filectx(abs).data()
495 if opts.get('decode'):
495 if opts.get('decode'):
496 data = repo.wwritedata(abs, data)
496 data = repo.wwritedata(abs, data)
497 fp.write(data)
497 fp.write(data)
498 err = 0
498 err = 0
499 return err
499 return err
500
500
501 def clone(ui, source, dest=None, **opts):
501 def clone(ui, source, dest=None, **opts):
502 """make a copy of an existing repository
502 """make a copy of an existing repository
503
503
504 Create a copy of an existing repository in a new directory.
504 Create a copy of an existing repository in a new directory.
505
505
506 If no destination directory name is specified, it defaults to the
506 If no destination directory name is specified, it defaults to the
507 basename of the source.
507 basename of the source.
508
508
509 The location of the source is added to the new repository's
509 The location of the source is added to the new repository's
510 .hg/hgrc file, as the default to be used for future pulls.
510 .hg/hgrc file, as the default to be used for future pulls.
511
511
512 For efficiency, hardlinks are used for cloning whenever the source
512 For efficiency, hardlinks are used for cloning whenever the source
513 and destination are on the same filesystem (note this applies only
513 and destination are on the same filesystem (note this applies only
514 to the repository data, not to the checked out files). Some
514 to the repository data, not to the checked out files). Some
515 filesystems, such as AFS, implement hardlinking incorrectly, but
515 filesystems, such as AFS, implement hardlinking incorrectly, but
516 do not report errors. In these cases, use the --pull option to
516 do not report errors. In these cases, use the --pull option to
517 avoid hardlinking.
517 avoid hardlinking.
518
518
519 In some cases, you can clone repositories and checked out files
519 In some cases, you can clone repositories and checked out files
520 using full hardlinks with
520 using full hardlinks with
521
521
522 $ cp -al REPO REPOCLONE
522 $ cp -al REPO REPOCLONE
523
523
524 This is the fastest way to clone, but it is not always safe. The
524 This is the fastest way to clone, but it is not always safe. The
525 operation is not atomic (making sure REPO is not modified during
525 operation is not atomic (making sure REPO is not modified during
526 the operation is up to you) and you have to make sure your editor
526 the operation is up to you) and you have to make sure your editor
527 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
527 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
528 this is not compatible with certain extensions that place their
528 this is not compatible with certain extensions that place their
529 metadata under the .hg directory, such as mq.
529 metadata under the .hg directory, such as mq.
530
530
531 If you use the -r option to clone up to a specific revision, no
531 If you use the -r option to clone up to a specific revision, no
532 subsequent revisions will be present in the cloned repository.
532 subsequent revisions will be present in the cloned repository.
533 This option implies --pull, even on local repositories.
533 This option implies --pull, even on local repositories.
534
534
535 See pull for valid source format details.
535 See pull for valid source format details.
536
536
537 It is possible to specify an ssh:// URL as the destination, but no
537 It is possible to specify an ssh:// URL as the destination, but no
538 .hg/hgrc and working directory will be created on the remote side.
538 .hg/hgrc and working directory will be created on the remote side.
539 Look at the help text for the pull command for important details
539 Look at the help text for the pull command for important details
540 about ssh:// URLs.
540 about ssh:// URLs.
541 """
541 """
542 cmdutil.setremoteconfig(ui, opts)
542 cmdutil.setremoteconfig(ui, opts)
543 hg.clone(ui, source, dest,
543 hg.clone(ui, source, dest,
544 pull=opts['pull'],
544 pull=opts['pull'],
545 stream=opts['uncompressed'],
545 stream=opts['uncompressed'],
546 rev=opts['rev'],
546 rev=opts['rev'],
547 update=not opts['noupdate'])
547 update=not opts['noupdate'])
548
548
549 def commit(ui, repo, *pats, **opts):
549 def commit(ui, repo, *pats, **opts):
550 """commit the specified files or all outstanding changes
550 """commit the specified files or all outstanding changes
551
551
552 Commit changes to the given files into the repository.
552 Commit changes to the given files into the repository.
553
553
554 If a list of files is omitted, all changes reported by "hg status"
554 If a list of files is omitted, all changes reported by "hg status"
555 will be committed.
555 will be committed.
556
556
557 If you are committing the result of a merge, do not provide any
557 If you are committing the result of a merge, do not provide any
558 file names or -I/-X filters.
558 file names or -I/-X filters.
559
559
560 If no commit message is specified, the configured editor is started to
560 If no commit message is specified, the configured editor is started to
561 enter a message.
561 enter a message.
562
562
563 See 'hg help dates' for a list of formats valid for -d/--date.
563 See 'hg help dates' for a list of formats valid for -d/--date.
564 """
564 """
565 def commitfunc(ui, repo, files, message, match, opts):
565 def commitfunc(ui, repo, files, message, match, opts):
566 return repo.commit(files, message, opts['user'], opts['date'], match,
566 return repo.commit(files, message, opts['user'], opts['date'], match,
567 force_editor=opts.get('force_editor'))
567 force_editor=opts.get('force_editor'))
568
568
569 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
569 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
570 if not node:
570 if not node:
571 return
571 return
572 cl = repo.changelog
572 cl = repo.changelog
573 rev = cl.rev(node)
573 rev = cl.rev(node)
574 parents = cl.parentrevs(rev)
574 parents = cl.parentrevs(rev)
575 if rev - 1 in parents:
575 if rev - 1 in parents:
576 # one of the parents was the old tip
576 # one of the parents was the old tip
577 return
577 return
578 if (parents == (nullrev, nullrev) or
578 if (parents == (nullrev, nullrev) or
579 len(cl.heads(cl.node(parents[0]))) > 1 and
579 len(cl.heads(cl.node(parents[0]))) > 1 and
580 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
580 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
581 ui.status(_('created new head\n'))
581 ui.status(_('created new head\n'))
582
582
583 def copy(ui, repo, *pats, **opts):
583 def copy(ui, repo, *pats, **opts):
584 """mark files as copied for the next commit
584 """mark files as copied for the next commit
585
585
586 Mark dest as having copies of source files. If dest is a
586 Mark dest as having copies of source files. If dest is a
587 directory, copies are put in that directory. If dest is a file,
587 directory, copies are put in that directory. If dest is a file,
588 there can only be one source.
588 there can only be one source.
589
589
590 By default, this command copies the contents of files as they
590 By default, this command copies the contents of files as they
591 stand in the working directory. If invoked with --after, the
591 stand in the working directory. If invoked with --after, the
592 operation is recorded, but no copying is performed.
592 operation is recorded, but no copying is performed.
593
593
594 This command takes effect in the next commit. To undo a copy
594 This command takes effect in the next commit. To undo a copy
595 before that, see hg revert.
595 before that, see hg revert.
596 """
596 """
597 wlock = repo.wlock(False)
597 wlock = repo.wlock(False)
598 try:
598 try:
599 return cmdutil.copy(ui, repo, pats, opts)
599 return cmdutil.copy(ui, repo, pats, opts)
600 finally:
600 finally:
601 del wlock
601 del wlock
602
602
603 def debugancestor(ui, repo, *args):
603 def debugancestor(ui, repo, *args):
604 """find the ancestor revision of two revisions in a given index"""
604 """find the ancestor revision of two revisions in a given index"""
605 if len(args) == 3:
605 if len(args) == 3:
606 index, rev1, rev2 = args
606 index, rev1, rev2 = args
607 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
607 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
608 lookup = r.lookup
608 lookup = r.lookup
609 elif len(args) == 2:
609 elif len(args) == 2:
610 if not repo:
610 if not repo:
611 raise util.Abort(_("There is no Mercurial repository here "
611 raise util.Abort(_("There is no Mercurial repository here "
612 "(.hg not found)"))
612 "(.hg not found)"))
613 rev1, rev2 = args
613 rev1, rev2 = args
614 r = repo.changelog
614 r = repo.changelog
615 lookup = repo.lookup
615 lookup = repo.lookup
616 else:
616 else:
617 raise util.Abort(_('either two or three arguments required'))
617 raise util.Abort(_('either two or three arguments required'))
618 a = r.ancestor(lookup(rev1), lookup(rev2))
618 a = r.ancestor(lookup(rev1), lookup(rev2))
619 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
619 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
620
620
621 def debugcomplete(ui, cmd='', **opts):
621 def debugcomplete(ui, cmd='', **opts):
622 """returns the completion list associated with the given command"""
622 """returns the completion list associated with the given command"""
623
623
624 if opts['options']:
624 if opts['options']:
625 options = []
625 options = []
626 otables = [globalopts]
626 otables = [globalopts]
627 if cmd:
627 if cmd:
628 aliases, entry = cmdutil.findcmd(ui, cmd, table)
628 aliases, entry = cmdutil.findcmd(ui, cmd, table)
629 otables.append(entry[1])
629 otables.append(entry[1])
630 for t in otables:
630 for t in otables:
631 for o in t:
631 for o in t:
632 if o[0]:
632 if o[0]:
633 options.append('-%s' % o[0])
633 options.append('-%s' % o[0])
634 options.append('--%s' % o[1])
634 options.append('--%s' % o[1])
635 ui.write("%s\n" % "\n".join(options))
635 ui.write("%s\n" % "\n".join(options))
636 return
636 return
637
637
638 clist = cmdutil.findpossible(ui, cmd, table).keys()
638 clist = cmdutil.findpossible(ui, cmd, table).keys()
639 clist.sort()
639 clist.sort()
640 ui.write("%s\n" % "\n".join(clist))
640 ui.write("%s\n" % "\n".join(clist))
641
641
642 def debugfsinfo(ui, path = "."):
642 def debugfsinfo(ui, path = "."):
643 file('.debugfsinfo', 'w').write('')
643 file('.debugfsinfo', 'w').write('')
644 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
644 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
645 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
645 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
646 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
646 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
647 and 'yes' or 'no'))
647 and 'yes' or 'no'))
648 os.unlink('.debugfsinfo')
648 os.unlink('.debugfsinfo')
649
649
650 def debugrebuildstate(ui, repo, rev=""):
650 def debugrebuildstate(ui, repo, rev=""):
651 """rebuild the dirstate as it would look like for the given revision"""
651 """rebuild the dirstate as it would look like for the given revision"""
652 if rev == "":
652 if rev == "":
653 rev = repo.changelog.tip()
653 rev = repo.changelog.tip()
654 ctx = repo.changectx(rev)
654 ctx = repo.changectx(rev)
655 files = ctx.manifest()
655 files = ctx.manifest()
656 wlock = repo.wlock()
656 wlock = repo.wlock()
657 try:
657 try:
658 repo.dirstate.rebuild(rev, files)
658 repo.dirstate.rebuild(rev, files)
659 finally:
659 finally:
660 del wlock
660 del wlock
661
661
662 def debugcheckstate(ui, repo):
662 def debugcheckstate(ui, repo):
663 """validate the correctness of the current dirstate"""
663 """validate the correctness of the current dirstate"""
664 parent1, parent2 = repo.dirstate.parents()
664 parent1, parent2 = repo.dirstate.parents()
665 m1 = repo.changectx(parent1).manifest()
665 m1 = repo.changectx(parent1).manifest()
666 m2 = repo.changectx(parent2).manifest()
666 m2 = repo.changectx(parent2).manifest()
667 errors = 0
667 errors = 0
668 for f in repo.dirstate:
668 for f in repo.dirstate:
669 state = repo.dirstate[f]
669 state = repo.dirstate[f]
670 if state in "nr" and f not in m1:
670 if state in "nr" and f not in m1:
671 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
671 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
672 errors += 1
672 errors += 1
673 if state in "a" and f in m1:
673 if state in "a" and f in m1:
674 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
674 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
675 errors += 1
675 errors += 1
676 if state in "m" and f not in m1 and f not in m2:
676 if state in "m" and f not in m1 and f not in m2:
677 ui.warn(_("%s in state %s, but not in either manifest\n") %
677 ui.warn(_("%s in state %s, but not in either manifest\n") %
678 (f, state))
678 (f, state))
679 errors += 1
679 errors += 1
680 for f in m1:
680 for f in m1:
681 state = repo.dirstate[f]
681 state = repo.dirstate[f]
682 if state not in "nrm":
682 if state not in "nrm":
683 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
683 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
684 errors += 1
684 errors += 1
685 if errors:
685 if errors:
686 error = _(".hg/dirstate inconsistent with current parent's manifest")
686 error = _(".hg/dirstate inconsistent with current parent's manifest")
687 raise util.Abort(error)
687 raise util.Abort(error)
688
688
689 def showconfig(ui, repo, *values, **opts):
689 def showconfig(ui, repo, *values, **opts):
690 """show combined config settings from all hgrc files
690 """show combined config settings from all hgrc files
691
691
692 With no args, print names and values of all config items.
692 With no args, print names and values of all config items.
693
693
694 With one arg of the form section.name, print just the value of
694 With one arg of the form section.name, print just the value of
695 that config item.
695 that config item.
696
696
697 With multiple args, print names and values of all config items
697 With multiple args, print names and values of all config items
698 with matching section names."""
698 with matching section names."""
699
699
700 untrusted = bool(opts.get('untrusted'))
700 untrusted = bool(opts.get('untrusted'))
701 if values:
701 if values:
702 if len([v for v in values if '.' in v]) > 1:
702 if len([v for v in values if '.' in v]) > 1:
703 raise util.Abort(_('only one config item permitted'))
703 raise util.Abort(_('only one config item permitted'))
704 for section, name, value in ui.walkconfig(untrusted=untrusted):
704 for section, name, value in ui.walkconfig(untrusted=untrusted):
705 sectname = section + '.' + name
705 sectname = section + '.' + name
706 if values:
706 if values:
707 for v in values:
707 for v in values:
708 if v == section:
708 if v == section:
709 ui.write('%s=%s\n' % (sectname, value))
709 ui.write('%s=%s\n' % (sectname, value))
710 elif v == sectname:
710 elif v == sectname:
711 ui.write(value, '\n')
711 ui.write(value, '\n')
712 else:
712 else:
713 ui.write('%s=%s\n' % (sectname, value))
713 ui.write('%s=%s\n' % (sectname, value))
714
714
715 def debugsetparents(ui, repo, rev1, rev2=None):
715 def debugsetparents(ui, repo, rev1, rev2=None):
716 """manually set the parents of the current working directory
716 """manually set the parents of the current working directory
717
717
718 This is useful for writing repository conversion tools, but should
718 This is useful for writing repository conversion tools, but should
719 be used with care.
719 be used with care.
720 """
720 """
721
721
722 if not rev2:
722 if not rev2:
723 rev2 = hex(nullid)
723 rev2 = hex(nullid)
724
724
725 wlock = repo.wlock()
725 wlock = repo.wlock()
726 try:
726 try:
727 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
727 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
728 finally:
728 finally:
729 del wlock
729 del wlock
730
730
731 def debugstate(ui, repo, nodates=None):
731 def debugstate(ui, repo, nodates=None):
732 """show the contents of the current dirstate"""
732 """show the contents of the current dirstate"""
733 k = repo.dirstate._map.items()
733 k = repo.dirstate._map.items()
734 k.sort()
734 k.sort()
735 timestr = ""
735 timestr = ""
736 showdate = not nodates
736 showdate = not nodates
737 for file_, ent in k:
737 for file_, ent in k:
738 if showdate:
738 if showdate:
739 if ent[3] == -1:
739 if ent[3] == -1:
740 # Pad or slice to locale representation
740 # Pad or slice to locale representation
741 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
741 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
742 timestr = 'unset'
742 timestr = 'unset'
743 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
743 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
744 else:
744 else:
745 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
745 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
746 if ent[1] & 020000:
746 if ent[1] & 020000:
747 mode = 'lnk'
747 mode = 'lnk'
748 else:
748 else:
749 mode = '%3o' % (ent[1] & 0777)
749 mode = '%3o' % (ent[1] & 0777)
750 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
750 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
751 for f in repo.dirstate.copies():
751 for f in repo.dirstate.copies():
752 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
752 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
753
753
754 def debugdata(ui, file_, rev):
754 def debugdata(ui, file_, rev):
755 """dump the contents of a data file revision"""
755 """dump the contents of a data file revision"""
756 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
756 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
757 try:
757 try:
758 ui.write(r.revision(r.lookup(rev)))
758 ui.write(r.revision(r.lookup(rev)))
759 except KeyError:
759 except KeyError:
760 raise util.Abort(_('invalid revision identifier %s') % rev)
760 raise util.Abort(_('invalid revision identifier %s') % rev)
761
761
762 def debugdate(ui, date, range=None, **opts):
762 def debugdate(ui, date, range=None, **opts):
763 """parse and display a date"""
763 """parse and display a date"""
764 if opts["extended"]:
764 if opts["extended"]:
765 d = util.parsedate(date, util.extendeddateformats)
765 d = util.parsedate(date, util.extendeddateformats)
766 else:
766 else:
767 d = util.parsedate(date)
767 d = util.parsedate(date)
768 ui.write("internal: %s %s\n" % d)
768 ui.write("internal: %s %s\n" % d)
769 ui.write("standard: %s\n" % util.datestr(d))
769 ui.write("standard: %s\n" % util.datestr(d))
770 if range:
770 if range:
771 m = util.matchdate(range)
771 m = util.matchdate(range)
772 ui.write("match: %s\n" % m(d[0]))
772 ui.write("match: %s\n" % m(d[0]))
773
773
774 def debugindex(ui, file_):
774 def debugindex(ui, file_):
775 """dump the contents of an index file"""
775 """dump the contents of an index file"""
776 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
776 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
777 ui.write(" rev offset length base linkrev" +
777 ui.write(" rev offset length base linkrev" +
778 " nodeid p1 p2\n")
778 " nodeid p1 p2\n")
779 for i in xrange(r.count()):
779 for i in xrange(r.count()):
780 node = r.node(i)
780 node = r.node(i)
781 try:
781 try:
782 pp = r.parents(node)
782 pp = r.parents(node)
783 except:
783 except:
784 pp = [nullid, nullid]
784 pp = [nullid, nullid]
785 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
785 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
786 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
786 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
787 short(node), short(pp[0]), short(pp[1])))
787 short(node), short(pp[0]), short(pp[1])))
788
788
789 def debugindexdot(ui, file_):
789 def debugindexdot(ui, file_):
790 """dump an index DAG as a .dot file"""
790 """dump an index DAG as a .dot file"""
791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
792 ui.write("digraph G {\n")
792 ui.write("digraph G {\n")
793 for i in xrange(r.count()):
793 for i in xrange(r.count()):
794 node = r.node(i)
794 node = r.node(i)
795 pp = r.parents(node)
795 pp = r.parents(node)
796 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
796 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
797 if pp[1] != nullid:
797 if pp[1] != nullid:
798 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
798 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
799 ui.write("}\n")
799 ui.write("}\n")
800
800
801 def debuginstall(ui):
801 def debuginstall(ui):
802 '''test Mercurial installation'''
802 '''test Mercurial installation'''
803
803
804 def writetemp(contents):
804 def writetemp(contents):
805 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
805 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
806 f = os.fdopen(fd, "wb")
806 f = os.fdopen(fd, "wb")
807 f.write(contents)
807 f.write(contents)
808 f.close()
808 f.close()
809 return name
809 return name
810
810
811 problems = 0
811 problems = 0
812
812
813 # encoding
813 # encoding
814 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
814 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
815 try:
815 try:
816 util.fromlocal("test")
816 util.fromlocal("test")
817 except util.Abort, inst:
817 except util.Abort, inst:
818 ui.write(" %s\n" % inst)
818 ui.write(" %s\n" % inst)
819 ui.write(_(" (check that your locale is properly set)\n"))
819 ui.write(_(" (check that your locale is properly set)\n"))
820 problems += 1
820 problems += 1
821
821
822 # compiled modules
822 # compiled modules
823 ui.status(_("Checking extensions...\n"))
823 ui.status(_("Checking extensions...\n"))
824 try:
824 try:
825 import bdiff, mpatch, base85
825 import bdiff, mpatch, base85
826 except Exception, inst:
826 except Exception, inst:
827 ui.write(" %s\n" % inst)
827 ui.write(" %s\n" % inst)
828 ui.write(_(" One or more extensions could not be found"))
828 ui.write(_(" One or more extensions could not be found"))
829 ui.write(_(" (check that you compiled the extensions)\n"))
829 ui.write(_(" (check that you compiled the extensions)\n"))
830 problems += 1
830 problems += 1
831
831
832 # templates
832 # templates
833 ui.status(_("Checking templates...\n"))
833 ui.status(_("Checking templates...\n"))
834 try:
834 try:
835 import templater
835 import templater
836 t = templater.templater(templater.templatepath("map-cmdline.default"))
836 t = templater.templater(templater.templatepath("map-cmdline.default"))
837 except Exception, inst:
837 except Exception, inst:
838 ui.write(" %s\n" % inst)
838 ui.write(" %s\n" % inst)
839 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
839 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
840 problems += 1
840 problems += 1
841
841
842 # patch
842 # patch
843 ui.status(_("Checking patch...\n"))
843 ui.status(_("Checking patch...\n"))
844 patchproblems = 0
844 patchproblems = 0
845 a = "1\n2\n3\n4\n"
845 a = "1\n2\n3\n4\n"
846 b = "1\n2\n3\ninsert\n4\n"
846 b = "1\n2\n3\ninsert\n4\n"
847 fa = writetemp(a)
847 fa = writetemp(a)
848 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
848 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
849 os.path.basename(fa))
849 os.path.basename(fa))
850 fd = writetemp(d)
850 fd = writetemp(d)
851
851
852 files = {}
852 files = {}
853 try:
853 try:
854 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
854 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
855 except util.Abort, e:
855 except util.Abort, e:
856 ui.write(_(" patch call failed:\n"))
856 ui.write(_(" patch call failed:\n"))
857 ui.write(" " + str(e) + "\n")
857 ui.write(" " + str(e) + "\n")
858 patchproblems += 1
858 patchproblems += 1
859 else:
859 else:
860 if list(files) != [os.path.basename(fa)]:
860 if list(files) != [os.path.basename(fa)]:
861 ui.write(_(" unexpected patch output!\n"))
861 ui.write(_(" unexpected patch output!\n"))
862 patchproblems += 1
862 patchproblems += 1
863 a = file(fa).read()
863 a = file(fa).read()
864 if a != b:
864 if a != b:
865 ui.write(_(" patch test failed!\n"))
865 ui.write(_(" patch test failed!\n"))
866 patchproblems += 1
866 patchproblems += 1
867
867
868 if patchproblems:
868 if patchproblems:
869 if ui.config('ui', 'patch'):
869 if ui.config('ui', 'patch'):
870 ui.write(_(" (Current patch tool may be incompatible with patch,"
870 ui.write(_(" (Current patch tool may be incompatible with patch,"
871 " or misconfigured. Please check your .hgrc file)\n"))
871 " or misconfigured. Please check your .hgrc file)\n"))
872 else:
872 else:
873 ui.write(_(" Internal patcher failure, please report this error"
873 ui.write(_(" Internal patcher failure, please report this error"
874 " to http://www.selenic.com/mercurial/bts\n"))
874 " to http://www.selenic.com/mercurial/bts\n"))
875 problems += patchproblems
875 problems += patchproblems
876
876
877 os.unlink(fa)
877 os.unlink(fa)
878 os.unlink(fd)
878 os.unlink(fd)
879
879
880 # editor
880 # editor
881 ui.status(_("Checking commit editor...\n"))
881 ui.status(_("Checking commit editor...\n"))
882 editor = ui.geteditor()
882 editor = ui.geteditor()
883 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
883 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
884 if not cmdpath:
884 if not cmdpath:
885 if editor == 'vi':
885 if editor == 'vi':
886 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
886 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
887 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
887 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
888 else:
888 else:
889 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
889 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
890 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
890 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
891 problems += 1
891 problems += 1
892
892
893 # check username
893 # check username
894 ui.status(_("Checking username...\n"))
894 ui.status(_("Checking username...\n"))
895 user = os.environ.get("HGUSER")
895 user = os.environ.get("HGUSER")
896 if user is None:
896 if user is None:
897 user = ui.config("ui", "username")
897 user = ui.config("ui", "username")
898 if user is None:
898 if user is None:
899 user = os.environ.get("EMAIL")
899 user = os.environ.get("EMAIL")
900 if not user:
900 if not user:
901 ui.warn(" ")
901 ui.warn(" ")
902 ui.username()
902 ui.username()
903 ui.write(_(" (specify a username in your .hgrc file)\n"))
903 ui.write(_(" (specify a username in your .hgrc file)\n"))
904
904
905 if not problems:
905 if not problems:
906 ui.status(_("No problems detected\n"))
906 ui.status(_("No problems detected\n"))
907 else:
907 else:
908 ui.write(_("%s problems detected,"
908 ui.write(_("%s problems detected,"
909 " please check your install!\n") % problems)
909 " please check your install!\n") % problems)
910
910
911 return problems
911 return problems
912
912
913 def debugrename(ui, repo, file1, *pats, **opts):
913 def debugrename(ui, repo, file1, *pats, **opts):
914 """dump rename information"""
914 """dump rename information"""
915
915
916 ctx = repo.changectx(opts.get('rev', 'tip'))
916 ctx = repo.changectx(opts.get('rev', 'tip'))
917 m = cmdutil.match(repo, (file1,) + pats, opts)
917 m = cmdutil.match(repo, (file1,) + pats, opts)
918 for src, abs in cmdutil.walk(repo, m, ctx.node()):
918 for src, abs in repo.walk(m, ctx.node()):
919 fctx = ctx.filectx(abs)
919 fctx = ctx.filectx(abs)
920 o = fctx.filelog().renamed(fctx.filenode())
920 o = fctx.filelog().renamed(fctx.filenode())
921 rel = m.rel(abs)
921 rel = m.rel(abs)
922 if o:
922 if o:
923 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
923 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
924 else:
924 else:
925 ui.write(_("%s not renamed\n") % rel)
925 ui.write(_("%s not renamed\n") % rel)
926
926
927 def debugwalk(ui, repo, *pats, **opts):
927 def debugwalk(ui, repo, *pats, **opts):
928 """show how files match on given patterns"""
928 """show how files match on given patterns"""
929 m = cmdutil.match(repo, pats, opts)
929 m = cmdutil.match(repo, pats, opts)
930 items = list(cmdutil.walk(repo, m))
930 items = list(repo.walk(m))
931 if not items:
931 if not items:
932 return
932 return
933 fmt = '%%s %%-%ds %%-%ds %%s' % (
933 fmt = '%%s %%-%ds %%-%ds %%s' % (
934 max([len(abs) for (src, abs) in items]),
934 max([len(abs) for (src, abs) in items]),
935 max([len(m.rel(abs)) for (src, abs) in items]))
935 max([len(m.rel(abs)) for (src, abs) in items]))
936 for src, abs in items:
936 for src, abs in items:
937 line = fmt % (src, abs, m.rel(abs), m.exact(abs) and 'exact' or '')
937 line = fmt % (src, abs, m.rel(abs), m.exact(abs) and 'exact' or '')
938 ui.write("%s\n" % line.rstrip())
938 ui.write("%s\n" % line.rstrip())
939
939
940 def diff(ui, repo, *pats, **opts):
940 def diff(ui, repo, *pats, **opts):
941 """diff repository (or selected files)
941 """diff repository (or selected files)
942
942
943 Show differences between revisions for the specified files.
943 Show differences between revisions for the specified files.
944
944
945 Differences between files are shown using the unified diff format.
945 Differences between files are shown using the unified diff format.
946
946
947 NOTE: diff may generate unexpected results for merges, as it will
947 NOTE: diff may generate unexpected results for merges, as it will
948 default to comparing against the working directory's first parent
948 default to comparing against the working directory's first parent
949 changeset if no revisions are specified.
949 changeset if no revisions are specified.
950
950
951 When two revision arguments are given, then changes are shown
951 When two revision arguments are given, then changes are shown
952 between those revisions. If only one revision is specified then
952 between those revisions. If only one revision is specified then
953 that revision is compared to the working directory, and, when no
953 that revision is compared to the working directory, and, when no
954 revisions are specified, the working directory files are compared
954 revisions are specified, the working directory files are compared
955 to its parent.
955 to its parent.
956
956
957 Without the -a option, diff will avoid generating diffs of files
957 Without the -a option, diff will avoid generating diffs of files
958 it detects as binary. With -a, diff will generate a diff anyway,
958 it detects as binary. With -a, diff will generate a diff anyway,
959 probably with undesirable results.
959 probably with undesirable results.
960 """
960 """
961 node1, node2 = cmdutil.revpair(repo, opts['rev'])
961 node1, node2 = cmdutil.revpair(repo, opts['rev'])
962
962
963 m = cmdutil.match(repo, pats, opts)
963 m = cmdutil.match(repo, pats, opts)
964 patch.diff(repo, node1, node2, m.files(), match=m,
964 patch.diff(repo, node1, node2, m.files(), match=m,
965 opts=patch.diffopts(ui, opts))
965 opts=patch.diffopts(ui, opts))
966
966
967 def export(ui, repo, *changesets, **opts):
967 def export(ui, repo, *changesets, **opts):
968 """dump the header and diffs for one or more changesets
968 """dump the header and diffs for one or more changesets
969
969
970 Print the changeset header and diffs for one or more revisions.
970 Print the changeset header and diffs for one or more revisions.
971
971
972 The information shown in the changeset header is: author,
972 The information shown in the changeset header is: author,
973 changeset hash, parent(s) and commit comment.
973 changeset hash, parent(s) and commit comment.
974
974
975 NOTE: export may generate unexpected diff output for merge changesets,
975 NOTE: export may generate unexpected diff output for merge changesets,
976 as it will compare the merge changeset against its first parent only.
976 as it will compare the merge changeset against its first parent only.
977
977
978 Output may be to a file, in which case the name of the file is
978 Output may be to a file, in which case the name of the file is
979 given using a format string. The formatting rules are as follows:
979 given using a format string. The formatting rules are as follows:
980
980
981 %% literal "%" character
981 %% literal "%" character
982 %H changeset hash (40 bytes of hexadecimal)
982 %H changeset hash (40 bytes of hexadecimal)
983 %N number of patches being generated
983 %N number of patches being generated
984 %R changeset revision number
984 %R changeset revision number
985 %b basename of the exporting repository
985 %b basename of the exporting repository
986 %h short-form changeset hash (12 bytes of hexadecimal)
986 %h short-form changeset hash (12 bytes of hexadecimal)
987 %n zero-padded sequence number, starting at 1
987 %n zero-padded sequence number, starting at 1
988 %r zero-padded changeset revision number
988 %r zero-padded changeset revision number
989
989
990 Without the -a option, export will avoid generating diffs of files
990 Without the -a option, export will avoid generating diffs of files
991 it detects as binary. With -a, export will generate a diff anyway,
991 it detects as binary. With -a, export will generate a diff anyway,
992 probably with undesirable results.
992 probably with undesirable results.
993
993
994 With the --switch-parent option, the diff will be against the second
994 With the --switch-parent option, the diff will be against the second
995 parent. It can be useful to review a merge.
995 parent. It can be useful to review a merge.
996 """
996 """
997 if not changesets:
997 if not changesets:
998 raise util.Abort(_("export requires at least one changeset"))
998 raise util.Abort(_("export requires at least one changeset"))
999 revs = cmdutil.revrange(repo, changesets)
999 revs = cmdutil.revrange(repo, changesets)
1000 if len(revs) > 1:
1000 if len(revs) > 1:
1001 ui.note(_('exporting patches:\n'))
1001 ui.note(_('exporting patches:\n'))
1002 else:
1002 else:
1003 ui.note(_('exporting patch:\n'))
1003 ui.note(_('exporting patch:\n'))
1004 patch.export(repo, revs, template=opts['output'],
1004 patch.export(repo, revs, template=opts['output'],
1005 switch_parent=opts['switch_parent'],
1005 switch_parent=opts['switch_parent'],
1006 opts=patch.diffopts(ui, opts))
1006 opts=patch.diffopts(ui, opts))
1007
1007
1008 def grep(ui, repo, pattern, *pats, **opts):
1008 def grep(ui, repo, pattern, *pats, **opts):
1009 """search for a pattern in specified files and revisions
1009 """search for a pattern in specified files and revisions
1010
1010
1011 Search revisions of files for a regular expression.
1011 Search revisions of files for a regular expression.
1012
1012
1013 This command behaves differently than Unix grep. It only accepts
1013 This command behaves differently than Unix grep. It only accepts
1014 Python/Perl regexps. It searches repository history, not the
1014 Python/Perl regexps. It searches repository history, not the
1015 working directory. It always prints the revision number in which
1015 working directory. It always prints the revision number in which
1016 a match appears.
1016 a match appears.
1017
1017
1018 By default, grep only prints output for the first revision of a
1018 By default, grep only prints output for the first revision of a
1019 file in which it finds a match. To get it to print every revision
1019 file in which it finds a match. To get it to print every revision
1020 that contains a change in match status ("-" for a match that
1020 that contains a change in match status ("-" for a match that
1021 becomes a non-match, or "+" for a non-match that becomes a match),
1021 becomes a non-match, or "+" for a non-match that becomes a match),
1022 use the --all flag.
1022 use the --all flag.
1023 """
1023 """
1024 reflags = 0
1024 reflags = 0
1025 if opts['ignore_case']:
1025 if opts['ignore_case']:
1026 reflags |= re.I
1026 reflags |= re.I
1027 try:
1027 try:
1028 regexp = re.compile(pattern, reflags)
1028 regexp = re.compile(pattern, reflags)
1029 except Exception, inst:
1029 except Exception, inst:
1030 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1030 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1031 return None
1031 return None
1032 sep, eol = ':', '\n'
1032 sep, eol = ':', '\n'
1033 if opts['print0']:
1033 if opts['print0']:
1034 sep = eol = '\0'
1034 sep = eol = '\0'
1035
1035
1036 fcache = {}
1036 fcache = {}
1037 def getfile(fn):
1037 def getfile(fn):
1038 if fn not in fcache:
1038 if fn not in fcache:
1039 fcache[fn] = repo.file(fn)
1039 fcache[fn] = repo.file(fn)
1040 return fcache[fn]
1040 return fcache[fn]
1041
1041
1042 def matchlines(body):
1042 def matchlines(body):
1043 begin = 0
1043 begin = 0
1044 linenum = 0
1044 linenum = 0
1045 while True:
1045 while True:
1046 match = regexp.search(body, begin)
1046 match = regexp.search(body, begin)
1047 if not match:
1047 if not match:
1048 break
1048 break
1049 mstart, mend = match.span()
1049 mstart, mend = match.span()
1050 linenum += body.count('\n', begin, mstart) + 1
1050 linenum += body.count('\n', begin, mstart) + 1
1051 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1051 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1052 lend = body.find('\n', mend)
1052 lend = body.find('\n', mend)
1053 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1053 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1054 begin = lend + 1
1054 begin = lend + 1
1055
1055
1056 class linestate(object):
1056 class linestate(object):
1057 def __init__(self, line, linenum, colstart, colend):
1057 def __init__(self, line, linenum, colstart, colend):
1058 self.line = line
1058 self.line = line
1059 self.linenum = linenum
1059 self.linenum = linenum
1060 self.colstart = colstart
1060 self.colstart = colstart
1061 self.colend = colend
1061 self.colend = colend
1062
1062
1063 def __hash__(self):
1063 def __hash__(self):
1064 return hash((self.linenum, self.line))
1064 return hash((self.linenum, self.line))
1065
1065
1066 def __eq__(self, other):
1066 def __eq__(self, other):
1067 return self.line == other.line
1067 return self.line == other.line
1068
1068
1069 matches = {}
1069 matches = {}
1070 copies = {}
1070 copies = {}
1071 def grepbody(fn, rev, body):
1071 def grepbody(fn, rev, body):
1072 matches[rev].setdefault(fn, [])
1072 matches[rev].setdefault(fn, [])
1073 m = matches[rev][fn]
1073 m = matches[rev][fn]
1074 for lnum, cstart, cend, line in matchlines(body):
1074 for lnum, cstart, cend, line in matchlines(body):
1075 s = linestate(line, lnum, cstart, cend)
1075 s = linestate(line, lnum, cstart, cend)
1076 m.append(s)
1076 m.append(s)
1077
1077
1078 def difflinestates(a, b):
1078 def difflinestates(a, b):
1079 sm = difflib.SequenceMatcher(None, a, b)
1079 sm = difflib.SequenceMatcher(None, a, b)
1080 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1080 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1081 if tag == 'insert':
1081 if tag == 'insert':
1082 for i in xrange(blo, bhi):
1082 for i in xrange(blo, bhi):
1083 yield ('+', b[i])
1083 yield ('+', b[i])
1084 elif tag == 'delete':
1084 elif tag == 'delete':
1085 for i in xrange(alo, ahi):
1085 for i in xrange(alo, ahi):
1086 yield ('-', a[i])
1086 yield ('-', a[i])
1087 elif tag == 'replace':
1087 elif tag == 'replace':
1088 for i in xrange(alo, ahi):
1088 for i in xrange(alo, ahi):
1089 yield ('-', a[i])
1089 yield ('-', a[i])
1090 for i in xrange(blo, bhi):
1090 for i in xrange(blo, bhi):
1091 yield ('+', b[i])
1091 yield ('+', b[i])
1092
1092
1093 prev = {}
1093 prev = {}
1094 def display(fn, rev, states, prevstates):
1094 def display(fn, rev, states, prevstates):
1095 datefunc = ui.quiet and util.shortdate or util.datestr
1095 datefunc = ui.quiet and util.shortdate or util.datestr
1096 found = False
1096 found = False
1097 filerevmatches = {}
1097 filerevmatches = {}
1098 r = prev.get(fn, -1)
1098 r = prev.get(fn, -1)
1099 if opts['all']:
1099 if opts['all']:
1100 iter = difflinestates(states, prevstates)
1100 iter = difflinestates(states, prevstates)
1101 else:
1101 else:
1102 iter = [('', l) for l in prevstates]
1102 iter = [('', l) for l in prevstates]
1103 for change, l in iter:
1103 for change, l in iter:
1104 cols = [fn, str(r)]
1104 cols = [fn, str(r)]
1105 if opts['line_number']:
1105 if opts['line_number']:
1106 cols.append(str(l.linenum))
1106 cols.append(str(l.linenum))
1107 if opts['all']:
1107 if opts['all']:
1108 cols.append(change)
1108 cols.append(change)
1109 if opts['user']:
1109 if opts['user']:
1110 cols.append(ui.shortuser(get(r)[1]))
1110 cols.append(ui.shortuser(get(r)[1]))
1111 if opts.get('date'):
1111 if opts.get('date'):
1112 cols.append(datefunc(get(r)[2]))
1112 cols.append(datefunc(get(r)[2]))
1113 if opts['files_with_matches']:
1113 if opts['files_with_matches']:
1114 c = (fn, r)
1114 c = (fn, r)
1115 if c in filerevmatches:
1115 if c in filerevmatches:
1116 continue
1116 continue
1117 filerevmatches[c] = 1
1117 filerevmatches[c] = 1
1118 else:
1118 else:
1119 cols.append(l.line)
1119 cols.append(l.line)
1120 ui.write(sep.join(cols), eol)
1120 ui.write(sep.join(cols), eol)
1121 found = True
1121 found = True
1122 return found
1122 return found
1123
1123
1124 fstate = {}
1124 fstate = {}
1125 skip = {}
1125 skip = {}
1126 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1126 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1127 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1127 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1128 found = False
1128 found = False
1129 follow = opts.get('follow')
1129 follow = opts.get('follow')
1130 for st, rev, fns in changeiter:
1130 for st, rev, fns in changeiter:
1131 if st == 'window':
1131 if st == 'window':
1132 matches.clear()
1132 matches.clear()
1133 elif st == 'add':
1133 elif st == 'add':
1134 ctx = repo.changectx(rev)
1134 ctx = repo.changectx(rev)
1135 matches[rev] = {}
1135 matches[rev] = {}
1136 for fn in fns:
1136 for fn in fns:
1137 if fn in skip:
1137 if fn in skip:
1138 continue
1138 continue
1139 try:
1139 try:
1140 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1140 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1141 fstate.setdefault(fn, [])
1141 fstate.setdefault(fn, [])
1142 if follow:
1142 if follow:
1143 copied = getfile(fn).renamed(ctx.filenode(fn))
1143 copied = getfile(fn).renamed(ctx.filenode(fn))
1144 if copied:
1144 if copied:
1145 copies.setdefault(rev, {})[fn] = copied[0]
1145 copies.setdefault(rev, {})[fn] = copied[0]
1146 except revlog.LookupError:
1146 except revlog.LookupError:
1147 pass
1147 pass
1148 elif st == 'iter':
1148 elif st == 'iter':
1149 states = matches[rev].items()
1149 states = matches[rev].items()
1150 states.sort()
1150 states.sort()
1151 for fn, m in states:
1151 for fn, m in states:
1152 copy = copies.get(rev, {}).get(fn)
1152 copy = copies.get(rev, {}).get(fn)
1153 if fn in skip:
1153 if fn in skip:
1154 if copy:
1154 if copy:
1155 skip[copy] = True
1155 skip[copy] = True
1156 continue
1156 continue
1157 if fn in prev or fstate[fn]:
1157 if fn in prev or fstate[fn]:
1158 r = display(fn, rev, m, fstate[fn])
1158 r = display(fn, rev, m, fstate[fn])
1159 found = found or r
1159 found = found or r
1160 if r and not opts['all']:
1160 if r and not opts['all']:
1161 skip[fn] = True
1161 skip[fn] = True
1162 if copy:
1162 if copy:
1163 skip[copy] = True
1163 skip[copy] = True
1164 fstate[fn] = m
1164 fstate[fn] = m
1165 if copy:
1165 if copy:
1166 fstate[copy] = m
1166 fstate[copy] = m
1167 prev[fn] = rev
1167 prev[fn] = rev
1168
1168
1169 fstate = fstate.items()
1169 fstate = fstate.items()
1170 fstate.sort()
1170 fstate.sort()
1171 for fn, state in fstate:
1171 for fn, state in fstate:
1172 if fn in skip:
1172 if fn in skip:
1173 continue
1173 continue
1174 if fn not in copies.get(prev[fn], {}):
1174 if fn not in copies.get(prev[fn], {}):
1175 found = display(fn, rev, {}, state) or found
1175 found = display(fn, rev, {}, state) or found
1176 return (not found and 1) or 0
1176 return (not found and 1) or 0
1177
1177
1178 def heads(ui, repo, *branchrevs, **opts):
1178 def heads(ui, repo, *branchrevs, **opts):
1179 """show current repository heads or show branch heads
1179 """show current repository heads or show branch heads
1180
1180
1181 With no arguments, show all repository head changesets.
1181 With no arguments, show all repository head changesets.
1182
1182
1183 If branch or revisions names are given this will show the heads of
1183 If branch or revisions names are given this will show the heads of
1184 the specified branches or the branches those revisions are tagged
1184 the specified branches or the branches those revisions are tagged
1185 with.
1185 with.
1186
1186
1187 Repository "heads" are changesets that don't have child
1187 Repository "heads" are changesets that don't have child
1188 changesets. They are where development generally takes place and
1188 changesets. They are where development generally takes place and
1189 are the usual targets for update and merge operations.
1189 are the usual targets for update and merge operations.
1190
1190
1191 Branch heads are changesets that have a given branch tag, but have
1191 Branch heads are changesets that have a given branch tag, but have
1192 no child changesets with that tag. They are usually where
1192 no child changesets with that tag. They are usually where
1193 development on the given branch takes place.
1193 development on the given branch takes place.
1194 """
1194 """
1195 if opts['rev']:
1195 if opts['rev']:
1196 start = repo.lookup(opts['rev'])
1196 start = repo.lookup(opts['rev'])
1197 else:
1197 else:
1198 start = None
1198 start = None
1199 if not branchrevs:
1199 if not branchrevs:
1200 # Assume we're looking repo-wide heads if no revs were specified.
1200 # Assume we're looking repo-wide heads if no revs were specified.
1201 heads = repo.heads(start)
1201 heads = repo.heads(start)
1202 else:
1202 else:
1203 heads = []
1203 heads = []
1204 visitedset = util.set()
1204 visitedset = util.set()
1205 for branchrev in branchrevs:
1205 for branchrev in branchrevs:
1206 branch = repo.changectx(branchrev).branch()
1206 branch = repo.changectx(branchrev).branch()
1207 if branch in visitedset:
1207 if branch in visitedset:
1208 continue
1208 continue
1209 visitedset.add(branch)
1209 visitedset.add(branch)
1210 bheads = repo.branchheads(branch, start)
1210 bheads = repo.branchheads(branch, start)
1211 if not bheads:
1211 if not bheads:
1212 if branch != branchrev:
1212 if branch != branchrev:
1213 ui.warn(_("no changes on branch %s containing %s are "
1213 ui.warn(_("no changes on branch %s containing %s are "
1214 "reachable from %s\n")
1214 "reachable from %s\n")
1215 % (branch, branchrev, opts['rev']))
1215 % (branch, branchrev, opts['rev']))
1216 else:
1216 else:
1217 ui.warn(_("no changes on branch %s are reachable from %s\n")
1217 ui.warn(_("no changes on branch %s are reachable from %s\n")
1218 % (branch, opts['rev']))
1218 % (branch, opts['rev']))
1219 heads.extend(bheads)
1219 heads.extend(bheads)
1220 if not heads:
1220 if not heads:
1221 return 1
1221 return 1
1222 displayer = cmdutil.show_changeset(ui, repo, opts)
1222 displayer = cmdutil.show_changeset(ui, repo, opts)
1223 for n in heads:
1223 for n in heads:
1224 displayer.show(changenode=n)
1224 displayer.show(changenode=n)
1225
1225
1226 def help_(ui, name=None, with_version=False):
1226 def help_(ui, name=None, with_version=False):
1227 """show help for a command, extension, or list of commands
1227 """show help for a command, extension, or list of commands
1228
1228
1229 With no arguments, print a list of commands and short help.
1229 With no arguments, print a list of commands and short help.
1230
1230
1231 Given a command name, print help for that command.
1231 Given a command name, print help for that command.
1232
1232
1233 Given an extension name, print help for that extension, and the
1233 Given an extension name, print help for that extension, and the
1234 commands it provides."""
1234 commands it provides."""
1235 option_lists = []
1235 option_lists = []
1236
1236
1237 def addglobalopts(aliases):
1237 def addglobalopts(aliases):
1238 if ui.verbose:
1238 if ui.verbose:
1239 option_lists.append((_("global options:"), globalopts))
1239 option_lists.append((_("global options:"), globalopts))
1240 if name == 'shortlist':
1240 if name == 'shortlist':
1241 option_lists.append((_('use "hg help" for the full list '
1241 option_lists.append((_('use "hg help" for the full list '
1242 'of commands'), ()))
1242 'of commands'), ()))
1243 else:
1243 else:
1244 if name == 'shortlist':
1244 if name == 'shortlist':
1245 msg = _('use "hg help" for the full list of commands '
1245 msg = _('use "hg help" for the full list of commands '
1246 'or "hg -v" for details')
1246 'or "hg -v" for details')
1247 elif aliases:
1247 elif aliases:
1248 msg = _('use "hg -v help%s" to show aliases and '
1248 msg = _('use "hg -v help%s" to show aliases and '
1249 'global options') % (name and " " + name or "")
1249 'global options') % (name and " " + name or "")
1250 else:
1250 else:
1251 msg = _('use "hg -v help %s" to show global options') % name
1251 msg = _('use "hg -v help %s" to show global options') % name
1252 option_lists.append((msg, ()))
1252 option_lists.append((msg, ()))
1253
1253
1254 def helpcmd(name):
1254 def helpcmd(name):
1255 if with_version:
1255 if with_version:
1256 version_(ui)
1256 version_(ui)
1257 ui.write('\n')
1257 ui.write('\n')
1258 aliases, i = cmdutil.findcmd(ui, name, table)
1258 aliases, i = cmdutil.findcmd(ui, name, table)
1259 # synopsis
1259 # synopsis
1260 ui.write("%s\n" % i[2])
1260 ui.write("%s\n" % i[2])
1261
1261
1262 # aliases
1262 # aliases
1263 if not ui.quiet and len(aliases) > 1:
1263 if not ui.quiet and len(aliases) > 1:
1264 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1264 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1265
1265
1266 # description
1266 # description
1267 doc = i[0].__doc__
1267 doc = i[0].__doc__
1268 if not doc:
1268 if not doc:
1269 doc = _("(No help text available)")
1269 doc = _("(No help text available)")
1270 if ui.quiet:
1270 if ui.quiet:
1271 doc = doc.splitlines(0)[0]
1271 doc = doc.splitlines(0)[0]
1272 ui.write("\n%s\n" % doc.rstrip())
1272 ui.write("\n%s\n" % doc.rstrip())
1273
1273
1274 if not ui.quiet:
1274 if not ui.quiet:
1275 # options
1275 # options
1276 if i[1]:
1276 if i[1]:
1277 option_lists.append((_("options:\n"), i[1]))
1277 option_lists.append((_("options:\n"), i[1]))
1278
1278
1279 addglobalopts(False)
1279 addglobalopts(False)
1280
1280
1281 def helplist(header, select=None):
1281 def helplist(header, select=None):
1282 h = {}
1282 h = {}
1283 cmds = {}
1283 cmds = {}
1284 for c, e in table.items():
1284 for c, e in table.items():
1285 f = c.split("|", 1)[0]
1285 f = c.split("|", 1)[0]
1286 if select and not select(f):
1286 if select and not select(f):
1287 continue
1287 continue
1288 if name == "shortlist" and not f.startswith("^"):
1288 if name == "shortlist" and not f.startswith("^"):
1289 continue
1289 continue
1290 f = f.lstrip("^")
1290 f = f.lstrip("^")
1291 if not ui.debugflag and f.startswith("debug"):
1291 if not ui.debugflag and f.startswith("debug"):
1292 continue
1292 continue
1293 doc = e[0].__doc__
1293 doc = e[0].__doc__
1294 if not doc:
1294 if not doc:
1295 doc = _("(No help text available)")
1295 doc = _("(No help text available)")
1296 h[f] = doc.splitlines(0)[0].rstrip()
1296 h[f] = doc.splitlines(0)[0].rstrip()
1297 cmds[f] = c.lstrip("^")
1297 cmds[f] = c.lstrip("^")
1298
1298
1299 if not h:
1299 if not h:
1300 ui.status(_('no commands defined\n'))
1300 ui.status(_('no commands defined\n'))
1301 return
1301 return
1302
1302
1303 ui.status(header)
1303 ui.status(header)
1304 fns = h.keys()
1304 fns = h.keys()
1305 fns.sort()
1305 fns.sort()
1306 m = max(map(len, fns))
1306 m = max(map(len, fns))
1307 for f in fns:
1307 for f in fns:
1308 if ui.verbose:
1308 if ui.verbose:
1309 commands = cmds[f].replace("|",", ")
1309 commands = cmds[f].replace("|",", ")
1310 ui.write(" %s:\n %s\n"%(commands, h[f]))
1310 ui.write(" %s:\n %s\n"%(commands, h[f]))
1311 else:
1311 else:
1312 ui.write(' %-*s %s\n' % (m, f, h[f]))
1312 ui.write(' %-*s %s\n' % (m, f, h[f]))
1313
1313
1314 if not ui.quiet:
1314 if not ui.quiet:
1315 addglobalopts(True)
1315 addglobalopts(True)
1316
1316
1317 def helptopic(name):
1317 def helptopic(name):
1318 v = None
1318 v = None
1319 for i in help.helptable:
1319 for i in help.helptable:
1320 l = i.split('|')
1320 l = i.split('|')
1321 if name in l:
1321 if name in l:
1322 v = i
1322 v = i
1323 header = l[-1]
1323 header = l[-1]
1324 if not v:
1324 if not v:
1325 raise cmdutil.UnknownCommand(name)
1325 raise cmdutil.UnknownCommand(name)
1326
1326
1327 # description
1327 # description
1328 doc = help.helptable[v]
1328 doc = help.helptable[v]
1329 if not doc:
1329 if not doc:
1330 doc = _("(No help text available)")
1330 doc = _("(No help text available)")
1331 if callable(doc):
1331 if callable(doc):
1332 doc = doc()
1332 doc = doc()
1333
1333
1334 ui.write("%s\n" % header)
1334 ui.write("%s\n" % header)
1335 ui.write("%s\n" % doc.rstrip())
1335 ui.write("%s\n" % doc.rstrip())
1336
1336
1337 def helpext(name):
1337 def helpext(name):
1338 try:
1338 try:
1339 mod = extensions.find(name)
1339 mod = extensions.find(name)
1340 except KeyError:
1340 except KeyError:
1341 raise cmdutil.UnknownCommand(name)
1341 raise cmdutil.UnknownCommand(name)
1342
1342
1343 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1343 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1344 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1344 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1345 for d in doc[1:]:
1345 for d in doc[1:]:
1346 ui.write(d, '\n')
1346 ui.write(d, '\n')
1347
1347
1348 ui.status('\n')
1348 ui.status('\n')
1349
1349
1350 try:
1350 try:
1351 ct = mod.cmdtable
1351 ct = mod.cmdtable
1352 except AttributeError:
1352 except AttributeError:
1353 ct = {}
1353 ct = {}
1354
1354
1355 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1355 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1356 helplist(_('list of commands:\n\n'), modcmds.has_key)
1356 helplist(_('list of commands:\n\n'), modcmds.has_key)
1357
1357
1358 if name and name != 'shortlist':
1358 if name and name != 'shortlist':
1359 i = None
1359 i = None
1360 for f in (helpcmd, helptopic, helpext):
1360 for f in (helpcmd, helptopic, helpext):
1361 try:
1361 try:
1362 f(name)
1362 f(name)
1363 i = None
1363 i = None
1364 break
1364 break
1365 except cmdutil.UnknownCommand, inst:
1365 except cmdutil.UnknownCommand, inst:
1366 i = inst
1366 i = inst
1367 if i:
1367 if i:
1368 raise i
1368 raise i
1369
1369
1370 else:
1370 else:
1371 # program name
1371 # program name
1372 if ui.verbose or with_version:
1372 if ui.verbose or with_version:
1373 version_(ui)
1373 version_(ui)
1374 else:
1374 else:
1375 ui.status(_("Mercurial Distributed SCM\n"))
1375 ui.status(_("Mercurial Distributed SCM\n"))
1376 ui.status('\n')
1376 ui.status('\n')
1377
1377
1378 # list of commands
1378 # list of commands
1379 if name == "shortlist":
1379 if name == "shortlist":
1380 header = _('basic commands:\n\n')
1380 header = _('basic commands:\n\n')
1381 else:
1381 else:
1382 header = _('list of commands:\n\n')
1382 header = _('list of commands:\n\n')
1383
1383
1384 helplist(header)
1384 helplist(header)
1385
1385
1386 # list all option lists
1386 # list all option lists
1387 opt_output = []
1387 opt_output = []
1388 for title, options in option_lists:
1388 for title, options in option_lists:
1389 opt_output.append(("\n%s" % title, None))
1389 opt_output.append(("\n%s" % title, None))
1390 for shortopt, longopt, default, desc in options:
1390 for shortopt, longopt, default, desc in options:
1391 if "DEPRECATED" in desc and not ui.verbose: continue
1391 if "DEPRECATED" in desc and not ui.verbose: continue
1392 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1392 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1393 longopt and " --%s" % longopt),
1393 longopt and " --%s" % longopt),
1394 "%s%s" % (desc,
1394 "%s%s" % (desc,
1395 default
1395 default
1396 and _(" (default: %s)") % default
1396 and _(" (default: %s)") % default
1397 or "")))
1397 or "")))
1398
1398
1399 if opt_output:
1399 if opt_output:
1400 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1400 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1401 for first, second in opt_output:
1401 for first, second in opt_output:
1402 if second:
1402 if second:
1403 ui.write(" %-*s %s\n" % (opts_len, first, second))
1403 ui.write(" %-*s %s\n" % (opts_len, first, second))
1404 else:
1404 else:
1405 ui.write("%s\n" % first)
1405 ui.write("%s\n" % first)
1406
1406
1407 def identify(ui, repo, source=None,
1407 def identify(ui, repo, source=None,
1408 rev=None, num=None, id=None, branch=None, tags=None):
1408 rev=None, num=None, id=None, branch=None, tags=None):
1409 """identify the working copy or specified revision
1409 """identify the working copy or specified revision
1410
1410
1411 With no revision, print a summary of the current state of the repo.
1411 With no revision, print a summary of the current state of the repo.
1412
1412
1413 With a path, do a lookup in another repository.
1413 With a path, do a lookup in another repository.
1414
1414
1415 This summary identifies the repository state using one or two parent
1415 This summary identifies the repository state using one or two parent
1416 hash identifiers, followed by a "+" if there are uncommitted changes
1416 hash identifiers, followed by a "+" if there are uncommitted changes
1417 in the working directory, a list of tags for this revision and a branch
1417 in the working directory, a list of tags for this revision and a branch
1418 name for non-default branches.
1418 name for non-default branches.
1419 """
1419 """
1420
1420
1421 if not repo and not source:
1421 if not repo and not source:
1422 raise util.Abort(_("There is no Mercurial repository here "
1422 raise util.Abort(_("There is no Mercurial repository here "
1423 "(.hg not found)"))
1423 "(.hg not found)"))
1424
1424
1425 hexfunc = ui.debugflag and hex or short
1425 hexfunc = ui.debugflag and hex or short
1426 default = not (num or id or branch or tags)
1426 default = not (num or id or branch or tags)
1427 output = []
1427 output = []
1428
1428
1429 if source:
1429 if source:
1430 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1430 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1431 srepo = hg.repository(ui, source)
1431 srepo = hg.repository(ui, source)
1432 if not rev and revs:
1432 if not rev and revs:
1433 rev = revs[0]
1433 rev = revs[0]
1434 if not rev:
1434 if not rev:
1435 rev = "tip"
1435 rev = "tip"
1436 if num or branch or tags:
1436 if num or branch or tags:
1437 raise util.Abort(
1437 raise util.Abort(
1438 "can't query remote revision number, branch, or tags")
1438 "can't query remote revision number, branch, or tags")
1439 output = [hexfunc(srepo.lookup(rev))]
1439 output = [hexfunc(srepo.lookup(rev))]
1440 elif not rev:
1440 elif not rev:
1441 ctx = repo.workingctx()
1441 ctx = repo.workingctx()
1442 parents = ctx.parents()
1442 parents = ctx.parents()
1443 changed = False
1443 changed = False
1444 if default or id or num:
1444 if default or id or num:
1445 changed = ctx.files() + ctx.deleted()
1445 changed = ctx.files() + ctx.deleted()
1446 if default or id:
1446 if default or id:
1447 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1447 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1448 (changed) and "+" or "")]
1448 (changed) and "+" or "")]
1449 if num:
1449 if num:
1450 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1450 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1451 (changed) and "+" or ""))
1451 (changed) and "+" or ""))
1452 else:
1452 else:
1453 ctx = repo.changectx(rev)
1453 ctx = repo.changectx(rev)
1454 if default or id:
1454 if default or id:
1455 output = [hexfunc(ctx.node())]
1455 output = [hexfunc(ctx.node())]
1456 if num:
1456 if num:
1457 output.append(str(ctx.rev()))
1457 output.append(str(ctx.rev()))
1458
1458
1459 if not source and default and not ui.quiet:
1459 if not source and default and not ui.quiet:
1460 b = util.tolocal(ctx.branch())
1460 b = util.tolocal(ctx.branch())
1461 if b != 'default':
1461 if b != 'default':
1462 output.append("(%s)" % b)
1462 output.append("(%s)" % b)
1463
1463
1464 # multiple tags for a single parent separated by '/'
1464 # multiple tags for a single parent separated by '/'
1465 t = "/".join(ctx.tags())
1465 t = "/".join(ctx.tags())
1466 if t:
1466 if t:
1467 output.append(t)
1467 output.append(t)
1468
1468
1469 if branch:
1469 if branch:
1470 output.append(util.tolocal(ctx.branch()))
1470 output.append(util.tolocal(ctx.branch()))
1471
1471
1472 if tags:
1472 if tags:
1473 output.extend(ctx.tags())
1473 output.extend(ctx.tags())
1474
1474
1475 ui.write("%s\n" % ' '.join(output))
1475 ui.write("%s\n" % ' '.join(output))
1476
1476
1477 def import_(ui, repo, patch1, *patches, **opts):
1477 def import_(ui, repo, patch1, *patches, **opts):
1478 """import an ordered set of patches
1478 """import an ordered set of patches
1479
1479
1480 Import a list of patches and commit them individually.
1480 Import a list of patches and commit them individually.
1481
1481
1482 If there are outstanding changes in the working directory, import
1482 If there are outstanding changes in the working directory, import
1483 will abort unless given the -f flag.
1483 will abort unless given the -f flag.
1484
1484
1485 You can import a patch straight from a mail message. Even patches
1485 You can import a patch straight from a mail message. Even patches
1486 as attachments work (body part must be type text/plain or
1486 as attachments work (body part must be type text/plain or
1487 text/x-patch to be used). From and Subject headers of email
1487 text/x-patch to be used). From and Subject headers of email
1488 message are used as default committer and commit message. All
1488 message are used as default committer and commit message. All
1489 text/plain body parts before first diff are added to commit
1489 text/plain body parts before first diff are added to commit
1490 message.
1490 message.
1491
1491
1492 If the imported patch was generated by hg export, user and description
1492 If the imported patch was generated by hg export, user and description
1493 from patch override values from message headers and body. Values
1493 from patch override values from message headers and body. Values
1494 given on command line with -m and -u override these.
1494 given on command line with -m and -u override these.
1495
1495
1496 If --exact is specified, import will set the working directory
1496 If --exact is specified, import will set the working directory
1497 to the parent of each patch before applying it, and will abort
1497 to the parent of each patch before applying it, and will abort
1498 if the resulting changeset has a different ID than the one
1498 if the resulting changeset has a different ID than the one
1499 recorded in the patch. This may happen due to character set
1499 recorded in the patch. This may happen due to character set
1500 problems or other deficiencies in the text patch format.
1500 problems or other deficiencies in the text patch format.
1501
1501
1502 To read a patch from standard input, use patch name "-".
1502 To read a patch from standard input, use patch name "-".
1503 See 'hg help dates' for a list of formats valid for -d/--date.
1503 See 'hg help dates' for a list of formats valid for -d/--date.
1504 """
1504 """
1505 patches = (patch1,) + patches
1505 patches = (patch1,) + patches
1506
1506
1507 date = opts.get('date')
1507 date = opts.get('date')
1508 if date:
1508 if date:
1509 opts['date'] = util.parsedate(date)
1509 opts['date'] = util.parsedate(date)
1510
1510
1511 if opts.get('exact') or not opts['force']:
1511 if opts.get('exact') or not opts['force']:
1512 cmdutil.bail_if_changed(repo)
1512 cmdutil.bail_if_changed(repo)
1513
1513
1514 d = opts["base"]
1514 d = opts["base"]
1515 strip = opts["strip"]
1515 strip = opts["strip"]
1516 wlock = lock = None
1516 wlock = lock = None
1517 try:
1517 try:
1518 wlock = repo.wlock()
1518 wlock = repo.wlock()
1519 lock = repo.lock()
1519 lock = repo.lock()
1520 for p in patches:
1520 for p in patches:
1521 pf = os.path.join(d, p)
1521 pf = os.path.join(d, p)
1522
1522
1523 if pf == '-':
1523 if pf == '-':
1524 ui.status(_("applying patch from stdin\n"))
1524 ui.status(_("applying patch from stdin\n"))
1525 data = patch.extract(ui, sys.stdin)
1525 data = patch.extract(ui, sys.stdin)
1526 else:
1526 else:
1527 ui.status(_("applying %s\n") % p)
1527 ui.status(_("applying %s\n") % p)
1528 if os.path.exists(pf):
1528 if os.path.exists(pf):
1529 data = patch.extract(ui, file(pf, 'rb'))
1529 data = patch.extract(ui, file(pf, 'rb'))
1530 else:
1530 else:
1531 data = patch.extract(ui, urllib.urlopen(pf))
1531 data = patch.extract(ui, urllib.urlopen(pf))
1532 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1532 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1533
1533
1534 if tmpname is None:
1534 if tmpname is None:
1535 raise util.Abort(_('no diffs found'))
1535 raise util.Abort(_('no diffs found'))
1536
1536
1537 try:
1537 try:
1538 cmdline_message = cmdutil.logmessage(opts)
1538 cmdline_message = cmdutil.logmessage(opts)
1539 if cmdline_message:
1539 if cmdline_message:
1540 # pickup the cmdline msg
1540 # pickup the cmdline msg
1541 message = cmdline_message
1541 message = cmdline_message
1542 elif message:
1542 elif message:
1543 # pickup the patch msg
1543 # pickup the patch msg
1544 message = message.strip()
1544 message = message.strip()
1545 else:
1545 else:
1546 # launch the editor
1546 # launch the editor
1547 message = None
1547 message = None
1548 ui.debug(_('message:\n%s\n') % message)
1548 ui.debug(_('message:\n%s\n') % message)
1549
1549
1550 wp = repo.workingctx().parents()
1550 wp = repo.workingctx().parents()
1551 if opts.get('exact'):
1551 if opts.get('exact'):
1552 if not nodeid or not p1:
1552 if not nodeid or not p1:
1553 raise util.Abort(_('not a mercurial patch'))
1553 raise util.Abort(_('not a mercurial patch'))
1554 p1 = repo.lookup(p1)
1554 p1 = repo.lookup(p1)
1555 p2 = repo.lookup(p2 or hex(nullid))
1555 p2 = repo.lookup(p2 or hex(nullid))
1556
1556
1557 if p1 != wp[0].node():
1557 if p1 != wp[0].node():
1558 hg.clean(repo, p1)
1558 hg.clean(repo, p1)
1559 repo.dirstate.setparents(p1, p2)
1559 repo.dirstate.setparents(p1, p2)
1560 elif p2:
1560 elif p2:
1561 try:
1561 try:
1562 p1 = repo.lookup(p1)
1562 p1 = repo.lookup(p1)
1563 p2 = repo.lookup(p2)
1563 p2 = repo.lookup(p2)
1564 if p1 == wp[0].node():
1564 if p1 == wp[0].node():
1565 repo.dirstate.setparents(p1, p2)
1565 repo.dirstate.setparents(p1, p2)
1566 except RepoError:
1566 except RepoError:
1567 pass
1567 pass
1568 if opts.get('exact') or opts.get('import_branch'):
1568 if opts.get('exact') or opts.get('import_branch'):
1569 repo.dirstate.setbranch(branch or 'default')
1569 repo.dirstate.setbranch(branch or 'default')
1570
1570
1571 files = {}
1571 files = {}
1572 try:
1572 try:
1573 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1573 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1574 files=files)
1574 files=files)
1575 finally:
1575 finally:
1576 files = patch.updatedir(ui, repo, files)
1576 files = patch.updatedir(ui, repo, files)
1577 if not opts.get('no_commit'):
1577 if not opts.get('no_commit'):
1578 n = repo.commit(files, message, opts.get('user') or user,
1578 n = repo.commit(files, message, opts.get('user') or user,
1579 opts.get('date') or date)
1579 opts.get('date') or date)
1580 if opts.get('exact'):
1580 if opts.get('exact'):
1581 if hex(n) != nodeid:
1581 if hex(n) != nodeid:
1582 repo.rollback()
1582 repo.rollback()
1583 raise util.Abort(_('patch is damaged'
1583 raise util.Abort(_('patch is damaged'
1584 ' or loses information'))
1584 ' or loses information'))
1585 # Force a dirstate write so that the next transaction
1585 # Force a dirstate write so that the next transaction
1586 # backups an up-do-date file.
1586 # backups an up-do-date file.
1587 repo.dirstate.write()
1587 repo.dirstate.write()
1588 finally:
1588 finally:
1589 os.unlink(tmpname)
1589 os.unlink(tmpname)
1590 finally:
1590 finally:
1591 del lock, wlock
1591 del lock, wlock
1592
1592
1593 def incoming(ui, repo, source="default", **opts):
1593 def incoming(ui, repo, source="default", **opts):
1594 """show new changesets found in source
1594 """show new changesets found in source
1595
1595
1596 Show new changesets found in the specified path/URL or the default
1596 Show new changesets found in the specified path/URL or the default
1597 pull location. These are the changesets that would be pulled if a pull
1597 pull location. These are the changesets that would be pulled if a pull
1598 was requested.
1598 was requested.
1599
1599
1600 For remote repository, using --bundle avoids downloading the changesets
1600 For remote repository, using --bundle avoids downloading the changesets
1601 twice if the incoming is followed by a pull.
1601 twice if the incoming is followed by a pull.
1602
1602
1603 See pull for valid source format details.
1603 See pull for valid source format details.
1604 """
1604 """
1605 limit = cmdutil.loglimit(opts)
1605 limit = cmdutil.loglimit(opts)
1606 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1606 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1607 cmdutil.setremoteconfig(ui, opts)
1607 cmdutil.setremoteconfig(ui, opts)
1608
1608
1609 other = hg.repository(ui, source)
1609 other = hg.repository(ui, source)
1610 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1610 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1611 if revs:
1611 if revs:
1612 revs = [other.lookup(rev) for rev in revs]
1612 revs = [other.lookup(rev) for rev in revs]
1613 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1613 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1614 if not incoming:
1614 if not incoming:
1615 try:
1615 try:
1616 os.unlink(opts["bundle"])
1616 os.unlink(opts["bundle"])
1617 except:
1617 except:
1618 pass
1618 pass
1619 ui.status(_("no changes found\n"))
1619 ui.status(_("no changes found\n"))
1620 return 1
1620 return 1
1621
1621
1622 cleanup = None
1622 cleanup = None
1623 try:
1623 try:
1624 fname = opts["bundle"]
1624 fname = opts["bundle"]
1625 if fname or not other.local():
1625 if fname or not other.local():
1626 # create a bundle (uncompressed if other repo is not local)
1626 # create a bundle (uncompressed if other repo is not local)
1627 if revs is None:
1627 if revs is None:
1628 cg = other.changegroup(incoming, "incoming")
1628 cg = other.changegroup(incoming, "incoming")
1629 else:
1629 else:
1630 cg = other.changegroupsubset(incoming, revs, 'incoming')
1630 cg = other.changegroupsubset(incoming, revs, 'incoming')
1631 bundletype = other.local() and "HG10BZ" or "HG10UN"
1631 bundletype = other.local() and "HG10BZ" or "HG10UN"
1632 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1632 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1633 # keep written bundle?
1633 # keep written bundle?
1634 if opts["bundle"]:
1634 if opts["bundle"]:
1635 cleanup = None
1635 cleanup = None
1636 if not other.local():
1636 if not other.local():
1637 # use the created uncompressed bundlerepo
1637 # use the created uncompressed bundlerepo
1638 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1638 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1639
1639
1640 o = other.changelog.nodesbetween(incoming, revs)[0]
1640 o = other.changelog.nodesbetween(incoming, revs)[0]
1641 if opts['newest_first']:
1641 if opts['newest_first']:
1642 o.reverse()
1642 o.reverse()
1643 displayer = cmdutil.show_changeset(ui, other, opts)
1643 displayer = cmdutil.show_changeset(ui, other, opts)
1644 count = 0
1644 count = 0
1645 for n in o:
1645 for n in o:
1646 if count >= limit:
1646 if count >= limit:
1647 break
1647 break
1648 parents = [p for p in other.changelog.parents(n) if p != nullid]
1648 parents = [p for p in other.changelog.parents(n) if p != nullid]
1649 if opts['no_merges'] and len(parents) == 2:
1649 if opts['no_merges'] and len(parents) == 2:
1650 continue
1650 continue
1651 count += 1
1651 count += 1
1652 displayer.show(changenode=n)
1652 displayer.show(changenode=n)
1653 finally:
1653 finally:
1654 if hasattr(other, 'close'):
1654 if hasattr(other, 'close'):
1655 other.close()
1655 other.close()
1656 if cleanup:
1656 if cleanup:
1657 os.unlink(cleanup)
1657 os.unlink(cleanup)
1658
1658
1659 def init(ui, dest=".", **opts):
1659 def init(ui, dest=".", **opts):
1660 """create a new repository in the given directory
1660 """create a new repository in the given directory
1661
1661
1662 Initialize a new repository in the given directory. If the given
1662 Initialize a new repository in the given directory. If the given
1663 directory does not exist, it is created.
1663 directory does not exist, it is created.
1664
1664
1665 If no directory is given, the current directory is used.
1665 If no directory is given, the current directory is used.
1666
1666
1667 It is possible to specify an ssh:// URL as the destination.
1667 It is possible to specify an ssh:// URL as the destination.
1668 Look at the help text for the pull command for important details
1668 Look at the help text for the pull command for important details
1669 about ssh:// URLs.
1669 about ssh:// URLs.
1670 """
1670 """
1671 cmdutil.setremoteconfig(ui, opts)
1671 cmdutil.setremoteconfig(ui, opts)
1672 hg.repository(ui, dest, create=1)
1672 hg.repository(ui, dest, create=1)
1673
1673
1674 def locate(ui, repo, *pats, **opts):
1674 def locate(ui, repo, *pats, **opts):
1675 """locate files matching specific patterns
1675 """locate files matching specific patterns
1676
1676
1677 Print all files under Mercurial control whose names match the
1677 Print all files under Mercurial control whose names match the
1678 given patterns.
1678 given patterns.
1679
1679
1680 This command searches the entire repository by default. To search
1680 This command searches the entire repository by default. To search
1681 just the current directory and its subdirectories, use
1681 just the current directory and its subdirectories, use
1682 "--include .".
1682 "--include .".
1683
1683
1684 If no patterns are given to match, this command prints all file
1684 If no patterns are given to match, this command prints all file
1685 names.
1685 names.
1686
1686
1687 If you want to feed the output of this command into the "xargs"
1687 If you want to feed the output of this command into the "xargs"
1688 command, use the "-0" option to both this command and "xargs".
1688 command, use the "-0" option to both this command and "xargs".
1689 This will avoid the problem of "xargs" treating single filenames
1689 This will avoid the problem of "xargs" treating single filenames
1690 that contain white space as multiple filenames.
1690 that contain white space as multiple filenames.
1691 """
1691 """
1692 end = opts['print0'] and '\0' or '\n'
1692 end = opts['print0'] and '\0' or '\n'
1693 rev = opts['rev']
1693 rev = opts['rev']
1694 if rev:
1694 if rev:
1695 node = repo.lookup(rev)
1695 node = repo.lookup(rev)
1696 else:
1696 else:
1697 node = None
1697 node = None
1698
1698
1699 ret = 1
1699 ret = 1
1700 m = cmdutil.match(repo, pats, opts, default='relglob')
1700 m = cmdutil.match(repo, pats, opts, default='relglob')
1701 m.bad = lambda x,y: False
1701 m.bad = lambda x,y: False
1702 for src, abs in cmdutil.walk(repo, m, node):
1702 for src, abs in repo.walk(m, node):
1703 if not node and abs not in repo.dirstate:
1703 if not node and abs not in repo.dirstate:
1704 continue
1704 continue
1705 if opts['fullpath']:
1705 if opts['fullpath']:
1706 ui.write(os.path.join(repo.root, abs), end)
1706 ui.write(os.path.join(repo.root, abs), end)
1707 else:
1707 else:
1708 ui.write(((pats and m.rel(abs)) or abs), end)
1708 ui.write(((pats and m.rel(abs)) or abs), end)
1709 ret = 0
1709 ret = 0
1710
1710
1711 return ret
1711 return ret
1712
1712
1713 def log(ui, repo, *pats, **opts):
1713 def log(ui, repo, *pats, **opts):
1714 """show revision history of entire repository or files
1714 """show revision history of entire repository or files
1715
1715
1716 Print the revision history of the specified files or the entire
1716 Print the revision history of the specified files or the entire
1717 project.
1717 project.
1718
1718
1719 File history is shown without following rename or copy history of
1719 File history is shown without following rename or copy history of
1720 files. Use -f/--follow with a file name to follow history across
1720 files. Use -f/--follow with a file name to follow history across
1721 renames and copies. --follow without a file name will only show
1721 renames and copies. --follow without a file name will only show
1722 ancestors or descendants of the starting revision. --follow-first
1722 ancestors or descendants of the starting revision. --follow-first
1723 only follows the first parent of merge revisions.
1723 only follows the first parent of merge revisions.
1724
1724
1725 If no revision range is specified, the default is tip:0 unless
1725 If no revision range is specified, the default is tip:0 unless
1726 --follow is set, in which case the working directory parent is
1726 --follow is set, in which case the working directory parent is
1727 used as the starting revision.
1727 used as the starting revision.
1728
1728
1729 See 'hg help dates' for a list of formats valid for -d/--date.
1729 See 'hg help dates' for a list of formats valid for -d/--date.
1730
1730
1731 By default this command outputs: changeset id and hash, tags,
1731 By default this command outputs: changeset id and hash, tags,
1732 non-trivial parents, user, date and time, and a summary for each
1732 non-trivial parents, user, date and time, and a summary for each
1733 commit. When the -v/--verbose switch is used, the list of changed
1733 commit. When the -v/--verbose switch is used, the list of changed
1734 files and full commit message is shown.
1734 files and full commit message is shown.
1735
1735
1736 NOTE: log -p may generate unexpected diff output for merge
1736 NOTE: log -p may generate unexpected diff output for merge
1737 changesets, as it will compare the merge changeset against its
1737 changesets, as it will compare the merge changeset against its
1738 first parent only. Also, the files: list will only reflect files
1738 first parent only. Also, the files: list will only reflect files
1739 that are different from BOTH parents.
1739 that are different from BOTH parents.
1740
1740
1741 """
1741 """
1742
1742
1743 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1743 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1744 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1744 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1745
1745
1746 limit = cmdutil.loglimit(opts)
1746 limit = cmdutil.loglimit(opts)
1747 count = 0
1747 count = 0
1748
1748
1749 if opts['copies'] and opts['rev']:
1749 if opts['copies'] and opts['rev']:
1750 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1750 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1751 else:
1751 else:
1752 endrev = repo.changelog.count()
1752 endrev = repo.changelog.count()
1753 rcache = {}
1753 rcache = {}
1754 ncache = {}
1754 ncache = {}
1755 def getrenamed(fn, rev):
1755 def getrenamed(fn, rev):
1756 '''looks up all renames for a file (up to endrev) the first
1756 '''looks up all renames for a file (up to endrev) the first
1757 time the file is given. It indexes on the changerev and only
1757 time the file is given. It indexes on the changerev and only
1758 parses the manifest if linkrev != changerev.
1758 parses the manifest if linkrev != changerev.
1759 Returns rename info for fn at changerev rev.'''
1759 Returns rename info for fn at changerev rev.'''
1760 if fn not in rcache:
1760 if fn not in rcache:
1761 rcache[fn] = {}
1761 rcache[fn] = {}
1762 ncache[fn] = {}
1762 ncache[fn] = {}
1763 fl = repo.file(fn)
1763 fl = repo.file(fn)
1764 for i in xrange(fl.count()):
1764 for i in xrange(fl.count()):
1765 node = fl.node(i)
1765 node = fl.node(i)
1766 lr = fl.linkrev(node)
1766 lr = fl.linkrev(node)
1767 renamed = fl.renamed(node)
1767 renamed = fl.renamed(node)
1768 rcache[fn][lr] = renamed
1768 rcache[fn][lr] = renamed
1769 if renamed:
1769 if renamed:
1770 ncache[fn][node] = renamed
1770 ncache[fn][node] = renamed
1771 if lr >= endrev:
1771 if lr >= endrev:
1772 break
1772 break
1773 if rev in rcache[fn]:
1773 if rev in rcache[fn]:
1774 return rcache[fn][rev]
1774 return rcache[fn][rev]
1775
1775
1776 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1776 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1777 # filectx logic.
1777 # filectx logic.
1778
1778
1779 try:
1779 try:
1780 return repo.changectx(rev).filectx(fn).renamed()
1780 return repo.changectx(rev).filectx(fn).renamed()
1781 except revlog.LookupError:
1781 except revlog.LookupError:
1782 pass
1782 pass
1783 return None
1783 return None
1784
1784
1785 df = False
1785 df = False
1786 if opts["date"]:
1786 if opts["date"]:
1787 df = util.matchdate(opts["date"])
1787 df = util.matchdate(opts["date"])
1788
1788
1789 only_branches = opts['only_branch']
1789 only_branches = opts['only_branch']
1790
1790
1791 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1791 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1792 for st, rev, fns in changeiter:
1792 for st, rev, fns in changeiter:
1793 if st == 'add':
1793 if st == 'add':
1794 changenode = repo.changelog.node(rev)
1794 changenode = repo.changelog.node(rev)
1795 parents = [p for p in repo.changelog.parentrevs(rev)
1795 parents = [p for p in repo.changelog.parentrevs(rev)
1796 if p != nullrev]
1796 if p != nullrev]
1797 if opts['no_merges'] and len(parents) == 2:
1797 if opts['no_merges'] and len(parents) == 2:
1798 continue
1798 continue
1799 if opts['only_merges'] and len(parents) != 2:
1799 if opts['only_merges'] and len(parents) != 2:
1800 continue
1800 continue
1801
1801
1802 if only_branches:
1802 if only_branches:
1803 revbranch = get(rev)[5]['branch']
1803 revbranch = get(rev)[5]['branch']
1804 if revbranch not in only_branches:
1804 if revbranch not in only_branches:
1805 continue
1805 continue
1806
1806
1807 if df:
1807 if df:
1808 changes = get(rev)
1808 changes = get(rev)
1809 if not df(changes[2][0]):
1809 if not df(changes[2][0]):
1810 continue
1810 continue
1811
1811
1812 if opts['keyword']:
1812 if opts['keyword']:
1813 changes = get(rev)
1813 changes = get(rev)
1814 miss = 0
1814 miss = 0
1815 for k in [kw.lower() for kw in opts['keyword']]:
1815 for k in [kw.lower() for kw in opts['keyword']]:
1816 if not (k in changes[1].lower() or
1816 if not (k in changes[1].lower() or
1817 k in changes[4].lower() or
1817 k in changes[4].lower() or
1818 k in " ".join(changes[3]).lower()):
1818 k in " ".join(changes[3]).lower()):
1819 miss = 1
1819 miss = 1
1820 break
1820 break
1821 if miss:
1821 if miss:
1822 continue
1822 continue
1823
1823
1824 copies = []
1824 copies = []
1825 if opts.get('copies') and rev:
1825 if opts.get('copies') and rev:
1826 for fn in get(rev)[3]:
1826 for fn in get(rev)[3]:
1827 rename = getrenamed(fn, rev)
1827 rename = getrenamed(fn, rev)
1828 if rename:
1828 if rename:
1829 copies.append((fn, rename[0]))
1829 copies.append((fn, rename[0]))
1830 displayer.show(rev, changenode, copies=copies)
1830 displayer.show(rev, changenode, copies=copies)
1831 elif st == 'iter':
1831 elif st == 'iter':
1832 if count == limit: break
1832 if count == limit: break
1833 if displayer.flush(rev):
1833 if displayer.flush(rev):
1834 count += 1
1834 count += 1
1835
1835
1836 def manifest(ui, repo, node=None, rev=None):
1836 def manifest(ui, repo, node=None, rev=None):
1837 """output the current or given revision of the project manifest
1837 """output the current or given revision of the project manifest
1838
1838
1839 Print a list of version controlled files for the given revision.
1839 Print a list of version controlled files for the given revision.
1840 If no revision is given, the parent of the working directory is used,
1840 If no revision is given, the parent of the working directory is used,
1841 or tip if no revision is checked out.
1841 or tip if no revision is checked out.
1842
1842
1843 The manifest is the list of files being version controlled. If no revision
1843 The manifest is the list of files being version controlled. If no revision
1844 is given then the first parent of the working directory is used.
1844 is given then the first parent of the working directory is used.
1845
1845
1846 With -v flag, print file permissions, symlink and executable bits. With
1846 With -v flag, print file permissions, symlink and executable bits. With
1847 --debug flag, print file revision hashes.
1847 --debug flag, print file revision hashes.
1848 """
1848 """
1849
1849
1850 if rev and node:
1850 if rev and node:
1851 raise util.Abort(_("please specify just one revision"))
1851 raise util.Abort(_("please specify just one revision"))
1852
1852
1853 if not node:
1853 if not node:
1854 node = rev
1854 node = rev
1855
1855
1856 m = repo.changectx(node).manifest()
1856 m = repo.changectx(node).manifest()
1857 files = m.keys()
1857 files = m.keys()
1858 files.sort()
1858 files.sort()
1859
1859
1860 for f in files:
1860 for f in files:
1861 if ui.debugflag:
1861 if ui.debugflag:
1862 ui.write("%40s " % hex(m[f]))
1862 ui.write("%40s " % hex(m[f]))
1863 if ui.verbose:
1863 if ui.verbose:
1864 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1864 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1865 perm = m.execf(f) and "755" or "644"
1865 perm = m.execf(f) and "755" or "644"
1866 ui.write("%3s %1s " % (perm, type))
1866 ui.write("%3s %1s " % (perm, type))
1867 ui.write("%s\n" % f)
1867 ui.write("%s\n" % f)
1868
1868
1869 def merge(ui, repo, node=None, force=None, rev=None):
1869 def merge(ui, repo, node=None, force=None, rev=None):
1870 """merge working directory with another revision
1870 """merge working directory with another revision
1871
1871
1872 Merge the contents of the current working directory and the
1872 Merge the contents of the current working directory and the
1873 requested revision. Files that changed between either parent are
1873 requested revision. Files that changed between either parent are
1874 marked as changed for the next commit and a commit must be
1874 marked as changed for the next commit and a commit must be
1875 performed before any further updates are allowed.
1875 performed before any further updates are allowed.
1876
1876
1877 If no revision is specified, the working directory's parent is a
1877 If no revision is specified, the working directory's parent is a
1878 head revision, and the repository contains exactly one other head,
1878 head revision, and the repository contains exactly one other head,
1879 the other head is merged with by default. Otherwise, an explicit
1879 the other head is merged with by default. Otherwise, an explicit
1880 revision to merge with must be provided.
1880 revision to merge with must be provided.
1881 """
1881 """
1882
1882
1883 if rev and node:
1883 if rev and node:
1884 raise util.Abort(_("please specify just one revision"))
1884 raise util.Abort(_("please specify just one revision"))
1885 if not node:
1885 if not node:
1886 node = rev
1886 node = rev
1887
1887
1888 if not node:
1888 if not node:
1889 heads = repo.heads()
1889 heads = repo.heads()
1890 if len(heads) > 2:
1890 if len(heads) > 2:
1891 raise util.Abort(_('repo has %d heads - '
1891 raise util.Abort(_('repo has %d heads - '
1892 'please merge with an explicit rev') %
1892 'please merge with an explicit rev') %
1893 len(heads))
1893 len(heads))
1894 parent = repo.dirstate.parents()[0]
1894 parent = repo.dirstate.parents()[0]
1895 if len(heads) == 1:
1895 if len(heads) == 1:
1896 msg = _('there is nothing to merge')
1896 msg = _('there is nothing to merge')
1897 if parent != repo.lookup(repo.workingctx().branch()):
1897 if parent != repo.lookup(repo.workingctx().branch()):
1898 msg = _('%s - use "hg update" instead') % msg
1898 msg = _('%s - use "hg update" instead') % msg
1899 raise util.Abort(msg)
1899 raise util.Abort(msg)
1900
1900
1901 if parent not in heads:
1901 if parent not in heads:
1902 raise util.Abort(_('working dir not at a head rev - '
1902 raise util.Abort(_('working dir not at a head rev - '
1903 'use "hg update" or merge with an explicit rev'))
1903 'use "hg update" or merge with an explicit rev'))
1904 node = parent == heads[0] and heads[-1] or heads[0]
1904 node = parent == heads[0] and heads[-1] or heads[0]
1905 return hg.merge(repo, node, force=force)
1905 return hg.merge(repo, node, force=force)
1906
1906
1907 def outgoing(ui, repo, dest=None, **opts):
1907 def outgoing(ui, repo, dest=None, **opts):
1908 """show changesets not found in destination
1908 """show changesets not found in destination
1909
1909
1910 Show changesets not found in the specified destination repository or
1910 Show changesets not found in the specified destination repository or
1911 the default push location. These are the changesets that would be pushed
1911 the default push location. These are the changesets that would be pushed
1912 if a push was requested.
1912 if a push was requested.
1913
1913
1914 See pull for valid destination format details.
1914 See pull for valid destination format details.
1915 """
1915 """
1916 limit = cmdutil.loglimit(opts)
1916 limit = cmdutil.loglimit(opts)
1917 dest, revs, checkout = hg.parseurl(
1917 dest, revs, checkout = hg.parseurl(
1918 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1918 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1919 cmdutil.setremoteconfig(ui, opts)
1919 cmdutil.setremoteconfig(ui, opts)
1920 if revs:
1920 if revs:
1921 revs = [repo.lookup(rev) for rev in revs]
1921 revs = [repo.lookup(rev) for rev in revs]
1922
1922
1923 other = hg.repository(ui, dest)
1923 other = hg.repository(ui, dest)
1924 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1924 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1925 o = repo.findoutgoing(other, force=opts['force'])
1925 o = repo.findoutgoing(other, force=opts['force'])
1926 if not o:
1926 if not o:
1927 ui.status(_("no changes found\n"))
1927 ui.status(_("no changes found\n"))
1928 return 1
1928 return 1
1929 o = repo.changelog.nodesbetween(o, revs)[0]
1929 o = repo.changelog.nodesbetween(o, revs)[0]
1930 if opts['newest_first']:
1930 if opts['newest_first']:
1931 o.reverse()
1931 o.reverse()
1932 displayer = cmdutil.show_changeset(ui, repo, opts)
1932 displayer = cmdutil.show_changeset(ui, repo, opts)
1933 count = 0
1933 count = 0
1934 for n in o:
1934 for n in o:
1935 if count >= limit:
1935 if count >= limit:
1936 break
1936 break
1937 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1937 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1938 if opts['no_merges'] and len(parents) == 2:
1938 if opts['no_merges'] and len(parents) == 2:
1939 continue
1939 continue
1940 count += 1
1940 count += 1
1941 displayer.show(changenode=n)
1941 displayer.show(changenode=n)
1942
1942
1943 def parents(ui, repo, file_=None, **opts):
1943 def parents(ui, repo, file_=None, **opts):
1944 """show the parents of the working dir or revision
1944 """show the parents of the working dir or revision
1945
1945
1946 Print the working directory's parent revisions. If a
1946 Print the working directory's parent revisions. If a
1947 revision is given via --rev, the parent of that revision
1947 revision is given via --rev, the parent of that revision
1948 will be printed. If a file argument is given, revision in
1948 will be printed. If a file argument is given, revision in
1949 which the file was last changed (before the working directory
1949 which the file was last changed (before the working directory
1950 revision or the argument to --rev if given) is printed.
1950 revision or the argument to --rev if given) is printed.
1951 """
1951 """
1952 rev = opts.get('rev')
1952 rev = opts.get('rev')
1953 if rev:
1953 if rev:
1954 ctx = repo.changectx(rev)
1954 ctx = repo.changectx(rev)
1955 else:
1955 else:
1956 ctx = repo.workingctx()
1956 ctx = repo.workingctx()
1957
1957
1958 if file_:
1958 if file_:
1959 m = cmdutil.match(repo, (file_,), opts)
1959 m = cmdutil.match(repo, (file_,), opts)
1960 if m.anypats() or len(m.files()) != 1:
1960 if m.anypats() or len(m.files()) != 1:
1961 raise util.Abort(_('can only specify an explicit file name'))
1961 raise util.Abort(_('can only specify an explicit file name'))
1962 file_ = m.files()[0]
1962 file_ = m.files()[0]
1963 filenodes = []
1963 filenodes = []
1964 for cp in ctx.parents():
1964 for cp in ctx.parents():
1965 if not cp:
1965 if not cp:
1966 continue
1966 continue
1967 try:
1967 try:
1968 filenodes.append(cp.filenode(file_))
1968 filenodes.append(cp.filenode(file_))
1969 except revlog.LookupError:
1969 except revlog.LookupError:
1970 pass
1970 pass
1971 if not filenodes:
1971 if not filenodes:
1972 raise util.Abort(_("'%s' not found in manifest!") % file_)
1972 raise util.Abort(_("'%s' not found in manifest!") % file_)
1973 fl = repo.file(file_)
1973 fl = repo.file(file_)
1974 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1974 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1975 else:
1975 else:
1976 p = [cp.node() for cp in ctx.parents()]
1976 p = [cp.node() for cp in ctx.parents()]
1977
1977
1978 displayer = cmdutil.show_changeset(ui, repo, opts)
1978 displayer = cmdutil.show_changeset(ui, repo, opts)
1979 for n in p:
1979 for n in p:
1980 if n != nullid:
1980 if n != nullid:
1981 displayer.show(changenode=n)
1981 displayer.show(changenode=n)
1982
1982
1983 def paths(ui, repo, search=None):
1983 def paths(ui, repo, search=None):
1984 """show definition of symbolic path names
1984 """show definition of symbolic path names
1985
1985
1986 Show definition of symbolic path name NAME. If no name is given, show
1986 Show definition of symbolic path name NAME. If no name is given, show
1987 definition of available names.
1987 definition of available names.
1988
1988
1989 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1989 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1990 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1990 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1991 """
1991 """
1992 if search:
1992 if search:
1993 for name, path in ui.configitems("paths"):
1993 for name, path in ui.configitems("paths"):
1994 if name == search:
1994 if name == search:
1995 ui.write("%s\n" % util.hidepassword(path))
1995 ui.write("%s\n" % util.hidepassword(path))
1996 return
1996 return
1997 ui.warn(_("not found!\n"))
1997 ui.warn(_("not found!\n"))
1998 return 1
1998 return 1
1999 else:
1999 else:
2000 for name, path in ui.configitems("paths"):
2000 for name, path in ui.configitems("paths"):
2001 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2001 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2002
2002
2003 def postincoming(ui, repo, modheads, optupdate, checkout):
2003 def postincoming(ui, repo, modheads, optupdate, checkout):
2004 if modheads == 0:
2004 if modheads == 0:
2005 return
2005 return
2006 if optupdate:
2006 if optupdate:
2007 if modheads <= 1 or checkout:
2007 if modheads <= 1 or checkout:
2008 return hg.update(repo, checkout)
2008 return hg.update(repo, checkout)
2009 else:
2009 else:
2010 ui.status(_("not updating, since new heads added\n"))
2010 ui.status(_("not updating, since new heads added\n"))
2011 if modheads > 1:
2011 if modheads > 1:
2012 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2012 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2013 else:
2013 else:
2014 ui.status(_("(run 'hg update' to get a working copy)\n"))
2014 ui.status(_("(run 'hg update' to get a working copy)\n"))
2015
2015
2016 def pull(ui, repo, source="default", **opts):
2016 def pull(ui, repo, source="default", **opts):
2017 """pull changes from the specified source
2017 """pull changes from the specified source
2018
2018
2019 Pull changes from a remote repository to a local one.
2019 Pull changes from a remote repository to a local one.
2020
2020
2021 This finds all changes from the repository at the specified path
2021 This finds all changes from the repository at the specified path
2022 or URL and adds them to the local repository. By default, this
2022 or URL and adds them to the local repository. By default, this
2023 does not update the copy of the project in the working directory.
2023 does not update the copy of the project in the working directory.
2024
2024
2025 Valid URLs are of the form:
2025 Valid URLs are of the form:
2026
2026
2027 local/filesystem/path (or file://local/filesystem/path)
2027 local/filesystem/path (or file://local/filesystem/path)
2028 http://[user@]host[:port]/[path]
2028 http://[user@]host[:port]/[path]
2029 https://[user@]host[:port]/[path]
2029 https://[user@]host[:port]/[path]
2030 ssh://[user@]host[:port]/[path]
2030 ssh://[user@]host[:port]/[path]
2031 static-http://host[:port]/[path]
2031 static-http://host[:port]/[path]
2032
2032
2033 Paths in the local filesystem can either point to Mercurial
2033 Paths in the local filesystem can either point to Mercurial
2034 repositories or to bundle files (as created by 'hg bundle' or
2034 repositories or to bundle files (as created by 'hg bundle' or
2035 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2035 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2036 allows access to a Mercurial repository where you simply use a web
2036 allows access to a Mercurial repository where you simply use a web
2037 server to publish the .hg directory as static content.
2037 server to publish the .hg directory as static content.
2038
2038
2039 An optional identifier after # indicates a particular branch, tag,
2039 An optional identifier after # indicates a particular branch, tag,
2040 or changeset to pull.
2040 or changeset to pull.
2041
2041
2042 Some notes about using SSH with Mercurial:
2042 Some notes about using SSH with Mercurial:
2043 - SSH requires an accessible shell account on the destination machine
2043 - SSH requires an accessible shell account on the destination machine
2044 and a copy of hg in the remote path or specified with as remotecmd.
2044 and a copy of hg in the remote path or specified with as remotecmd.
2045 - path is relative to the remote user's home directory by default.
2045 - path is relative to the remote user's home directory by default.
2046 Use an extra slash at the start of a path to specify an absolute path:
2046 Use an extra slash at the start of a path to specify an absolute path:
2047 ssh://example.com//tmp/repository
2047 ssh://example.com//tmp/repository
2048 - Mercurial doesn't use its own compression via SSH; the right thing
2048 - Mercurial doesn't use its own compression via SSH; the right thing
2049 to do is to configure it in your ~/.ssh/config, e.g.:
2049 to do is to configure it in your ~/.ssh/config, e.g.:
2050 Host *.mylocalnetwork.example.com
2050 Host *.mylocalnetwork.example.com
2051 Compression no
2051 Compression no
2052 Host *
2052 Host *
2053 Compression yes
2053 Compression yes
2054 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2054 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2055 with the --ssh command line option.
2055 with the --ssh command line option.
2056 """
2056 """
2057 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2057 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2058 cmdutil.setremoteconfig(ui, opts)
2058 cmdutil.setremoteconfig(ui, opts)
2059
2059
2060 other = hg.repository(ui, source)
2060 other = hg.repository(ui, source)
2061 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2061 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2062 if revs:
2062 if revs:
2063 try:
2063 try:
2064 revs = [other.lookup(rev) for rev in revs]
2064 revs = [other.lookup(rev) for rev in revs]
2065 except NoCapability:
2065 except NoCapability:
2066 error = _("Other repository doesn't support revision lookup, "
2066 error = _("Other repository doesn't support revision lookup, "
2067 "so a rev cannot be specified.")
2067 "so a rev cannot be specified.")
2068 raise util.Abort(error)
2068 raise util.Abort(error)
2069
2069
2070 modheads = repo.pull(other, heads=revs, force=opts['force'])
2070 modheads = repo.pull(other, heads=revs, force=opts['force'])
2071 return postincoming(ui, repo, modheads, opts['update'], checkout)
2071 return postincoming(ui, repo, modheads, opts['update'], checkout)
2072
2072
2073 def push(ui, repo, dest=None, **opts):
2073 def push(ui, repo, dest=None, **opts):
2074 """push changes to the specified destination
2074 """push changes to the specified destination
2075
2075
2076 Push changes from the local repository to the given destination.
2076 Push changes from the local repository to the given destination.
2077
2077
2078 This is the symmetrical operation for pull. It helps to move
2078 This is the symmetrical operation for pull. It helps to move
2079 changes from the current repository to a different one. If the
2079 changes from the current repository to a different one. If the
2080 destination is local this is identical to a pull in that directory
2080 destination is local this is identical to a pull in that directory
2081 from the current one.
2081 from the current one.
2082
2082
2083 By default, push will refuse to run if it detects the result would
2083 By default, push will refuse to run if it detects the result would
2084 increase the number of remote heads. This generally indicates the
2084 increase the number of remote heads. This generally indicates the
2085 the client has forgotten to sync and merge before pushing.
2085 the client has forgotten to sync and merge before pushing.
2086
2086
2087 Valid URLs are of the form:
2087 Valid URLs are of the form:
2088
2088
2089 local/filesystem/path (or file://local/filesystem/path)
2089 local/filesystem/path (or file://local/filesystem/path)
2090 ssh://[user@]host[:port]/[path]
2090 ssh://[user@]host[:port]/[path]
2091 http://[user@]host[:port]/[path]
2091 http://[user@]host[:port]/[path]
2092 https://[user@]host[:port]/[path]
2092 https://[user@]host[:port]/[path]
2093
2093
2094 An optional identifier after # indicates a particular branch, tag,
2094 An optional identifier after # indicates a particular branch, tag,
2095 or changeset to push.
2095 or changeset to push.
2096
2096
2097 Look at the help text for the pull command for important details
2097 Look at the help text for the pull command for important details
2098 about ssh:// URLs.
2098 about ssh:// URLs.
2099
2099
2100 Pushing to http:// and https:// URLs is only possible, if this
2100 Pushing to http:// and https:// URLs is only possible, if this
2101 feature is explicitly enabled on the remote Mercurial server.
2101 feature is explicitly enabled on the remote Mercurial server.
2102 """
2102 """
2103 dest, revs, checkout = hg.parseurl(
2103 dest, revs, checkout = hg.parseurl(
2104 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2104 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2105 cmdutil.setremoteconfig(ui, opts)
2105 cmdutil.setremoteconfig(ui, opts)
2106
2106
2107 other = hg.repository(ui, dest)
2107 other = hg.repository(ui, dest)
2108 ui.status('pushing to %s\n' % util.hidepassword(dest))
2108 ui.status('pushing to %s\n' % util.hidepassword(dest))
2109 if revs:
2109 if revs:
2110 revs = [repo.lookup(rev) for rev in revs]
2110 revs = [repo.lookup(rev) for rev in revs]
2111 r = repo.push(other, opts['force'], revs=revs)
2111 r = repo.push(other, opts['force'], revs=revs)
2112 return r == 0
2112 return r == 0
2113
2113
2114 def rawcommit(ui, repo, *pats, **opts):
2114 def rawcommit(ui, repo, *pats, **opts):
2115 """raw commit interface (DEPRECATED)
2115 """raw commit interface (DEPRECATED)
2116
2116
2117 (DEPRECATED)
2117 (DEPRECATED)
2118 Lowlevel commit, for use in helper scripts.
2118 Lowlevel commit, for use in helper scripts.
2119
2119
2120 This command is not intended to be used by normal users, as it is
2120 This command is not intended to be used by normal users, as it is
2121 primarily useful for importing from other SCMs.
2121 primarily useful for importing from other SCMs.
2122
2122
2123 This command is now deprecated and will be removed in a future
2123 This command is now deprecated and will be removed in a future
2124 release, please use debugsetparents and commit instead.
2124 release, please use debugsetparents and commit instead.
2125 """
2125 """
2126
2126
2127 ui.warn(_("(the rawcommit command is deprecated)\n"))
2127 ui.warn(_("(the rawcommit command is deprecated)\n"))
2128
2128
2129 message = cmdutil.logmessage(opts)
2129 message = cmdutil.logmessage(opts)
2130
2130
2131 files = cmdutil.match(repo, pats, opts).files()
2131 files = cmdutil.match(repo, pats, opts).files()
2132 if opts['files']:
2132 if opts['files']:
2133 files += open(opts['files']).read().splitlines()
2133 files += open(opts['files']).read().splitlines()
2134
2134
2135 parents = [repo.lookup(p) for p in opts['parent']]
2135 parents = [repo.lookup(p) for p in opts['parent']]
2136
2136
2137 try:
2137 try:
2138 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2138 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2139 except ValueError, inst:
2139 except ValueError, inst:
2140 raise util.Abort(str(inst))
2140 raise util.Abort(str(inst))
2141
2141
2142 def recover(ui, repo):
2142 def recover(ui, repo):
2143 """roll back an interrupted transaction
2143 """roll back an interrupted transaction
2144
2144
2145 Recover from an interrupted commit or pull.
2145 Recover from an interrupted commit or pull.
2146
2146
2147 This command tries to fix the repository status after an interrupted
2147 This command tries to fix the repository status after an interrupted
2148 operation. It should only be necessary when Mercurial suggests it.
2148 operation. It should only be necessary when Mercurial suggests it.
2149 """
2149 """
2150 if repo.recover():
2150 if repo.recover():
2151 return hg.verify(repo)
2151 return hg.verify(repo)
2152 return 1
2152 return 1
2153
2153
2154 def remove(ui, repo, *pats, **opts):
2154 def remove(ui, repo, *pats, **opts):
2155 """remove the specified files on the next commit
2155 """remove the specified files on the next commit
2156
2156
2157 Schedule the indicated files for removal from the repository.
2157 Schedule the indicated files for removal from the repository.
2158
2158
2159 This only removes files from the current branch, not from the entire
2159 This only removes files from the current branch, not from the entire
2160 project history. -A can be used to remove only files that have already
2160 project history. -A can be used to remove only files that have already
2161 been deleted, -f can be used to force deletion, and -Af can be used
2161 been deleted, -f can be used to force deletion, and -Af can be used
2162 to remove files from the next revision without deleting them.
2162 to remove files from the next revision without deleting them.
2163
2163
2164 The following table details the behavior of remove for different file
2164 The following table details the behavior of remove for different file
2165 states (columns) and option combinations (rows). The file states are
2165 states (columns) and option combinations (rows). The file states are
2166 Added, Clean, Modified and Missing (as reported by hg status). The
2166 Added, Clean, Modified and Missing (as reported by hg status). The
2167 actions are Warn, Remove (from branch) and Delete (from disk).
2167 actions are Warn, Remove (from branch) and Delete (from disk).
2168
2168
2169 A C M !
2169 A C M !
2170 none W RD W R
2170 none W RD W R
2171 -f R RD RD R
2171 -f R RD RD R
2172 -A W W W R
2172 -A W W W R
2173 -Af R R R R
2173 -Af R R R R
2174
2174
2175 This command schedules the files to be removed at the next commit.
2175 This command schedules the files to be removed at the next commit.
2176 To undo a remove before that, see hg revert.
2176 To undo a remove before that, see hg revert.
2177 """
2177 """
2178
2178
2179 after, force = opts.get('after'), opts.get('force')
2179 after, force = opts.get('after'), opts.get('force')
2180 if not pats and not after:
2180 if not pats and not after:
2181 raise util.Abort(_('no files specified'))
2181 raise util.Abort(_('no files specified'))
2182
2182
2183 m = cmdutil.match(repo, pats, opts)
2183 m = cmdutil.match(repo, pats, opts)
2184 mardu = map(dict.fromkeys, repo.status(files=m.files(), match=m))[:5]
2184 mardu = map(dict.fromkeys, repo.status(files=m.files(), match=m))[:5]
2185 modified, added, removed, deleted, unknown = mardu
2185 modified, added, removed, deleted, unknown = mardu
2186
2186
2187 remove, forget = [], []
2187 remove, forget = [], []
2188 for src, abs in cmdutil.walk(repo, m):
2188 for src, abs in repo.walk(m):
2189
2189
2190 reason = None
2190 reason = None
2191 if abs in removed or abs in unknown:
2191 if abs in removed or abs in unknown:
2192 continue
2192 continue
2193
2193
2194 # last column
2194 # last column
2195 elif abs in deleted:
2195 elif abs in deleted:
2196 remove.append(abs)
2196 remove.append(abs)
2197
2197
2198 # rest of the third row
2198 # rest of the third row
2199 elif after and not force:
2199 elif after and not force:
2200 reason = _('still exists (use -f to force removal)')
2200 reason = _('still exists (use -f to force removal)')
2201
2201
2202 # rest of the first column
2202 # rest of the first column
2203 elif abs in added:
2203 elif abs in added:
2204 if not force:
2204 if not force:
2205 reason = _('has been marked for add (use -f to force removal)')
2205 reason = _('has been marked for add (use -f to force removal)')
2206 else:
2206 else:
2207 forget.append(abs)
2207 forget.append(abs)
2208
2208
2209 # rest of the third column
2209 # rest of the third column
2210 elif abs in modified:
2210 elif abs in modified:
2211 if not force:
2211 if not force:
2212 reason = _('is modified (use -f to force removal)')
2212 reason = _('is modified (use -f to force removal)')
2213 else:
2213 else:
2214 remove.append(abs)
2214 remove.append(abs)
2215
2215
2216 # rest of the second column
2216 # rest of the second column
2217 elif not reason:
2217 elif not reason:
2218 remove.append(abs)
2218 remove.append(abs)
2219
2219
2220 if reason:
2220 if reason:
2221 ui.warn(_('not removing %s: file %s\n') % (m.rel(abs), reason))
2221 ui.warn(_('not removing %s: file %s\n') % (m.rel(abs), reason))
2222 elif ui.verbose or not m.exact(abs):
2222 elif ui.verbose or not m.exact(abs):
2223 ui.status(_('removing %s\n') % m.rel(abs))
2223 ui.status(_('removing %s\n') % m.rel(abs))
2224
2224
2225 repo.forget(forget)
2225 repo.forget(forget)
2226 repo.remove(remove, unlink=not after)
2226 repo.remove(remove, unlink=not after)
2227
2227
2228 def rename(ui, repo, *pats, **opts):
2228 def rename(ui, repo, *pats, **opts):
2229 """rename files; equivalent of copy + remove
2229 """rename files; equivalent of copy + remove
2230
2230
2231 Mark dest as copies of sources; mark sources for deletion. If
2231 Mark dest as copies of sources; mark sources for deletion. If
2232 dest is a directory, copies are put in that directory. If dest is
2232 dest is a directory, copies are put in that directory. If dest is
2233 a file, there can only be one source.
2233 a file, there can only be one source.
2234
2234
2235 By default, this command copies the contents of files as they
2235 By default, this command copies the contents of files as they
2236 stand in the working directory. If invoked with --after, the
2236 stand in the working directory. If invoked with --after, the
2237 operation is recorded, but no copying is performed.
2237 operation is recorded, but no copying is performed.
2238
2238
2239 This command takes effect in the next commit. To undo a rename
2239 This command takes effect in the next commit. To undo a rename
2240 before that, see hg revert.
2240 before that, see hg revert.
2241 """
2241 """
2242 wlock = repo.wlock(False)
2242 wlock = repo.wlock(False)
2243 try:
2243 try:
2244 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2244 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2245 finally:
2245 finally:
2246 del wlock
2246 del wlock
2247
2247
2248 def resolve(ui, repo, *pats, **opts):
2248 def resolve(ui, repo, *pats, **opts):
2249 """resolve file merges from a branch merge or update
2249 """resolve file merges from a branch merge or update
2250
2250
2251 This command will attempt to resolve unresolved merges from the
2251 This command will attempt to resolve unresolved merges from the
2252 last update or merge command. This will use the local file
2252 last update or merge command. This will use the local file
2253 revision preserved at the last update or merge to cleanly retry
2253 revision preserved at the last update or merge to cleanly retry
2254 the file merge attempt. With no file or options specified, this
2254 the file merge attempt. With no file or options specified, this
2255 command will attempt to resolve all unresolved files.
2255 command will attempt to resolve all unresolved files.
2256 """
2256 """
2257
2257
2258 if len([x for x in opts if opts[x]]) > 1:
2258 if len([x for x in opts if opts[x]]) > 1:
2259 raise util.Abort(_("too many options specified"))
2259 raise util.Abort(_("too many options specified"))
2260
2260
2261 ms = merge_.mergestate(repo)
2261 ms = merge_.mergestate(repo)
2262 mf = util.matcher(repo.root, "", pats, [], [])[1]
2262 mf = util.matcher(repo.root, "", pats, [], [])[1]
2263
2263
2264 for f in ms:
2264 for f in ms:
2265 if mf(f):
2265 if mf(f):
2266 if opts.get("list"):
2266 if opts.get("list"):
2267 ui.write("%s %s\n" % (ms[f].upper(), f))
2267 ui.write("%s %s\n" % (ms[f].upper(), f))
2268 elif opts.get("mark"):
2268 elif opts.get("mark"):
2269 ms.mark(f, "r")
2269 ms.mark(f, "r")
2270 elif opts.get("unmark"):
2270 elif opts.get("unmark"):
2271 ms.mark(f, "u")
2271 ms.mark(f, "u")
2272 else:
2272 else:
2273 wctx = repo.workingctx()
2273 wctx = repo.workingctx()
2274 mctx = wctx.parents()[-1]
2274 mctx = wctx.parents()[-1]
2275 ms.resolve(f, wctx, mctx)
2275 ms.resolve(f, wctx, mctx)
2276
2276
2277 def revert(ui, repo, *pats, **opts):
2277 def revert(ui, repo, *pats, **opts):
2278 """restore individual files or dirs to an earlier state
2278 """restore individual files or dirs to an earlier state
2279
2279
2280 (use update -r to check out earlier revisions, revert does not
2280 (use update -r to check out earlier revisions, revert does not
2281 change the working dir parents)
2281 change the working dir parents)
2282
2282
2283 With no revision specified, revert the named files or directories
2283 With no revision specified, revert the named files or directories
2284 to the contents they had in the parent of the working directory.
2284 to the contents they had in the parent of the working directory.
2285 This restores the contents of the affected files to an unmodified
2285 This restores the contents of the affected files to an unmodified
2286 state and unschedules adds, removes, copies, and renames. If the
2286 state and unschedules adds, removes, copies, and renames. If the
2287 working directory has two parents, you must explicitly specify the
2287 working directory has two parents, you must explicitly specify the
2288 revision to revert to.
2288 revision to revert to.
2289
2289
2290 Using the -r option, revert the given files or directories to their
2290 Using the -r option, revert the given files or directories to their
2291 contents as of a specific revision. This can be helpful to "roll
2291 contents as of a specific revision. This can be helpful to "roll
2292 back" some or all of an earlier change.
2292 back" some or all of an earlier change.
2293 See 'hg help dates' for a list of formats valid for -d/--date.
2293 See 'hg help dates' for a list of formats valid for -d/--date.
2294
2294
2295 Revert modifies the working directory. It does not commit any
2295 Revert modifies the working directory. It does not commit any
2296 changes, or change the parent of the working directory. If you
2296 changes, or change the parent of the working directory. If you
2297 revert to a revision other than the parent of the working
2297 revert to a revision other than the parent of the working
2298 directory, the reverted files will thus appear modified
2298 directory, the reverted files will thus appear modified
2299 afterwards.
2299 afterwards.
2300
2300
2301 If a file has been deleted, it is restored. If the executable
2301 If a file has been deleted, it is restored. If the executable
2302 mode of a file was changed, it is reset.
2302 mode of a file was changed, it is reset.
2303
2303
2304 If names are given, all files matching the names are reverted.
2304 If names are given, all files matching the names are reverted.
2305 If no arguments are given, no files are reverted.
2305 If no arguments are given, no files are reverted.
2306
2306
2307 Modified files are saved with a .orig suffix before reverting.
2307 Modified files are saved with a .orig suffix before reverting.
2308 To disable these backups, use --no-backup.
2308 To disable these backups, use --no-backup.
2309 """
2309 """
2310
2310
2311 if opts["date"]:
2311 if opts["date"]:
2312 if opts["rev"]:
2312 if opts["rev"]:
2313 raise util.Abort(_("you can't specify a revision and a date"))
2313 raise util.Abort(_("you can't specify a revision and a date"))
2314 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2314 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2315
2315
2316 if not pats and not opts['all']:
2316 if not pats and not opts['all']:
2317 raise util.Abort(_('no files or directories specified; '
2317 raise util.Abort(_('no files or directories specified; '
2318 'use --all to revert the whole repo'))
2318 'use --all to revert the whole repo'))
2319
2319
2320 parent, p2 = repo.dirstate.parents()
2320 parent, p2 = repo.dirstate.parents()
2321 if not opts['rev'] and p2 != nullid:
2321 if not opts['rev'] and p2 != nullid:
2322 raise util.Abort(_('uncommitted merge - please provide a '
2322 raise util.Abort(_('uncommitted merge - please provide a '
2323 'specific revision'))
2323 'specific revision'))
2324 ctx = repo.changectx(opts['rev'])
2324 ctx = repo.changectx(opts['rev'])
2325 node = ctx.node()
2325 node = ctx.node()
2326 mf = ctx.manifest()
2326 mf = ctx.manifest()
2327 if node == parent:
2327 if node == parent:
2328 pmf = mf
2328 pmf = mf
2329 else:
2329 else:
2330 pmf = None
2330 pmf = None
2331
2331
2332 # need all matching names in dirstate and manifest of target rev,
2332 # need all matching names in dirstate and manifest of target rev,
2333 # so have to walk both. do not print errors if files exist in one
2333 # so have to walk both. do not print errors if files exist in one
2334 # but not other.
2334 # but not other.
2335
2335
2336 names = {}
2336 names = {}
2337
2337
2338 wlock = repo.wlock()
2338 wlock = repo.wlock()
2339 try:
2339 try:
2340 # walk dirstate.
2340 # walk dirstate.
2341 files = []
2341 files = []
2342
2342
2343 m = cmdutil.match(repo, pats, opts)
2343 m = cmdutil.match(repo, pats, opts)
2344 m.bad = lambda x,y: False
2344 m.bad = lambda x,y: False
2345 for src, abs in cmdutil.walk(repo, m):
2345 for src, abs in repo.walk(m):
2346 names[abs] = m.rel(abs), m.exact(abs)
2346 names[abs] = m.rel(abs), m.exact(abs)
2347
2347
2348 # walk target manifest.
2348 # walk target manifest.
2349
2349
2350 def badfn(path, msg):
2350 def badfn(path, msg):
2351 if path in names:
2351 if path in names:
2352 return False
2352 return False
2353 path_ = path + '/'
2353 path_ = path + '/'
2354 for f in names:
2354 for f in names:
2355 if f.startswith(path_):
2355 if f.startswith(path_):
2356 return False
2356 return False
2357 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2357 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2358 return False
2358 return False
2359
2359
2360 m = cmdutil.match(repo, pats, opts)
2360 m = cmdutil.match(repo, pats, opts)
2361 m.bad = badfn
2361 m.bad = badfn
2362 for src, abs in cmdutil.walk(repo, m, node=node):
2362 for src, abs in repo.walk(m, node=node):
2363 if abs not in names:
2363 if abs not in names:
2364 names[abs] = m.rel(abs), m.exact(abs)
2364 names[abs] = m.rel(abs), m.exact(abs)
2365
2365
2366 changes = repo.status(files=files, match=names.has_key)[:4]
2366 changes = repo.status(files=files, match=names.has_key)[:4]
2367 modified, added, removed, deleted = map(dict.fromkeys, changes)
2367 modified, added, removed, deleted = map(dict.fromkeys, changes)
2368
2368
2369 # if f is a rename, also revert the source
2369 # if f is a rename, also revert the source
2370 cwd = repo.getcwd()
2370 cwd = repo.getcwd()
2371 for f in added:
2371 for f in added:
2372 src = repo.dirstate.copied(f)
2372 src = repo.dirstate.copied(f)
2373 if src and src not in names and repo.dirstate[src] == 'r':
2373 if src and src not in names and repo.dirstate[src] == 'r':
2374 removed[src] = None
2374 removed[src] = None
2375 names[src] = (repo.pathto(src, cwd), True)
2375 names[src] = (repo.pathto(src, cwd), True)
2376
2376
2377 def removeforget(abs):
2377 def removeforget(abs):
2378 if repo.dirstate[abs] == 'a':
2378 if repo.dirstate[abs] == 'a':
2379 return _('forgetting %s\n')
2379 return _('forgetting %s\n')
2380 return _('removing %s\n')
2380 return _('removing %s\n')
2381
2381
2382 revert = ([], _('reverting %s\n'))
2382 revert = ([], _('reverting %s\n'))
2383 add = ([], _('adding %s\n'))
2383 add = ([], _('adding %s\n'))
2384 remove = ([], removeforget)
2384 remove = ([], removeforget)
2385 undelete = ([], _('undeleting %s\n'))
2385 undelete = ([], _('undeleting %s\n'))
2386
2386
2387 disptable = (
2387 disptable = (
2388 # dispatch table:
2388 # dispatch table:
2389 # file state
2389 # file state
2390 # action if in target manifest
2390 # action if in target manifest
2391 # action if not in target manifest
2391 # action if not in target manifest
2392 # make backup if in target manifest
2392 # make backup if in target manifest
2393 # make backup if not in target manifest
2393 # make backup if not in target manifest
2394 (modified, revert, remove, True, True),
2394 (modified, revert, remove, True, True),
2395 (added, revert, remove, True, False),
2395 (added, revert, remove, True, False),
2396 (removed, undelete, None, False, False),
2396 (removed, undelete, None, False, False),
2397 (deleted, revert, remove, False, False),
2397 (deleted, revert, remove, False, False),
2398 )
2398 )
2399
2399
2400 entries = names.items()
2400 entries = names.items()
2401 entries.sort()
2401 entries.sort()
2402
2402
2403 for abs, (rel, exact) in entries:
2403 for abs, (rel, exact) in entries:
2404 mfentry = mf.get(abs)
2404 mfentry = mf.get(abs)
2405 target = repo.wjoin(abs)
2405 target = repo.wjoin(abs)
2406 def handle(xlist, dobackup):
2406 def handle(xlist, dobackup):
2407 xlist[0].append(abs)
2407 xlist[0].append(abs)
2408 if dobackup and not opts['no_backup'] and util.lexists(target):
2408 if dobackup and not opts['no_backup'] and util.lexists(target):
2409 bakname = "%s.orig" % rel
2409 bakname = "%s.orig" % rel
2410 ui.note(_('saving current version of %s as %s\n') %
2410 ui.note(_('saving current version of %s as %s\n') %
2411 (rel, bakname))
2411 (rel, bakname))
2412 if not opts.get('dry_run'):
2412 if not opts.get('dry_run'):
2413 util.copyfile(target, bakname)
2413 util.copyfile(target, bakname)
2414 if ui.verbose or not exact:
2414 if ui.verbose or not exact:
2415 msg = xlist[1]
2415 msg = xlist[1]
2416 if not isinstance(msg, basestring):
2416 if not isinstance(msg, basestring):
2417 msg = msg(abs)
2417 msg = msg(abs)
2418 ui.status(msg % rel)
2418 ui.status(msg % rel)
2419 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2419 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2420 if abs not in table: continue
2420 if abs not in table: continue
2421 # file has changed in dirstate
2421 # file has changed in dirstate
2422 if mfentry:
2422 if mfentry:
2423 handle(hitlist, backuphit)
2423 handle(hitlist, backuphit)
2424 elif misslist is not None:
2424 elif misslist is not None:
2425 handle(misslist, backupmiss)
2425 handle(misslist, backupmiss)
2426 break
2426 break
2427 else:
2427 else:
2428 if abs not in repo.dirstate:
2428 if abs not in repo.dirstate:
2429 if mfentry:
2429 if mfentry:
2430 handle(add, True)
2430 handle(add, True)
2431 elif exact:
2431 elif exact:
2432 ui.warn(_('file not managed: %s\n') % rel)
2432 ui.warn(_('file not managed: %s\n') % rel)
2433 continue
2433 continue
2434 # file has not changed in dirstate
2434 # file has not changed in dirstate
2435 if node == parent:
2435 if node == parent:
2436 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2436 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2437 continue
2437 continue
2438 if pmf is None:
2438 if pmf is None:
2439 # only need parent manifest in this unlikely case,
2439 # only need parent manifest in this unlikely case,
2440 # so do not read by default
2440 # so do not read by default
2441 pmf = repo.changectx(parent).manifest()
2441 pmf = repo.changectx(parent).manifest()
2442 if abs in pmf:
2442 if abs in pmf:
2443 if mfentry:
2443 if mfentry:
2444 # if version of file is same in parent and target
2444 # if version of file is same in parent and target
2445 # manifests, do nothing
2445 # manifests, do nothing
2446 if (pmf[abs] != mfentry or
2446 if (pmf[abs] != mfentry or
2447 pmf.flags(abs) != mf.flags(abs)):
2447 pmf.flags(abs) != mf.flags(abs)):
2448 handle(revert, False)
2448 handle(revert, False)
2449 else:
2449 else:
2450 handle(remove, False)
2450 handle(remove, False)
2451
2451
2452 if not opts.get('dry_run'):
2452 if not opts.get('dry_run'):
2453 def checkout(f):
2453 def checkout(f):
2454 fc = ctx[f]
2454 fc = ctx[f]
2455 repo.wwrite(f, fc.data(), fc.fileflags())
2455 repo.wwrite(f, fc.data(), fc.fileflags())
2456
2456
2457 audit_path = util.path_auditor(repo.root)
2457 audit_path = util.path_auditor(repo.root)
2458 for f in remove[0]:
2458 for f in remove[0]:
2459 if repo.dirstate[f] == 'a':
2459 if repo.dirstate[f] == 'a':
2460 repo.dirstate.forget(f)
2460 repo.dirstate.forget(f)
2461 continue
2461 continue
2462 audit_path(f)
2462 audit_path(f)
2463 try:
2463 try:
2464 util.unlink(repo.wjoin(f))
2464 util.unlink(repo.wjoin(f))
2465 except OSError:
2465 except OSError:
2466 pass
2466 pass
2467 repo.dirstate.remove(f)
2467 repo.dirstate.remove(f)
2468
2468
2469 normal = None
2469 normal = None
2470 if node == parent:
2470 if node == parent:
2471 # We're reverting to our parent. If possible, we'd like status
2471 # We're reverting to our parent. If possible, we'd like status
2472 # to report the file as clean. We have to use normallookup for
2472 # to report the file as clean. We have to use normallookup for
2473 # merges to avoid losing information about merged/dirty files.
2473 # merges to avoid losing information about merged/dirty files.
2474 if p2 != nullid:
2474 if p2 != nullid:
2475 normal = repo.dirstate.normallookup
2475 normal = repo.dirstate.normallookup
2476 else:
2476 else:
2477 normal = repo.dirstate.normal
2477 normal = repo.dirstate.normal
2478 for f in revert[0]:
2478 for f in revert[0]:
2479 checkout(f)
2479 checkout(f)
2480 if normal:
2480 if normal:
2481 normal(f)
2481 normal(f)
2482
2482
2483 for f in add[0]:
2483 for f in add[0]:
2484 checkout(f)
2484 checkout(f)
2485 repo.dirstate.add(f)
2485 repo.dirstate.add(f)
2486
2486
2487 normal = repo.dirstate.normallookup
2487 normal = repo.dirstate.normallookup
2488 if node == parent and p2 == nullid:
2488 if node == parent and p2 == nullid:
2489 normal = repo.dirstate.normal
2489 normal = repo.dirstate.normal
2490 for f in undelete[0]:
2490 for f in undelete[0]:
2491 checkout(f)
2491 checkout(f)
2492 normal(f)
2492 normal(f)
2493
2493
2494 finally:
2494 finally:
2495 del wlock
2495 del wlock
2496
2496
2497 def rollback(ui, repo):
2497 def rollback(ui, repo):
2498 """roll back the last transaction
2498 """roll back the last transaction
2499
2499
2500 This command should be used with care. There is only one level of
2500 This command should be used with care. There is only one level of
2501 rollback, and there is no way to undo a rollback. It will also
2501 rollback, and there is no way to undo a rollback. It will also
2502 restore the dirstate at the time of the last transaction, losing
2502 restore the dirstate at the time of the last transaction, losing
2503 any dirstate changes since that time.
2503 any dirstate changes since that time.
2504
2504
2505 Transactions are used to encapsulate the effects of all commands
2505 Transactions are used to encapsulate the effects of all commands
2506 that create new changesets or propagate existing changesets into a
2506 that create new changesets or propagate existing changesets into a
2507 repository. For example, the following commands are transactional,
2507 repository. For example, the following commands are transactional,
2508 and their effects can be rolled back:
2508 and their effects can be rolled back:
2509
2509
2510 commit
2510 commit
2511 import
2511 import
2512 pull
2512 pull
2513 push (with this repository as destination)
2513 push (with this repository as destination)
2514 unbundle
2514 unbundle
2515
2515
2516 This command is not intended for use on public repositories. Once
2516 This command is not intended for use on public repositories. Once
2517 changes are visible for pull by other users, rolling a transaction
2517 changes are visible for pull by other users, rolling a transaction
2518 back locally is ineffective (someone else may already have pulled
2518 back locally is ineffective (someone else may already have pulled
2519 the changes). Furthermore, a race is possible with readers of the
2519 the changes). Furthermore, a race is possible with readers of the
2520 repository; for example an in-progress pull from the repository
2520 repository; for example an in-progress pull from the repository
2521 may fail if a rollback is performed.
2521 may fail if a rollback is performed.
2522 """
2522 """
2523 repo.rollback()
2523 repo.rollback()
2524
2524
2525 def root(ui, repo):
2525 def root(ui, repo):
2526 """print the root (top) of the current working dir
2526 """print the root (top) of the current working dir
2527
2527
2528 Print the root directory of the current repository.
2528 Print the root directory of the current repository.
2529 """
2529 """
2530 ui.write(repo.root + "\n")
2530 ui.write(repo.root + "\n")
2531
2531
2532 def serve(ui, repo, **opts):
2532 def serve(ui, repo, **opts):
2533 """export the repository via HTTP
2533 """export the repository via HTTP
2534
2534
2535 Start a local HTTP repository browser and pull server.
2535 Start a local HTTP repository browser and pull server.
2536
2536
2537 By default, the server logs accesses to stdout and errors to
2537 By default, the server logs accesses to stdout and errors to
2538 stderr. Use the "-A" and "-E" options to log to files.
2538 stderr. Use the "-A" and "-E" options to log to files.
2539 """
2539 """
2540
2540
2541 if opts["stdio"]:
2541 if opts["stdio"]:
2542 if repo is None:
2542 if repo is None:
2543 raise RepoError(_("There is no Mercurial repository here"
2543 raise RepoError(_("There is no Mercurial repository here"
2544 " (.hg not found)"))
2544 " (.hg not found)"))
2545 s = sshserver.sshserver(ui, repo)
2545 s = sshserver.sshserver(ui, repo)
2546 s.serve_forever()
2546 s.serve_forever()
2547
2547
2548 parentui = ui.parentui or ui
2548 parentui = ui.parentui or ui
2549 optlist = ("name templates style address port prefix ipv6"
2549 optlist = ("name templates style address port prefix ipv6"
2550 " accesslog errorlog webdir_conf certificate")
2550 " accesslog errorlog webdir_conf certificate")
2551 for o in optlist.split():
2551 for o in optlist.split():
2552 if opts[o]:
2552 if opts[o]:
2553 parentui.setconfig("web", o, str(opts[o]))
2553 parentui.setconfig("web", o, str(opts[o]))
2554 if (repo is not None) and (repo.ui != parentui):
2554 if (repo is not None) and (repo.ui != parentui):
2555 repo.ui.setconfig("web", o, str(opts[o]))
2555 repo.ui.setconfig("web", o, str(opts[o]))
2556
2556
2557 if repo is None and not ui.config("web", "webdir_conf"):
2557 if repo is None and not ui.config("web", "webdir_conf"):
2558 raise RepoError(_("There is no Mercurial repository here"
2558 raise RepoError(_("There is no Mercurial repository here"
2559 " (.hg not found)"))
2559 " (.hg not found)"))
2560
2560
2561 class service:
2561 class service:
2562 def init(self):
2562 def init(self):
2563 util.set_signal_handler()
2563 util.set_signal_handler()
2564 self.httpd = hgweb.server.create_server(parentui, repo)
2564 self.httpd = hgweb.server.create_server(parentui, repo)
2565
2565
2566 if not ui.verbose: return
2566 if not ui.verbose: return
2567
2567
2568 if self.httpd.prefix:
2568 if self.httpd.prefix:
2569 prefix = self.httpd.prefix.strip('/') + '/'
2569 prefix = self.httpd.prefix.strip('/') + '/'
2570 else:
2570 else:
2571 prefix = ''
2571 prefix = ''
2572
2572
2573 port = ':%d' % self.httpd.port
2573 port = ':%d' % self.httpd.port
2574 if port == ':80':
2574 if port == ':80':
2575 port = ''
2575 port = ''
2576
2576
2577 bindaddr = self.httpd.addr
2577 bindaddr = self.httpd.addr
2578 if bindaddr == '0.0.0.0':
2578 if bindaddr == '0.0.0.0':
2579 bindaddr = '*'
2579 bindaddr = '*'
2580 elif ':' in bindaddr: # IPv6
2580 elif ':' in bindaddr: # IPv6
2581 bindaddr = '[%s]' % bindaddr
2581 bindaddr = '[%s]' % bindaddr
2582
2582
2583 fqaddr = self.httpd.fqaddr
2583 fqaddr = self.httpd.fqaddr
2584 if ':' in fqaddr:
2584 if ':' in fqaddr:
2585 fqaddr = '[%s]' % fqaddr
2585 fqaddr = '[%s]' % fqaddr
2586 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2586 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2587 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2587 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2588
2588
2589 def run(self):
2589 def run(self):
2590 self.httpd.serve_forever()
2590 self.httpd.serve_forever()
2591
2591
2592 service = service()
2592 service = service()
2593
2593
2594 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2594 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2595
2595
2596 def status(ui, repo, *pats, **opts):
2596 def status(ui, repo, *pats, **opts):
2597 """show changed files in the working directory
2597 """show changed files in the working directory
2598
2598
2599 Show status of files in the repository. If names are given, only
2599 Show status of files in the repository. If names are given, only
2600 files that match are shown. Files that are clean or ignored or
2600 files that match are shown. Files that are clean or ignored or
2601 source of a copy/move operation, are not listed unless -c (clean),
2601 source of a copy/move operation, are not listed unless -c (clean),
2602 -i (ignored), -C (copies) or -A is given. Unless options described
2602 -i (ignored), -C (copies) or -A is given. Unless options described
2603 with "show only ..." are given, the options -mardu are used.
2603 with "show only ..." are given, the options -mardu are used.
2604
2604
2605 Option -q/--quiet hides untracked (unknown and ignored) files
2605 Option -q/--quiet hides untracked (unknown and ignored) files
2606 unless explicitly requested with -u/--unknown or -i/-ignored.
2606 unless explicitly requested with -u/--unknown or -i/-ignored.
2607
2607
2608 NOTE: status may appear to disagree with diff if permissions have
2608 NOTE: status may appear to disagree with diff if permissions have
2609 changed or a merge has occurred. The standard diff format does not
2609 changed or a merge has occurred. The standard diff format does not
2610 report permission changes and diff only reports changes relative
2610 report permission changes and diff only reports changes relative
2611 to one merge parent.
2611 to one merge parent.
2612
2612
2613 If one revision is given, it is used as the base revision.
2613 If one revision is given, it is used as the base revision.
2614 If two revisions are given, the difference between them is shown.
2614 If two revisions are given, the difference between them is shown.
2615
2615
2616 The codes used to show the status of files are:
2616 The codes used to show the status of files are:
2617 M = modified
2617 M = modified
2618 A = added
2618 A = added
2619 R = removed
2619 R = removed
2620 C = clean
2620 C = clean
2621 ! = deleted, but still tracked
2621 ! = deleted, but still tracked
2622 ? = not tracked
2622 ? = not tracked
2623 I = ignored
2623 I = ignored
2624 = the previous added file was copied from here
2624 = the previous added file was copied from here
2625 """
2625 """
2626
2626
2627 all = opts['all']
2627 all = opts['all']
2628 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2628 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2629
2629
2630 matcher = cmdutil.match(repo, pats, opts)
2630 matcher = cmdutil.match(repo, pats, opts)
2631 cwd = (pats and repo.getcwd()) or ''
2631 cwd = (pats and repo.getcwd()) or ''
2632 modified, added, removed, deleted, unknown, ignored, clean = [
2632 modified, added, removed, deleted, unknown, ignored, clean = [
2633 n for n in repo.status(node1, node2, matcher.files(), matcher,
2633 n for n in repo.status(node1, node2, matcher.files(), matcher,
2634 list_ignored=opts['ignored']
2634 list_ignored=opts['ignored']
2635 or all and not ui.quiet,
2635 or all and not ui.quiet,
2636 list_clean=opts['clean'] or all,
2636 list_clean=opts['clean'] or all,
2637 list_unknown=opts['unknown']
2637 list_unknown=opts['unknown']
2638 or not (ui.quiet or
2638 or not (ui.quiet or
2639 opts['modified'] or
2639 opts['modified'] or
2640 opts['added'] or
2640 opts['added'] or
2641 opts['removed'] or
2641 opts['removed'] or
2642 opts['deleted'] or
2642 opts['deleted'] or
2643 opts['ignored']))]
2643 opts['ignored']))]
2644
2644
2645 changetypes = (('modified', 'M', modified),
2645 changetypes = (('modified', 'M', modified),
2646 ('added', 'A', added),
2646 ('added', 'A', added),
2647 ('removed', 'R', removed),
2647 ('removed', 'R', removed),
2648 ('deleted', '!', deleted),
2648 ('deleted', '!', deleted),
2649 ('unknown', '?', unknown),
2649 ('unknown', '?', unknown),
2650 ('ignored', 'I', ignored))
2650 ('ignored', 'I', ignored))
2651
2651
2652 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2652 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2653
2653
2654 copy = {}
2654 copy = {}
2655 showcopy = {}
2655 showcopy = {}
2656 if ((all or opts.get('copies')) and not opts.get('no_status')):
2656 if ((all or opts.get('copies')) and not opts.get('no_status')):
2657 if opts.get('rev') == []:
2657 if opts.get('rev') == []:
2658 # fast path, more correct with merge parents
2658 # fast path, more correct with merge parents
2659 showcopy = copy = repo.dirstate.copies().copy()
2659 showcopy = copy = repo.dirstate.copies().copy()
2660 else:
2660 else:
2661 ctxn = repo.changectx(nullid)
2661 ctxn = repo.changectx(nullid)
2662 ctx1 = repo.changectx(node1)
2662 ctx1 = repo.changectx(node1)
2663 ctx2 = repo.changectx(node2)
2663 ctx2 = repo.changectx(node2)
2664 if node2 is None:
2664 if node2 is None:
2665 ctx2 = repo.workingctx()
2665 ctx2 = repo.workingctx()
2666 copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn)
2666 copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn)
2667 for k, v in copy.items():
2667 for k, v in copy.items():
2668 copy[v] = k
2668 copy[v] = k
2669
2669
2670 end = opts['print0'] and '\0' or '\n'
2670 end = opts['print0'] and '\0' or '\n'
2671
2671
2672 for opt, char, changes in ([ct for ct in explicit_changetypes
2672 for opt, char, changes in ([ct for ct in explicit_changetypes
2673 if all or opts[ct[0]]]
2673 if all or opts[ct[0]]]
2674 or changetypes):
2674 or changetypes):
2675
2675
2676 if opts['no_status']:
2676 if opts['no_status']:
2677 format = "%%s%s" % end
2677 format = "%%s%s" % end
2678 else:
2678 else:
2679 format = "%s %%s%s" % (char, end)
2679 format = "%s %%s%s" % (char, end)
2680
2680
2681 for f in changes:
2681 for f in changes:
2682 ui.write(format % repo.pathto(f, cwd))
2682 ui.write(format % repo.pathto(f, cwd))
2683 if f in copy and (f in added or f in showcopy):
2683 if f in copy and (f in added or f in showcopy):
2684 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2684 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2685
2685
2686 def tag(ui, repo, name1, *names, **opts):
2686 def tag(ui, repo, name1, *names, **opts):
2687 """add one or more tags for the current or given revision
2687 """add one or more tags for the current or given revision
2688
2688
2689 Name a particular revision using <name>.
2689 Name a particular revision using <name>.
2690
2690
2691 Tags are used to name particular revisions of the repository and are
2691 Tags are used to name particular revisions of the repository and are
2692 very useful to compare different revisions, to go back to significant
2692 very useful to compare different revisions, to go back to significant
2693 earlier versions or to mark branch points as releases, etc.
2693 earlier versions or to mark branch points as releases, etc.
2694
2694
2695 If no revision is given, the parent of the working directory is used,
2695 If no revision is given, the parent of the working directory is used,
2696 or tip if no revision is checked out.
2696 or tip if no revision is checked out.
2697
2697
2698 To facilitate version control, distribution, and merging of tags,
2698 To facilitate version control, distribution, and merging of tags,
2699 they are stored as a file named ".hgtags" which is managed
2699 they are stored as a file named ".hgtags" which is managed
2700 similarly to other project files and can be hand-edited if
2700 similarly to other project files and can be hand-edited if
2701 necessary. The file '.hg/localtags' is used for local tags (not
2701 necessary. The file '.hg/localtags' is used for local tags (not
2702 shared among repositories).
2702 shared among repositories).
2703
2703
2704 See 'hg help dates' for a list of formats valid for -d/--date.
2704 See 'hg help dates' for a list of formats valid for -d/--date.
2705 """
2705 """
2706
2706
2707 rev_ = None
2707 rev_ = None
2708 names = (name1,) + names
2708 names = (name1,) + names
2709 if len(names) != len(dict.fromkeys(names)):
2709 if len(names) != len(dict.fromkeys(names)):
2710 raise util.Abort(_('tag names must be unique'))
2710 raise util.Abort(_('tag names must be unique'))
2711 for n in names:
2711 for n in names:
2712 if n in ['tip', '.', 'null']:
2712 if n in ['tip', '.', 'null']:
2713 raise util.Abort(_('the name \'%s\' is reserved') % n)
2713 raise util.Abort(_('the name \'%s\' is reserved') % n)
2714 if opts['rev'] and opts['remove']:
2714 if opts['rev'] and opts['remove']:
2715 raise util.Abort(_("--rev and --remove are incompatible"))
2715 raise util.Abort(_("--rev and --remove are incompatible"))
2716 if opts['rev']:
2716 if opts['rev']:
2717 rev_ = opts['rev']
2717 rev_ = opts['rev']
2718 message = opts['message']
2718 message = opts['message']
2719 if opts['remove']:
2719 if opts['remove']:
2720 expectedtype = opts['local'] and 'local' or 'global'
2720 expectedtype = opts['local'] and 'local' or 'global'
2721 for n in names:
2721 for n in names:
2722 if not repo.tagtype(n):
2722 if not repo.tagtype(n):
2723 raise util.Abort(_('tag \'%s\' does not exist') % n)
2723 raise util.Abort(_('tag \'%s\' does not exist') % n)
2724 if repo.tagtype(n) != expectedtype:
2724 if repo.tagtype(n) != expectedtype:
2725 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2725 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2726 (n, expectedtype))
2726 (n, expectedtype))
2727 rev_ = nullid
2727 rev_ = nullid
2728 if not message:
2728 if not message:
2729 message = _('Removed tag %s') % ', '.join(names)
2729 message = _('Removed tag %s') % ', '.join(names)
2730 elif not opts['force']:
2730 elif not opts['force']:
2731 for n in names:
2731 for n in names:
2732 if n in repo.tags():
2732 if n in repo.tags():
2733 raise util.Abort(_('tag \'%s\' already exists '
2733 raise util.Abort(_('tag \'%s\' already exists '
2734 '(use -f to force)') % n)
2734 '(use -f to force)') % n)
2735 if not rev_ and repo.dirstate.parents()[1] != nullid:
2735 if not rev_ and repo.dirstate.parents()[1] != nullid:
2736 raise util.Abort(_('uncommitted merge - please provide a '
2736 raise util.Abort(_('uncommitted merge - please provide a '
2737 'specific revision'))
2737 'specific revision'))
2738 r = repo.changectx(rev_).node()
2738 r = repo.changectx(rev_).node()
2739
2739
2740 if not message:
2740 if not message:
2741 message = (_('Added tag %s for changeset %s') %
2741 message = (_('Added tag %s for changeset %s') %
2742 (', '.join(names), short(r)))
2742 (', '.join(names), short(r)))
2743
2743
2744 date = opts.get('date')
2744 date = opts.get('date')
2745 if date:
2745 if date:
2746 date = util.parsedate(date)
2746 date = util.parsedate(date)
2747
2747
2748 repo.tag(names, r, message, opts['local'], opts['user'], date)
2748 repo.tag(names, r, message, opts['local'], opts['user'], date)
2749
2749
2750 def tags(ui, repo):
2750 def tags(ui, repo):
2751 """list repository tags
2751 """list repository tags
2752
2752
2753 List the repository tags.
2753 List the repository tags.
2754
2754
2755 This lists both regular and local tags. When the -v/--verbose switch
2755 This lists both regular and local tags. When the -v/--verbose switch
2756 is used, a third column "local" is printed for local tags.
2756 is used, a third column "local" is printed for local tags.
2757 """
2757 """
2758
2758
2759 l = repo.tagslist()
2759 l = repo.tagslist()
2760 l.reverse()
2760 l.reverse()
2761 hexfunc = ui.debugflag and hex or short
2761 hexfunc = ui.debugflag and hex or short
2762 tagtype = ""
2762 tagtype = ""
2763
2763
2764 for t, n in l:
2764 for t, n in l:
2765 if ui.quiet:
2765 if ui.quiet:
2766 ui.write("%s\n" % t)
2766 ui.write("%s\n" % t)
2767 continue
2767 continue
2768
2768
2769 try:
2769 try:
2770 hn = hexfunc(n)
2770 hn = hexfunc(n)
2771 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2771 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2772 except revlog.LookupError:
2772 except revlog.LookupError:
2773 r = " ?:%s" % hn
2773 r = " ?:%s" % hn
2774 else:
2774 else:
2775 spaces = " " * (30 - util.locallen(t))
2775 spaces = " " * (30 - util.locallen(t))
2776 if ui.verbose:
2776 if ui.verbose:
2777 if repo.tagtype(t) == 'local':
2777 if repo.tagtype(t) == 'local':
2778 tagtype = " local"
2778 tagtype = " local"
2779 else:
2779 else:
2780 tagtype = ""
2780 tagtype = ""
2781 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2781 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2782
2782
2783 def tip(ui, repo, **opts):
2783 def tip(ui, repo, **opts):
2784 """show the tip revision
2784 """show the tip revision
2785
2785
2786 The tip revision (usually just called the tip) is the most
2786 The tip revision (usually just called the tip) is the most
2787 recently added changeset in the repository, the most recently
2787 recently added changeset in the repository, the most recently
2788 changed head.
2788 changed head.
2789
2789
2790 If you have just made a commit, that commit will be the tip. If
2790 If you have just made a commit, that commit will be the tip. If
2791 you have just pulled changes from another repository, the tip of
2791 you have just pulled changes from another repository, the tip of
2792 that repository becomes the current tip. The "tip" tag is special
2792 that repository becomes the current tip. The "tip" tag is special
2793 and cannot be renamed or assigned to a different changeset.
2793 and cannot be renamed or assigned to a different changeset.
2794 """
2794 """
2795 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2795 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2796
2796
2797 def unbundle(ui, repo, fname1, *fnames, **opts):
2797 def unbundle(ui, repo, fname1, *fnames, **opts):
2798 """apply one or more changegroup files
2798 """apply one or more changegroup files
2799
2799
2800 Apply one or more compressed changegroup files generated by the
2800 Apply one or more compressed changegroup files generated by the
2801 bundle command.
2801 bundle command.
2802 """
2802 """
2803 fnames = (fname1,) + fnames
2803 fnames = (fname1,) + fnames
2804
2804
2805 lock = None
2805 lock = None
2806 try:
2806 try:
2807 lock = repo.lock()
2807 lock = repo.lock()
2808 for fname in fnames:
2808 for fname in fnames:
2809 if os.path.exists(fname):
2809 if os.path.exists(fname):
2810 f = open(fname, "rb")
2810 f = open(fname, "rb")
2811 else:
2811 else:
2812 f = urllib.urlopen(fname)
2812 f = urllib.urlopen(fname)
2813 gen = changegroup.readbundle(f, fname)
2813 gen = changegroup.readbundle(f, fname)
2814 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2814 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2815 finally:
2815 finally:
2816 del lock
2816 del lock
2817
2817
2818 return postincoming(ui, repo, modheads, opts['update'], None)
2818 return postincoming(ui, repo, modheads, opts['update'], None)
2819
2819
2820 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2820 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2821 """update working directory
2821 """update working directory
2822
2822
2823 Update the working directory to the specified revision, or the
2823 Update the working directory to the specified revision, or the
2824 tip of the current branch if none is specified.
2824 tip of the current branch if none is specified.
2825
2825
2826 If the requested revision is a descendant of the working
2826 If the requested revision is a descendant of the working
2827 directory, any outstanding changes in the working directory will
2827 directory, any outstanding changes in the working directory will
2828 be merged into the result. If it is not directly descended but is
2828 be merged into the result. If it is not directly descended but is
2829 on the same named branch, update aborts with a suggestion to use
2829 on the same named branch, update aborts with a suggestion to use
2830 merge or update -C instead.
2830 merge or update -C instead.
2831
2831
2832 If the requested revision is on a different named branch and the
2832 If the requested revision is on a different named branch and the
2833 working directory is clean, update quietly switches branches.
2833 working directory is clean, update quietly switches branches.
2834
2834
2835 See 'hg help dates' for a list of formats valid for --date.
2835 See 'hg help dates' for a list of formats valid for --date.
2836 """
2836 """
2837 if rev and node:
2837 if rev and node:
2838 raise util.Abort(_("please specify just one revision"))
2838 raise util.Abort(_("please specify just one revision"))
2839
2839
2840 if not rev:
2840 if not rev:
2841 rev = node
2841 rev = node
2842
2842
2843 if date:
2843 if date:
2844 if rev:
2844 if rev:
2845 raise util.Abort(_("you can't specify a revision and a date"))
2845 raise util.Abort(_("you can't specify a revision and a date"))
2846 rev = cmdutil.finddate(ui, repo, date)
2846 rev = cmdutil.finddate(ui, repo, date)
2847
2847
2848 if clean:
2848 if clean:
2849 return hg.clean(repo, rev)
2849 return hg.clean(repo, rev)
2850 else:
2850 else:
2851 return hg.update(repo, rev)
2851 return hg.update(repo, rev)
2852
2852
2853 def verify(ui, repo):
2853 def verify(ui, repo):
2854 """verify the integrity of the repository
2854 """verify the integrity of the repository
2855
2855
2856 Verify the integrity of the current repository.
2856 Verify the integrity of the current repository.
2857
2857
2858 This will perform an extensive check of the repository's
2858 This will perform an extensive check of the repository's
2859 integrity, validating the hashes and checksums of each entry in
2859 integrity, validating the hashes and checksums of each entry in
2860 the changelog, manifest, and tracked files, as well as the
2860 the changelog, manifest, and tracked files, as well as the
2861 integrity of their crosslinks and indices.
2861 integrity of their crosslinks and indices.
2862 """
2862 """
2863 return hg.verify(repo)
2863 return hg.verify(repo)
2864
2864
2865 def version_(ui):
2865 def version_(ui):
2866 """output version and copyright information"""
2866 """output version and copyright information"""
2867 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2867 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2868 % version.get_version())
2868 % version.get_version())
2869 ui.status(_(
2869 ui.status(_(
2870 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2870 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2871 "This is free software; see the source for copying conditions. "
2871 "This is free software; see the source for copying conditions. "
2872 "There is NO\nwarranty; "
2872 "There is NO\nwarranty; "
2873 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2873 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2874 ))
2874 ))
2875
2875
2876 # Command options and aliases are listed here, alphabetically
2876 # Command options and aliases are listed here, alphabetically
2877
2877
2878 globalopts = [
2878 globalopts = [
2879 ('R', 'repository', '',
2879 ('R', 'repository', '',
2880 _('repository root directory or symbolic path name')),
2880 _('repository root directory or symbolic path name')),
2881 ('', 'cwd', '', _('change working directory')),
2881 ('', 'cwd', '', _('change working directory')),
2882 ('y', 'noninteractive', None,
2882 ('y', 'noninteractive', None,
2883 _('do not prompt, assume \'yes\' for any required answers')),
2883 _('do not prompt, assume \'yes\' for any required answers')),
2884 ('q', 'quiet', None, _('suppress output')),
2884 ('q', 'quiet', None, _('suppress output')),
2885 ('v', 'verbose', None, _('enable additional output')),
2885 ('v', 'verbose', None, _('enable additional output')),
2886 ('', 'config', [], _('set/override config option')),
2886 ('', 'config', [], _('set/override config option')),
2887 ('', 'debug', None, _('enable debugging output')),
2887 ('', 'debug', None, _('enable debugging output')),
2888 ('', 'debugger', None, _('start debugger')),
2888 ('', 'debugger', None, _('start debugger')),
2889 ('', 'encoding', util._encoding, _('set the charset encoding')),
2889 ('', 'encoding', util._encoding, _('set the charset encoding')),
2890 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2890 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2891 ('', 'lsprof', None, _('print improved command execution profile')),
2891 ('', 'lsprof', None, _('print improved command execution profile')),
2892 ('', 'traceback', None, _('print traceback on exception')),
2892 ('', 'traceback', None, _('print traceback on exception')),
2893 ('', 'time', None, _('time how long the command takes')),
2893 ('', 'time', None, _('time how long the command takes')),
2894 ('', 'profile', None, _('print command execution profile')),
2894 ('', 'profile', None, _('print command execution profile')),
2895 ('', 'version', None, _('output version information and exit')),
2895 ('', 'version', None, _('output version information and exit')),
2896 ('h', 'help', None, _('display help and exit')),
2896 ('h', 'help', None, _('display help and exit')),
2897 ]
2897 ]
2898
2898
2899 dryrunopts = [('n', 'dry-run', None,
2899 dryrunopts = [('n', 'dry-run', None,
2900 _('do not perform actions, just print output'))]
2900 _('do not perform actions, just print output'))]
2901
2901
2902 remoteopts = [
2902 remoteopts = [
2903 ('e', 'ssh', '', _('specify ssh command to use')),
2903 ('e', 'ssh', '', _('specify ssh command to use')),
2904 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2904 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2905 ]
2905 ]
2906
2906
2907 walkopts = [
2907 walkopts = [
2908 ('I', 'include', [], _('include names matching the given patterns')),
2908 ('I', 'include', [], _('include names matching the given patterns')),
2909 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2909 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2910 ]
2910 ]
2911
2911
2912 commitopts = [
2912 commitopts = [
2913 ('m', 'message', '', _('use <text> as commit message')),
2913 ('m', 'message', '', _('use <text> as commit message')),
2914 ('l', 'logfile', '', _('read commit message from <file>')),
2914 ('l', 'logfile', '', _('read commit message from <file>')),
2915 ]
2915 ]
2916
2916
2917 commitopts2 = [
2917 commitopts2 = [
2918 ('d', 'date', '', _('record datecode as commit date')),
2918 ('d', 'date', '', _('record datecode as commit date')),
2919 ('u', 'user', '', _('record user as committer')),
2919 ('u', 'user', '', _('record user as committer')),
2920 ]
2920 ]
2921
2921
2922 templateopts = [
2922 templateopts = [
2923 ('', 'style', '', _('display using template map file')),
2923 ('', 'style', '', _('display using template map file')),
2924 ('', 'template', '', _('display with template')),
2924 ('', 'template', '', _('display with template')),
2925 ]
2925 ]
2926
2926
2927 logopts = [
2927 logopts = [
2928 ('p', 'patch', None, _('show patch')),
2928 ('p', 'patch', None, _('show patch')),
2929 ('l', 'limit', '', _('limit number of changes displayed')),
2929 ('l', 'limit', '', _('limit number of changes displayed')),
2930 ('M', 'no-merges', None, _('do not show merges')),
2930 ('M', 'no-merges', None, _('do not show merges')),
2931 ] + templateopts
2931 ] + templateopts
2932
2932
2933 table = {
2933 table = {
2934 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2934 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2935 "addremove":
2935 "addremove":
2936 (addremove,
2936 (addremove,
2937 [('s', 'similarity', '',
2937 [('s', 'similarity', '',
2938 _('guess renamed files by similarity (0<=s<=100)')),
2938 _('guess renamed files by similarity (0<=s<=100)')),
2939 ] + walkopts + dryrunopts,
2939 ] + walkopts + dryrunopts,
2940 _('hg addremove [OPTION]... [FILE]...')),
2940 _('hg addremove [OPTION]... [FILE]...')),
2941 "^annotate|blame":
2941 "^annotate|blame":
2942 (annotate,
2942 (annotate,
2943 [('r', 'rev', '', _('annotate the specified revision')),
2943 [('r', 'rev', '', _('annotate the specified revision')),
2944 ('f', 'follow', None, _('follow file copies and renames')),
2944 ('f', 'follow', None, _('follow file copies and renames')),
2945 ('a', 'text', None, _('treat all files as text')),
2945 ('a', 'text', None, _('treat all files as text')),
2946 ('u', 'user', None, _('list the author (long with -v)')),
2946 ('u', 'user', None, _('list the author (long with -v)')),
2947 ('d', 'date', None, _('list the date (short with -q)')),
2947 ('d', 'date', None, _('list the date (short with -q)')),
2948 ('n', 'number', None, _('list the revision number (default)')),
2948 ('n', 'number', None, _('list the revision number (default)')),
2949 ('c', 'changeset', None, _('list the changeset')),
2949 ('c', 'changeset', None, _('list the changeset')),
2950 ('l', 'line-number', None,
2950 ('l', 'line-number', None,
2951 _('show line number at the first appearance'))
2951 _('show line number at the first appearance'))
2952 ] + walkopts,
2952 ] + walkopts,
2953 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2953 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2954 "archive":
2954 "archive":
2955 (archive,
2955 (archive,
2956 [('', 'no-decode', None, _('do not pass files through decoders')),
2956 [('', 'no-decode', None, _('do not pass files through decoders')),
2957 ('p', 'prefix', '', _('directory prefix for files in archive')),
2957 ('p', 'prefix', '', _('directory prefix for files in archive')),
2958 ('r', 'rev', '', _('revision to distribute')),
2958 ('r', 'rev', '', _('revision to distribute')),
2959 ('t', 'type', '', _('type of distribution to create')),
2959 ('t', 'type', '', _('type of distribution to create')),
2960 ] + walkopts,
2960 ] + walkopts,
2961 _('hg archive [OPTION]... DEST')),
2961 _('hg archive [OPTION]... DEST')),
2962 "backout":
2962 "backout":
2963 (backout,
2963 (backout,
2964 [('', 'merge', None,
2964 [('', 'merge', None,
2965 _('merge with old dirstate parent after backout')),
2965 _('merge with old dirstate parent after backout')),
2966 ('', 'parent', '', _('parent to choose when backing out merge')),
2966 ('', 'parent', '', _('parent to choose when backing out merge')),
2967 ('r', 'rev', '', _('revision to backout')),
2967 ('r', 'rev', '', _('revision to backout')),
2968 ] + walkopts + commitopts + commitopts2,
2968 ] + walkopts + commitopts + commitopts2,
2969 _('hg backout [OPTION]... [-r] REV')),
2969 _('hg backout [OPTION]... [-r] REV')),
2970 "bisect":
2970 "bisect":
2971 (bisect,
2971 (bisect,
2972 [('r', 'reset', False, _('reset bisect state')),
2972 [('r', 'reset', False, _('reset bisect state')),
2973 ('g', 'good', False, _('mark changeset good')),
2973 ('g', 'good', False, _('mark changeset good')),
2974 ('b', 'bad', False, _('mark changeset bad')),
2974 ('b', 'bad', False, _('mark changeset bad')),
2975 ('s', 'skip', False, _('skip testing changeset')),
2975 ('s', 'skip', False, _('skip testing changeset')),
2976 ('U', 'noupdate', False, _('do not update to target'))],
2976 ('U', 'noupdate', False, _('do not update to target'))],
2977 _("hg bisect [-gbsr] [REV]")),
2977 _("hg bisect [-gbsr] [REV]")),
2978 "branch":
2978 "branch":
2979 (branch,
2979 (branch,
2980 [('f', 'force', None,
2980 [('f', 'force', None,
2981 _('set branch name even if it shadows an existing branch'))],
2981 _('set branch name even if it shadows an existing branch'))],
2982 _('hg branch [-f] [NAME]')),
2982 _('hg branch [-f] [NAME]')),
2983 "branches":
2983 "branches":
2984 (branches,
2984 (branches,
2985 [('a', 'active', False,
2985 [('a', 'active', False,
2986 _('show only branches that have unmerged heads'))],
2986 _('show only branches that have unmerged heads'))],
2987 _('hg branches [-a]')),
2987 _('hg branches [-a]')),
2988 "bundle":
2988 "bundle":
2989 (bundle,
2989 (bundle,
2990 [('f', 'force', None,
2990 [('f', 'force', None,
2991 _('run even when remote repository is unrelated')),
2991 _('run even when remote repository is unrelated')),
2992 ('r', 'rev', [],
2992 ('r', 'rev', [],
2993 _('a changeset up to which you would like to bundle')),
2993 _('a changeset up to which you would like to bundle')),
2994 ('', 'base', [],
2994 ('', 'base', [],
2995 _('a base changeset to specify instead of a destination')),
2995 _('a base changeset to specify instead of a destination')),
2996 ('a', 'all', None, _('bundle all changesets in the repository')),
2996 ('a', 'all', None, _('bundle all changesets in the repository')),
2997 ('t', 'type', 'bzip2', _('bundle compression type to use')),
2997 ('t', 'type', 'bzip2', _('bundle compression type to use')),
2998 ] + remoteopts,
2998 ] + remoteopts,
2999 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
2999 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3000 "cat":
3000 "cat":
3001 (cat,
3001 (cat,
3002 [('o', 'output', '', _('print output to file with formatted name')),
3002 [('o', 'output', '', _('print output to file with formatted name')),
3003 ('r', 'rev', '', _('print the given revision')),
3003 ('r', 'rev', '', _('print the given revision')),
3004 ('', 'decode', None, _('apply any matching decode filter')),
3004 ('', 'decode', None, _('apply any matching decode filter')),
3005 ] + walkopts,
3005 ] + walkopts,
3006 _('hg cat [OPTION]... FILE...')),
3006 _('hg cat [OPTION]... FILE...')),
3007 "^clone":
3007 "^clone":
3008 (clone,
3008 (clone,
3009 [('U', 'noupdate', None, _('do not update the new working directory')),
3009 [('U', 'noupdate', None, _('do not update the new working directory')),
3010 ('r', 'rev', [],
3010 ('r', 'rev', [],
3011 _('a changeset you would like to have after cloning')),
3011 _('a changeset you would like to have after cloning')),
3012 ('', 'pull', None, _('use pull protocol to copy metadata')),
3012 ('', 'pull', None, _('use pull protocol to copy metadata')),
3013 ('', 'uncompressed', None,
3013 ('', 'uncompressed', None,
3014 _('use uncompressed transfer (fast over LAN)')),
3014 _('use uncompressed transfer (fast over LAN)')),
3015 ] + remoteopts,
3015 ] + remoteopts,
3016 _('hg clone [OPTION]... SOURCE [DEST]')),
3016 _('hg clone [OPTION]... SOURCE [DEST]')),
3017 "^commit|ci":
3017 "^commit|ci":
3018 (commit,
3018 (commit,
3019 [('A', 'addremove', None,
3019 [('A', 'addremove', None,
3020 _('mark new/missing files as added/removed before committing')),
3020 _('mark new/missing files as added/removed before committing')),
3021 ] + walkopts + commitopts + commitopts2,
3021 ] + walkopts + commitopts + commitopts2,
3022 _('hg commit [OPTION]... [FILE]...')),
3022 _('hg commit [OPTION]... [FILE]...')),
3023 "copy|cp":
3023 "copy|cp":
3024 (copy,
3024 (copy,
3025 [('A', 'after', None, _('record a copy that has already occurred')),
3025 [('A', 'after', None, _('record a copy that has already occurred')),
3026 ('f', 'force', None,
3026 ('f', 'force', None,
3027 _('forcibly copy over an existing managed file')),
3027 _('forcibly copy over an existing managed file')),
3028 ] + walkopts + dryrunopts,
3028 ] + walkopts + dryrunopts,
3029 _('hg copy [OPTION]... [SOURCE]... DEST')),
3029 _('hg copy [OPTION]... [SOURCE]... DEST')),
3030 "debugancestor": (debugancestor, [],
3030 "debugancestor": (debugancestor, [],
3031 _('hg debugancestor [INDEX] REV1 REV2')),
3031 _('hg debugancestor [INDEX] REV1 REV2')),
3032 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
3032 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
3033 "debugcomplete":
3033 "debugcomplete":
3034 (debugcomplete,
3034 (debugcomplete,
3035 [('o', 'options', None, _('show the command options'))],
3035 [('o', 'options', None, _('show the command options'))],
3036 _('hg debugcomplete [-o] CMD')),
3036 _('hg debugcomplete [-o] CMD')),
3037 "debugdate":
3037 "debugdate":
3038 (debugdate,
3038 (debugdate,
3039 [('e', 'extended', None, _('try extended date formats'))],
3039 [('e', 'extended', None, _('try extended date formats'))],
3040 _('hg debugdate [-e] DATE [RANGE]')),
3040 _('hg debugdate [-e] DATE [RANGE]')),
3041 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
3041 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
3042 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
3042 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
3043 "debugindex": (debugindex, [], _('hg debugindex FILE')),
3043 "debugindex": (debugindex, [], _('hg debugindex FILE')),
3044 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
3044 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
3045 "debuginstall": (debuginstall, [], _('hg debuginstall')),
3045 "debuginstall": (debuginstall, [], _('hg debuginstall')),
3046 "debugrawcommit|rawcommit":
3046 "debugrawcommit|rawcommit":
3047 (rawcommit,
3047 (rawcommit,
3048 [('p', 'parent', [], _('parent')),
3048 [('p', 'parent', [], _('parent')),
3049 ('F', 'files', '', _('file list'))
3049 ('F', 'files', '', _('file list'))
3050 ] + commitopts + commitopts2,
3050 ] + commitopts + commitopts2,
3051 _('hg debugrawcommit [OPTION]... [FILE]...')),
3051 _('hg debugrawcommit [OPTION]... [FILE]...')),
3052 "debugrebuildstate":
3052 "debugrebuildstate":
3053 (debugrebuildstate,
3053 (debugrebuildstate,
3054 [('r', 'rev', '', _('revision to rebuild to'))],
3054 [('r', 'rev', '', _('revision to rebuild to'))],
3055 _('hg debugrebuildstate [-r REV] [REV]')),
3055 _('hg debugrebuildstate [-r REV] [REV]')),
3056 "debugrename":
3056 "debugrename":
3057 (debugrename,
3057 (debugrename,
3058 [('r', 'rev', '', _('revision to debug'))],
3058 [('r', 'rev', '', _('revision to debug'))],
3059 _('hg debugrename [-r REV] FILE')),
3059 _('hg debugrename [-r REV] FILE')),
3060 "debugsetparents":
3060 "debugsetparents":
3061 (debugsetparents,
3061 (debugsetparents,
3062 [],
3062 [],
3063 _('hg debugsetparents REV1 [REV2]')),
3063 _('hg debugsetparents REV1 [REV2]')),
3064 "debugstate":
3064 "debugstate":
3065 (debugstate,
3065 (debugstate,
3066 [('', 'nodates', None, _('do not display the saved mtime'))],
3066 [('', 'nodates', None, _('do not display the saved mtime'))],
3067 _('hg debugstate [OPTS]')),
3067 _('hg debugstate [OPTS]')),
3068 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3068 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3069 "^diff":
3069 "^diff":
3070 (diff,
3070 (diff,
3071 [('r', 'rev', [], _('revision')),
3071 [('r', 'rev', [], _('revision')),
3072 ('a', 'text', None, _('treat all files as text')),
3072 ('a', 'text', None, _('treat all files as text')),
3073 ('p', 'show-function', None,
3073 ('p', 'show-function', None,
3074 _('show which function each change is in')),
3074 _('show which function each change is in')),
3075 ('g', 'git', None, _('use git extended diff format')),
3075 ('g', 'git', None, _('use git extended diff format')),
3076 ('', 'nodates', None, _("don't include dates in diff headers")),
3076 ('', 'nodates', None, _("don't include dates in diff headers")),
3077 ('w', 'ignore-all-space', None,
3077 ('w', 'ignore-all-space', None,
3078 _('ignore white space when comparing lines')),
3078 _('ignore white space when comparing lines')),
3079 ('b', 'ignore-space-change', None,
3079 ('b', 'ignore-space-change', None,
3080 _('ignore changes in the amount of white space')),
3080 _('ignore changes in the amount of white space')),
3081 ('B', 'ignore-blank-lines', None,
3081 ('B', 'ignore-blank-lines', None,
3082 _('ignore changes whose lines are all blank')),
3082 _('ignore changes whose lines are all blank')),
3083 ('U', 'unified', '',
3083 ('U', 'unified', '',
3084 _('number of lines of context to show'))
3084 _('number of lines of context to show'))
3085 ] + walkopts,
3085 ] + walkopts,
3086 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3086 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3087 "^export":
3087 "^export":
3088 (export,
3088 (export,
3089 [('o', 'output', '', _('print output to file with formatted name')),
3089 [('o', 'output', '', _('print output to file with formatted name')),
3090 ('a', 'text', None, _('treat all files as text')),
3090 ('a', 'text', None, _('treat all files as text')),
3091 ('g', 'git', None, _('use git extended diff format')),
3091 ('g', 'git', None, _('use git extended diff format')),
3092 ('', 'nodates', None, _("don't include dates in diff headers")),
3092 ('', 'nodates', None, _("don't include dates in diff headers")),
3093 ('', 'switch-parent', None, _('diff against the second parent'))],
3093 ('', 'switch-parent', None, _('diff against the second parent'))],
3094 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3094 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3095 "grep":
3095 "grep":
3096 (grep,
3096 (grep,
3097 [('0', 'print0', None, _('end fields with NUL')),
3097 [('0', 'print0', None, _('end fields with NUL')),
3098 ('', 'all', None, _('print all revisions that match')),
3098 ('', 'all', None, _('print all revisions that match')),
3099 ('f', 'follow', None,
3099 ('f', 'follow', None,
3100 _('follow changeset history, or file history across copies and renames')),
3100 _('follow changeset history, or file history across copies and renames')),
3101 ('i', 'ignore-case', None, _('ignore case when matching')),
3101 ('i', 'ignore-case', None, _('ignore case when matching')),
3102 ('l', 'files-with-matches', None,
3102 ('l', 'files-with-matches', None,
3103 _('print only filenames and revs that match')),
3103 _('print only filenames and revs that match')),
3104 ('n', 'line-number', None, _('print matching line numbers')),
3104 ('n', 'line-number', None, _('print matching line numbers')),
3105 ('r', 'rev', [], _('search in given revision range')),
3105 ('r', 'rev', [], _('search in given revision range')),
3106 ('u', 'user', None, _('list the author (long with -v)')),
3106 ('u', 'user', None, _('list the author (long with -v)')),
3107 ('d', 'date', None, _('list the date (short with -q)')),
3107 ('d', 'date', None, _('list the date (short with -q)')),
3108 ] + walkopts,
3108 ] + walkopts,
3109 _('hg grep [OPTION]... PATTERN [FILE]...')),
3109 _('hg grep [OPTION]... PATTERN [FILE]...')),
3110 "heads":
3110 "heads":
3111 (heads,
3111 (heads,
3112 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3112 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3113 ] + templateopts,
3113 ] + templateopts,
3114 _('hg heads [-r REV] [REV]...')),
3114 _('hg heads [-r REV] [REV]...')),
3115 "help": (help_, [], _('hg help [COMMAND]')),
3115 "help": (help_, [], _('hg help [COMMAND]')),
3116 "identify|id":
3116 "identify|id":
3117 (identify,
3117 (identify,
3118 [('r', 'rev', '', _('identify the specified rev')),
3118 [('r', 'rev', '', _('identify the specified rev')),
3119 ('n', 'num', None, _('show local revision number')),
3119 ('n', 'num', None, _('show local revision number')),
3120 ('i', 'id', None, _('show global revision id')),
3120 ('i', 'id', None, _('show global revision id')),
3121 ('b', 'branch', None, _('show branch')),
3121 ('b', 'branch', None, _('show branch')),
3122 ('t', 'tags', None, _('show tags'))],
3122 ('t', 'tags', None, _('show tags'))],
3123 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3123 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3124 "import|patch":
3124 "import|patch":
3125 (import_,
3125 (import_,
3126 [('p', 'strip', 1,
3126 [('p', 'strip', 1,
3127 _('directory strip option for patch. This has the same\n'
3127 _('directory strip option for patch. This has the same\n'
3128 'meaning as the corresponding patch option')),
3128 'meaning as the corresponding patch option')),
3129 ('b', 'base', '', _('base path')),
3129 ('b', 'base', '', _('base path')),
3130 ('f', 'force', None,
3130 ('f', 'force', None,
3131 _('skip check for outstanding uncommitted changes')),
3131 _('skip check for outstanding uncommitted changes')),
3132 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3132 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3133 ('', 'exact', None,
3133 ('', 'exact', None,
3134 _('apply patch to the nodes from which it was generated')),
3134 _('apply patch to the nodes from which it was generated')),
3135 ('', 'import-branch', None,
3135 ('', 'import-branch', None,
3136 _('Use any branch information in patch (implied by --exact)'))] +
3136 _('Use any branch information in patch (implied by --exact)'))] +
3137 commitopts + commitopts2,
3137 commitopts + commitopts2,
3138 _('hg import [OPTION]... PATCH...')),
3138 _('hg import [OPTION]... PATCH...')),
3139 "incoming|in":
3139 "incoming|in":
3140 (incoming,
3140 (incoming,
3141 [('f', 'force', None,
3141 [('f', 'force', None,
3142 _('run even when remote repository is unrelated')),
3142 _('run even when remote repository is unrelated')),
3143 ('n', 'newest-first', None, _('show newest record first')),
3143 ('n', 'newest-first', None, _('show newest record first')),
3144 ('', 'bundle', '', _('file to store the bundles into')),
3144 ('', 'bundle', '', _('file to store the bundles into')),
3145 ('r', 'rev', [],
3145 ('r', 'rev', [],
3146 _('a specific revision up to which you would like to pull')),
3146 _('a specific revision up to which you would like to pull')),
3147 ] + logopts + remoteopts,
3147 ] + logopts + remoteopts,
3148 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3148 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3149 ' [--bundle FILENAME] [SOURCE]')),
3149 ' [--bundle FILENAME] [SOURCE]')),
3150 "^init":
3150 "^init":
3151 (init,
3151 (init,
3152 remoteopts,
3152 remoteopts,
3153 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3153 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3154 "locate":
3154 "locate":
3155 (locate,
3155 (locate,
3156 [('r', 'rev', '', _('search the repository as it stood at rev')),
3156 [('r', 'rev', '', _('search the repository as it stood at rev')),
3157 ('0', 'print0', None,
3157 ('0', 'print0', None,
3158 _('end filenames with NUL, for use with xargs')),
3158 _('end filenames with NUL, for use with xargs')),
3159 ('f', 'fullpath', None,
3159 ('f', 'fullpath', None,
3160 _('print complete paths from the filesystem root')),
3160 _('print complete paths from the filesystem root')),
3161 ] + walkopts,
3161 ] + walkopts,
3162 _('hg locate [OPTION]... [PATTERN]...')),
3162 _('hg locate [OPTION]... [PATTERN]...')),
3163 "^log|history":
3163 "^log|history":
3164 (log,
3164 (log,
3165 [('f', 'follow', None,
3165 [('f', 'follow', None,
3166 _('follow changeset history, or file history across copies and renames')),
3166 _('follow changeset history, or file history across copies and renames')),
3167 ('', 'follow-first', None,
3167 ('', 'follow-first', None,
3168 _('only follow the first parent of merge changesets')),
3168 _('only follow the first parent of merge changesets')),
3169 ('d', 'date', '', _('show revs matching date spec')),
3169 ('d', 'date', '', _('show revs matching date spec')),
3170 ('C', 'copies', None, _('show copied files')),
3170 ('C', 'copies', None, _('show copied files')),
3171 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3171 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3172 ('r', 'rev', [], _('show the specified revision or range')),
3172 ('r', 'rev', [], _('show the specified revision or range')),
3173 ('', 'removed', None, _('include revs where files were removed')),
3173 ('', 'removed', None, _('include revs where files were removed')),
3174 ('m', 'only-merges', None, _('show only merges')),
3174 ('m', 'only-merges', None, _('show only merges')),
3175 ('b', 'only-branch', [],
3175 ('b', 'only-branch', [],
3176 _('show only changesets within the given named branch')),
3176 _('show only changesets within the given named branch')),
3177 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3177 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3178 ] + logopts + walkopts,
3178 ] + logopts + walkopts,
3179 _('hg log [OPTION]... [FILE]')),
3179 _('hg log [OPTION]... [FILE]')),
3180 "manifest":
3180 "manifest":
3181 (manifest,
3181 (manifest,
3182 [('r', 'rev', '', _('revision to display'))],
3182 [('r', 'rev', '', _('revision to display'))],
3183 _('hg manifest [-r REV]')),
3183 _('hg manifest [-r REV]')),
3184 "^merge":
3184 "^merge":
3185 (merge,
3185 (merge,
3186 [('f', 'force', None, _('force a merge with outstanding changes')),
3186 [('f', 'force', None, _('force a merge with outstanding changes')),
3187 ('r', 'rev', '', _('revision to merge')),
3187 ('r', 'rev', '', _('revision to merge')),
3188 ],
3188 ],
3189 _('hg merge [-f] [[-r] REV]')),
3189 _('hg merge [-f] [[-r] REV]')),
3190 "outgoing|out":
3190 "outgoing|out":
3191 (outgoing,
3191 (outgoing,
3192 [('f', 'force', None,
3192 [('f', 'force', None,
3193 _('run even when remote repository is unrelated')),
3193 _('run even when remote repository is unrelated')),
3194 ('r', 'rev', [],
3194 ('r', 'rev', [],
3195 _('a specific revision up to which you would like to push')),
3195 _('a specific revision up to which you would like to push')),
3196 ('n', 'newest-first', None, _('show newest record first')),
3196 ('n', 'newest-first', None, _('show newest record first')),
3197 ] + logopts + remoteopts,
3197 ] + logopts + remoteopts,
3198 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3198 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3199 "^parents":
3199 "^parents":
3200 (parents,
3200 (parents,
3201 [('r', 'rev', '', _('show parents from the specified rev')),
3201 [('r', 'rev', '', _('show parents from the specified rev')),
3202 ] + templateopts,
3202 ] + templateopts,
3203 _('hg parents [-r REV] [FILE]')),
3203 _('hg parents [-r REV] [FILE]')),
3204 "paths": (paths, [], _('hg paths [NAME]')),
3204 "paths": (paths, [], _('hg paths [NAME]')),
3205 "^pull":
3205 "^pull":
3206 (pull,
3206 (pull,
3207 [('u', 'update', None,
3207 [('u', 'update', None,
3208 _('update to new tip if changesets were pulled')),
3208 _('update to new tip if changesets were pulled')),
3209 ('f', 'force', None,
3209 ('f', 'force', None,
3210 _('run even when remote repository is unrelated')),
3210 _('run even when remote repository is unrelated')),
3211 ('r', 'rev', [],
3211 ('r', 'rev', [],
3212 _('a specific revision up to which you would like to pull')),
3212 _('a specific revision up to which you would like to pull')),
3213 ] + remoteopts,
3213 ] + remoteopts,
3214 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3214 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3215 "^push":
3215 "^push":
3216 (push,
3216 (push,
3217 [('f', 'force', None, _('force push')),
3217 [('f', 'force', None, _('force push')),
3218 ('r', 'rev', [],
3218 ('r', 'rev', [],
3219 _('a specific revision up to which you would like to push')),
3219 _('a specific revision up to which you would like to push')),
3220 ] + remoteopts,
3220 ] + remoteopts,
3221 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3221 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3222 "recover": (recover, [], _('hg recover')),
3222 "recover": (recover, [], _('hg recover')),
3223 "^remove|rm":
3223 "^remove|rm":
3224 (remove,
3224 (remove,
3225 [('A', 'after', None, _('record delete for missing files')),
3225 [('A', 'after', None, _('record delete for missing files')),
3226 ('f', 'force', None,
3226 ('f', 'force', None,
3227 _('remove (and delete) file even if added or modified')),
3227 _('remove (and delete) file even if added or modified')),
3228 ] + walkopts,
3228 ] + walkopts,
3229 _('hg remove [OPTION]... FILE...')),
3229 _('hg remove [OPTION]... FILE...')),
3230 "rename|mv":
3230 "rename|mv":
3231 (rename,
3231 (rename,
3232 [('A', 'after', None, _('record a rename that has already occurred')),
3232 [('A', 'after', None, _('record a rename that has already occurred')),
3233 ('f', 'force', None,
3233 ('f', 'force', None,
3234 _('forcibly copy over an existing managed file')),
3234 _('forcibly copy over an existing managed file')),
3235 ] + walkopts + dryrunopts,
3235 ] + walkopts + dryrunopts,
3236 _('hg rename [OPTION]... SOURCE... DEST')),
3236 _('hg rename [OPTION]... SOURCE... DEST')),
3237 "resolve":
3237 "resolve":
3238 (resolve,
3238 (resolve,
3239 [('l', 'list', None, _('list state of files needing merge')),
3239 [('l', 'list', None, _('list state of files needing merge')),
3240 ('m', 'mark', None, _('mark files as resolved')),
3240 ('m', 'mark', None, _('mark files as resolved')),
3241 ('u', 'unmark', None, _('unmark files as resolved'))],
3241 ('u', 'unmark', None, _('unmark files as resolved'))],
3242 ('hg resolve [OPTION] [FILES...]')),
3242 ('hg resolve [OPTION] [FILES...]')),
3243 "revert":
3243 "revert":
3244 (revert,
3244 (revert,
3245 [('a', 'all', None, _('revert all changes when no arguments given')),
3245 [('a', 'all', None, _('revert all changes when no arguments given')),
3246 ('d', 'date', '', _('tipmost revision matching date')),
3246 ('d', 'date', '', _('tipmost revision matching date')),
3247 ('r', 'rev', '', _('revision to revert to')),
3247 ('r', 'rev', '', _('revision to revert to')),
3248 ('', 'no-backup', None, _('do not save backup copies of files')),
3248 ('', 'no-backup', None, _('do not save backup copies of files')),
3249 ] + walkopts + dryrunopts,
3249 ] + walkopts + dryrunopts,
3250 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3250 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3251 "rollback": (rollback, [], _('hg rollback')),
3251 "rollback": (rollback, [], _('hg rollback')),
3252 "root": (root, [], _('hg root')),
3252 "root": (root, [], _('hg root')),
3253 "^serve":
3253 "^serve":
3254 (serve,
3254 (serve,
3255 [('A', 'accesslog', '', _('name of access log file to write to')),
3255 [('A', 'accesslog', '', _('name of access log file to write to')),
3256 ('d', 'daemon', None, _('run server in background')),
3256 ('d', 'daemon', None, _('run server in background')),
3257 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3257 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3258 ('E', 'errorlog', '', _('name of error log file to write to')),
3258 ('E', 'errorlog', '', _('name of error log file to write to')),
3259 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3259 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3260 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3260 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3261 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3261 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3262 ('n', 'name', '',
3262 ('n', 'name', '',
3263 _('name to show in web pages (default: working dir)')),
3263 _('name to show in web pages (default: working dir)')),
3264 ('', 'webdir-conf', '', _('name of the webdir config file'
3264 ('', 'webdir-conf', '', _('name of the webdir config file'
3265 ' (serve more than one repo)')),
3265 ' (serve more than one repo)')),
3266 ('', 'pid-file', '', _('name of file to write process ID to')),
3266 ('', 'pid-file', '', _('name of file to write process ID to')),
3267 ('', 'stdio', None, _('for remote clients')),
3267 ('', 'stdio', None, _('for remote clients')),
3268 ('t', 'templates', '', _('web templates to use')),
3268 ('t', 'templates', '', _('web templates to use')),
3269 ('', 'style', '', _('template style to use')),
3269 ('', 'style', '', _('template style to use')),
3270 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3270 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3271 ('', 'certificate', '', _('SSL certificate file'))],
3271 ('', 'certificate', '', _('SSL certificate file'))],
3272 _('hg serve [OPTION]...')),
3272 _('hg serve [OPTION]...')),
3273 "showconfig|debugconfig":
3273 "showconfig|debugconfig":
3274 (showconfig,
3274 (showconfig,
3275 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3275 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3276 _('hg showconfig [-u] [NAME]...')),
3276 _('hg showconfig [-u] [NAME]...')),
3277 "^status|st":
3277 "^status|st":
3278 (status,
3278 (status,
3279 [('A', 'all', None, _('show status of all files')),
3279 [('A', 'all', None, _('show status of all files')),
3280 ('m', 'modified', None, _('show only modified files')),
3280 ('m', 'modified', None, _('show only modified files')),
3281 ('a', 'added', None, _('show only added files')),
3281 ('a', 'added', None, _('show only added files')),
3282 ('r', 'removed', None, _('show only removed files')),
3282 ('r', 'removed', None, _('show only removed files')),
3283 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3283 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3284 ('c', 'clean', None, _('show only files without changes')),
3284 ('c', 'clean', None, _('show only files without changes')),
3285 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3285 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3286 ('i', 'ignored', None, _('show only ignored files')),
3286 ('i', 'ignored', None, _('show only ignored files')),
3287 ('n', 'no-status', None, _('hide status prefix')),
3287 ('n', 'no-status', None, _('hide status prefix')),
3288 ('C', 'copies', None, _('show source of copied files')),
3288 ('C', 'copies', None, _('show source of copied files')),
3289 ('0', 'print0', None,
3289 ('0', 'print0', None,
3290 _('end filenames with NUL, for use with xargs')),
3290 _('end filenames with NUL, for use with xargs')),
3291 ('', 'rev', [], _('show difference from revision')),
3291 ('', 'rev', [], _('show difference from revision')),
3292 ] + walkopts,
3292 ] + walkopts,
3293 _('hg status [OPTION]... [FILE]...')),
3293 _('hg status [OPTION]... [FILE]...')),
3294 "tag":
3294 "tag":
3295 (tag,
3295 (tag,
3296 [('f', 'force', None, _('replace existing tag')),
3296 [('f', 'force', None, _('replace existing tag')),
3297 ('l', 'local', None, _('make the tag local')),
3297 ('l', 'local', None, _('make the tag local')),
3298 ('r', 'rev', '', _('revision to tag')),
3298 ('r', 'rev', '', _('revision to tag')),
3299 ('', 'remove', None, _('remove a tag')),
3299 ('', 'remove', None, _('remove a tag')),
3300 # -l/--local is already there, commitopts cannot be used
3300 # -l/--local is already there, commitopts cannot be used
3301 ('m', 'message', '', _('use <text> as commit message')),
3301 ('m', 'message', '', _('use <text> as commit message')),
3302 ] + commitopts2,
3302 ] + commitopts2,
3303 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3303 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3304 "tags": (tags, [], _('hg tags')),
3304 "tags": (tags, [], _('hg tags')),
3305 "tip":
3305 "tip":
3306 (tip,
3306 (tip,
3307 [('p', 'patch', None, _('show patch')),
3307 [('p', 'patch', None, _('show patch')),
3308 ] + templateopts,
3308 ] + templateopts,
3309 _('hg tip [-p]')),
3309 _('hg tip [-p]')),
3310 "unbundle":
3310 "unbundle":
3311 (unbundle,
3311 (unbundle,
3312 [('u', 'update', None,
3312 [('u', 'update', None,
3313 _('update to new tip if changesets were unbundled'))],
3313 _('update to new tip if changesets were unbundled'))],
3314 _('hg unbundle [-u] FILE...')),
3314 _('hg unbundle [-u] FILE...')),
3315 "^update|up|checkout|co":
3315 "^update|up|checkout|co":
3316 (update,
3316 (update,
3317 [('C', 'clean', None, _('overwrite locally modified files')),
3317 [('C', 'clean', None, _('overwrite locally modified files')),
3318 ('d', 'date', '', _('tipmost revision matching date')),
3318 ('d', 'date', '', _('tipmost revision matching date')),
3319 ('r', 'rev', '', _('revision'))],
3319 ('r', 'rev', '', _('revision'))],
3320 _('hg update [-C] [-d DATE] [[-r] REV]')),
3320 _('hg update [-C] [-d DATE] [[-r] REV]')),
3321 "verify": (verify, [], _('hg verify')),
3321 "verify": (verify, [], _('hg verify')),
3322 "version": (version_, [], _('hg version')),
3322 "version": (version_, [], _('hg version')),
3323 }
3323 }
3324
3324
3325 norepo = ("clone init version help debugcomplete debugdata"
3325 norepo = ("clone init version help debugcomplete debugdata"
3326 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3326 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3327 optionalrepo = ("identify paths serve showconfig debugancestor")
3327 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,2137 +1,2137
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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import 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
10 import repo, changegroup
11 import changelog, dirstate, filelog, manifest, context, weakref
11 import changelog, dirstate, filelog, manifest, context, weakref
12 import lock, transaction, stat, errno, ui
12 import lock, transaction, stat, errno, ui
13 import os, revlog, time, util, extensions, hook, inspect
13 import os, revlog, time, util, extensions, hook, inspect
14
14
15 class localrepository(repo.repository):
15 class localrepository(repo.repository):
16 capabilities = util.set(('lookup', 'changegroupsubset'))
16 capabilities = util.set(('lookup', 'changegroupsubset'))
17 supported = ('revlogv1', 'store')
17 supported = ('revlogv1', 'store')
18
18
19 def __init__(self, parentui, path=None, create=0):
19 def __init__(self, parentui, path=None, create=0):
20 repo.repository.__init__(self)
20 repo.repository.__init__(self)
21 self.root = os.path.realpath(path)
21 self.root = os.path.realpath(path)
22 self.path = os.path.join(self.root, ".hg")
22 self.path = os.path.join(self.root, ".hg")
23 self.origroot = path
23 self.origroot = path
24 self.opener = util.opener(self.path)
24 self.opener = util.opener(self.path)
25 self.wopener = util.opener(self.root)
25 self.wopener = util.opener(self.root)
26
26
27 if not os.path.isdir(self.path):
27 if not os.path.isdir(self.path):
28 if create:
28 if create:
29 if not os.path.exists(path):
29 if not os.path.exists(path):
30 os.mkdir(path)
30 os.mkdir(path)
31 os.mkdir(self.path)
31 os.mkdir(self.path)
32 requirements = ["revlogv1"]
32 requirements = ["revlogv1"]
33 if parentui.configbool('format', 'usestore', True):
33 if parentui.configbool('format', 'usestore', True):
34 os.mkdir(os.path.join(self.path, "store"))
34 os.mkdir(os.path.join(self.path, "store"))
35 requirements.append("store")
35 requirements.append("store")
36 # create an invalid changelog
36 # create an invalid changelog
37 self.opener("00changelog.i", "a").write(
37 self.opener("00changelog.i", "a").write(
38 '\0\0\0\2' # represents revlogv2
38 '\0\0\0\2' # represents revlogv2
39 ' dummy changelog to prevent using the old repo layout'
39 ' dummy changelog to prevent using the old repo layout'
40 )
40 )
41 reqfile = self.opener("requires", "w")
41 reqfile = self.opener("requires", "w")
42 for r in requirements:
42 for r in requirements:
43 reqfile.write("%s\n" % r)
43 reqfile.write("%s\n" % r)
44 reqfile.close()
44 reqfile.close()
45 else:
45 else:
46 raise repo.RepoError(_("repository %s not found") % path)
46 raise repo.RepoError(_("repository %s not found") % path)
47 elif create:
47 elif create:
48 raise repo.RepoError(_("repository %s already exists") % path)
48 raise repo.RepoError(_("repository %s already exists") % path)
49 else:
49 else:
50 # find requirements
50 # find requirements
51 try:
51 try:
52 requirements = self.opener("requires").read().splitlines()
52 requirements = self.opener("requires").read().splitlines()
53 except IOError, inst:
53 except IOError, inst:
54 if inst.errno != errno.ENOENT:
54 if inst.errno != errno.ENOENT:
55 raise
55 raise
56 requirements = []
56 requirements = []
57 # check them
57 # check them
58 for r in requirements:
58 for r in requirements:
59 if r not in self.supported:
59 if r not in self.supported:
60 raise repo.RepoError(_("requirement '%s' not supported") % r)
60 raise repo.RepoError(_("requirement '%s' not supported") % r)
61
61
62 # setup store
62 # setup store
63 if "store" in requirements:
63 if "store" in requirements:
64 self.encodefn = util.encodefilename
64 self.encodefn = util.encodefilename
65 self.decodefn = util.decodefilename
65 self.decodefn = util.decodefilename
66 self.spath = os.path.join(self.path, "store")
66 self.spath = os.path.join(self.path, "store")
67 else:
67 else:
68 self.encodefn = lambda x: x
68 self.encodefn = lambda x: x
69 self.decodefn = lambda x: x
69 self.decodefn = lambda x: x
70 self.spath = self.path
70 self.spath = self.path
71
71
72 try:
72 try:
73 # files in .hg/ will be created using this mode
73 # files in .hg/ will be created using this mode
74 mode = os.stat(self.spath).st_mode
74 mode = os.stat(self.spath).st_mode
75 # avoid some useless chmods
75 # avoid some useless chmods
76 if (0777 & ~util._umask) == (0777 & mode):
76 if (0777 & ~util._umask) == (0777 & mode):
77 mode = None
77 mode = None
78 except OSError:
78 except OSError:
79 mode = None
79 mode = None
80
80
81 self._createmode = mode
81 self._createmode = mode
82 self.opener.createmode = mode
82 self.opener.createmode = mode
83 sopener = util.opener(self.spath)
83 sopener = util.opener(self.spath)
84 sopener.createmode = mode
84 sopener.createmode = mode
85 self.sopener = util.encodedopener(sopener, self.encodefn)
85 self.sopener = util.encodedopener(sopener, self.encodefn)
86
86
87 self.ui = ui.ui(parentui=parentui)
87 self.ui = ui.ui(parentui=parentui)
88 try:
88 try:
89 self.ui.readconfig(self.join("hgrc"), self.root)
89 self.ui.readconfig(self.join("hgrc"), self.root)
90 extensions.loadall(self.ui)
90 extensions.loadall(self.ui)
91 except IOError:
91 except IOError:
92 pass
92 pass
93
93
94 self.tagscache = None
94 self.tagscache = None
95 self._tagstypecache = None
95 self._tagstypecache = None
96 self.branchcache = None
96 self.branchcache = None
97 self._ubranchcache = None # UTF-8 version of branchcache
97 self._ubranchcache = None # UTF-8 version of branchcache
98 self._branchcachetip = None
98 self._branchcachetip = None
99 self.nodetagscache = None
99 self.nodetagscache = None
100 self.filterpats = {}
100 self.filterpats = {}
101 self._datafilters = {}
101 self._datafilters = {}
102 self._transref = self._lockref = self._wlockref = None
102 self._transref = self._lockref = self._wlockref = None
103
103
104 def __getattr__(self, name):
104 def __getattr__(self, name):
105 if name == 'changelog':
105 if name == 'changelog':
106 self.changelog = changelog.changelog(self.sopener)
106 self.changelog = changelog.changelog(self.sopener)
107 self.sopener.defversion = self.changelog.version
107 self.sopener.defversion = self.changelog.version
108 return self.changelog
108 return self.changelog
109 if name == 'manifest':
109 if name == 'manifest':
110 self.changelog
110 self.changelog
111 self.manifest = manifest.manifest(self.sopener)
111 self.manifest = manifest.manifest(self.sopener)
112 return self.manifest
112 return self.manifest
113 if name == 'dirstate':
113 if name == 'dirstate':
114 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
114 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
115 return self.dirstate
115 return self.dirstate
116 else:
116 else:
117 raise AttributeError, name
117 raise AttributeError, name
118
118
119 def url(self):
119 def url(self):
120 return 'file:' + self.root
120 return 'file:' + self.root
121
121
122 def hook(self, name, throw=False, **args):
122 def hook(self, name, throw=False, **args):
123 return hook.hook(self.ui, self, name, throw, **args)
123 return hook.hook(self.ui, self, name, throw, **args)
124
124
125 tag_disallowed = ':\r\n'
125 tag_disallowed = ':\r\n'
126
126
127 def _tag(self, names, node, message, local, user, date, parent=None,
127 def _tag(self, names, node, message, local, user, date, parent=None,
128 extra={}):
128 extra={}):
129 use_dirstate = parent is None
129 use_dirstate = parent is None
130
130
131 if isinstance(names, str):
131 if isinstance(names, str):
132 allchars = names
132 allchars = names
133 names = (names,)
133 names = (names,)
134 else:
134 else:
135 allchars = ''.join(names)
135 allchars = ''.join(names)
136 for c in self.tag_disallowed:
136 for c in self.tag_disallowed:
137 if c in allchars:
137 if c in allchars:
138 raise util.Abort(_('%r cannot be used in a tag name') % c)
138 raise util.Abort(_('%r cannot be used in a tag name') % c)
139
139
140 for name in names:
140 for name in names:
141 self.hook('pretag', throw=True, node=hex(node), tag=name,
141 self.hook('pretag', throw=True, node=hex(node), tag=name,
142 local=local)
142 local=local)
143
143
144 def writetags(fp, names, munge, prevtags):
144 def writetags(fp, names, munge, prevtags):
145 fp.seek(0, 2)
145 fp.seek(0, 2)
146 if prevtags and prevtags[-1] != '\n':
146 if prevtags and prevtags[-1] != '\n':
147 fp.write('\n')
147 fp.write('\n')
148 for name in names:
148 for name in names:
149 fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
149 fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
150 fp.close()
150 fp.close()
151
151
152 prevtags = ''
152 prevtags = ''
153 if local:
153 if local:
154 try:
154 try:
155 fp = self.opener('localtags', 'r+')
155 fp = self.opener('localtags', 'r+')
156 except IOError, err:
156 except IOError, err:
157 fp = self.opener('localtags', 'a')
157 fp = self.opener('localtags', 'a')
158 else:
158 else:
159 prevtags = fp.read()
159 prevtags = fp.read()
160
160
161 # local tags are stored in the current charset
161 # local tags are stored in the current charset
162 writetags(fp, names, None, prevtags)
162 writetags(fp, names, None, prevtags)
163 for name in names:
163 for name in names:
164 self.hook('tag', node=hex(node), tag=name, local=local)
164 self.hook('tag', node=hex(node), tag=name, local=local)
165 return
165 return
166
166
167 if use_dirstate:
167 if use_dirstate:
168 try:
168 try:
169 fp = self.wfile('.hgtags', 'rb+')
169 fp = self.wfile('.hgtags', 'rb+')
170 except IOError, err:
170 except IOError, err:
171 fp = self.wfile('.hgtags', 'ab')
171 fp = self.wfile('.hgtags', 'ab')
172 else:
172 else:
173 prevtags = fp.read()
173 prevtags = fp.read()
174 else:
174 else:
175 try:
175 try:
176 prevtags = self.filectx('.hgtags', parent).data()
176 prevtags = self.filectx('.hgtags', parent).data()
177 except revlog.LookupError:
177 except revlog.LookupError:
178 pass
178 pass
179 fp = self.wfile('.hgtags', 'wb')
179 fp = self.wfile('.hgtags', 'wb')
180 if prevtags:
180 if prevtags:
181 fp.write(prevtags)
181 fp.write(prevtags)
182
182
183 # committed tags are stored in UTF-8
183 # committed tags are stored in UTF-8
184 writetags(fp, names, util.fromlocal, prevtags)
184 writetags(fp, names, util.fromlocal, prevtags)
185
185
186 if use_dirstate and '.hgtags' not in self.dirstate:
186 if use_dirstate and '.hgtags' not in self.dirstate:
187 self.add(['.hgtags'])
187 self.add(['.hgtags'])
188
188
189 tagnode = self.commit(['.hgtags'], message, user, date, p1=parent,
189 tagnode = self.commit(['.hgtags'], message, user, date, p1=parent,
190 extra=extra)
190 extra=extra)
191
191
192 for name in names:
192 for name in names:
193 self.hook('tag', node=hex(node), tag=name, local=local)
193 self.hook('tag', node=hex(node), tag=name, local=local)
194
194
195 return tagnode
195 return tagnode
196
196
197 def tag(self, names, node, message, local, user, date):
197 def tag(self, names, node, message, local, user, date):
198 '''tag a revision with one or more symbolic names.
198 '''tag a revision with one or more symbolic names.
199
199
200 names is a list of strings or, when adding a single tag, names may be a
200 names is a list of strings or, when adding a single tag, names may be a
201 string.
201 string.
202
202
203 if local is True, the tags are stored in a per-repository file.
203 if local is True, the tags are stored in a per-repository file.
204 otherwise, they are stored in the .hgtags file, and a new
204 otherwise, they are stored in the .hgtags file, and a new
205 changeset is committed with the change.
205 changeset is committed with the change.
206
206
207 keyword arguments:
207 keyword arguments:
208
208
209 local: whether to store tags in non-version-controlled file
209 local: whether to store tags in non-version-controlled file
210 (default False)
210 (default False)
211
211
212 message: commit message to use if committing
212 message: commit message to use if committing
213
213
214 user: name of user to use if committing
214 user: name of user to use if committing
215
215
216 date: date tuple to use if committing'''
216 date: date tuple to use if committing'''
217
217
218 for x in self.status()[:5]:
218 for x in self.status()[:5]:
219 if '.hgtags' in x:
219 if '.hgtags' in x:
220 raise util.Abort(_('working copy of .hgtags is changed '
220 raise util.Abort(_('working copy of .hgtags is changed '
221 '(please commit .hgtags manually)'))
221 '(please commit .hgtags manually)'))
222
222
223 self._tag(names, node, message, local, user, date)
223 self._tag(names, node, message, local, user, date)
224
224
225 def tags(self):
225 def tags(self):
226 '''return a mapping of tag to node'''
226 '''return a mapping of tag to node'''
227 if self.tagscache:
227 if self.tagscache:
228 return self.tagscache
228 return self.tagscache
229
229
230 globaltags = {}
230 globaltags = {}
231 tagtypes = {}
231 tagtypes = {}
232
232
233 def readtags(lines, fn, tagtype):
233 def readtags(lines, fn, tagtype):
234 filetags = {}
234 filetags = {}
235 count = 0
235 count = 0
236
236
237 def warn(msg):
237 def warn(msg):
238 self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
238 self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
239
239
240 for l in lines:
240 for l in lines:
241 count += 1
241 count += 1
242 if not l:
242 if not l:
243 continue
243 continue
244 s = l.split(" ", 1)
244 s = l.split(" ", 1)
245 if len(s) != 2:
245 if len(s) != 2:
246 warn(_("cannot parse entry"))
246 warn(_("cannot parse entry"))
247 continue
247 continue
248 node, key = s
248 node, key = s
249 key = util.tolocal(key.strip()) # stored in UTF-8
249 key = util.tolocal(key.strip()) # stored in UTF-8
250 try:
250 try:
251 bin_n = bin(node)
251 bin_n = bin(node)
252 except TypeError:
252 except TypeError:
253 warn(_("node '%s' is not well formed") % node)
253 warn(_("node '%s' is not well formed") % node)
254 continue
254 continue
255 if bin_n not in self.changelog.nodemap:
255 if bin_n not in self.changelog.nodemap:
256 warn(_("tag '%s' refers to unknown node") % key)
256 warn(_("tag '%s' refers to unknown node") % key)
257 continue
257 continue
258
258
259 h = []
259 h = []
260 if key in filetags:
260 if key in filetags:
261 n, h = filetags[key]
261 n, h = filetags[key]
262 h.append(n)
262 h.append(n)
263 filetags[key] = (bin_n, h)
263 filetags[key] = (bin_n, h)
264
264
265 for k, nh in filetags.items():
265 for k, nh in filetags.items():
266 if k not in globaltags:
266 if k not in globaltags:
267 globaltags[k] = nh
267 globaltags[k] = nh
268 tagtypes[k] = tagtype
268 tagtypes[k] = tagtype
269 continue
269 continue
270
270
271 # we prefer the global tag if:
271 # we prefer the global tag if:
272 # it supercedes us OR
272 # it supercedes us OR
273 # mutual supercedes and it has a higher rank
273 # mutual supercedes and it has a higher rank
274 # otherwise we win because we're tip-most
274 # otherwise we win because we're tip-most
275 an, ah = nh
275 an, ah = nh
276 bn, bh = globaltags[k]
276 bn, bh = globaltags[k]
277 if (bn != an and an in bh and
277 if (bn != an and an in bh and
278 (bn not in ah or len(bh) > len(ah))):
278 (bn not in ah or len(bh) > len(ah))):
279 an = bn
279 an = bn
280 ah.extend([n for n in bh if n not in ah])
280 ah.extend([n for n in bh if n not in ah])
281 globaltags[k] = an, ah
281 globaltags[k] = an, ah
282 tagtypes[k] = tagtype
282 tagtypes[k] = tagtype
283
283
284 # read the tags file from each head, ending with the tip
284 # read the tags file from each head, ending with the tip
285 f = None
285 f = None
286 for rev, node, fnode in self._hgtagsnodes():
286 for rev, node, fnode in self._hgtagsnodes():
287 f = (f and f.filectx(fnode) or
287 f = (f and f.filectx(fnode) or
288 self.filectx('.hgtags', fileid=fnode))
288 self.filectx('.hgtags', fileid=fnode))
289 readtags(f.data().splitlines(), f, "global")
289 readtags(f.data().splitlines(), f, "global")
290
290
291 try:
291 try:
292 data = util.fromlocal(self.opener("localtags").read())
292 data = util.fromlocal(self.opener("localtags").read())
293 # localtags are stored in the local character set
293 # localtags are stored in the local character set
294 # while the internal tag table is stored in UTF-8
294 # while the internal tag table is stored in UTF-8
295 readtags(data.splitlines(), "localtags", "local")
295 readtags(data.splitlines(), "localtags", "local")
296 except IOError:
296 except IOError:
297 pass
297 pass
298
298
299 self.tagscache = {}
299 self.tagscache = {}
300 self._tagstypecache = {}
300 self._tagstypecache = {}
301 for k,nh in globaltags.items():
301 for k,nh in globaltags.items():
302 n = nh[0]
302 n = nh[0]
303 if n != nullid:
303 if n != nullid:
304 self.tagscache[k] = n
304 self.tagscache[k] = n
305 self._tagstypecache[k] = tagtypes[k]
305 self._tagstypecache[k] = tagtypes[k]
306 self.tagscache['tip'] = self.changelog.tip()
306 self.tagscache['tip'] = self.changelog.tip()
307
307
308 return self.tagscache
308 return self.tagscache
309
309
310 def tagtype(self, tagname):
310 def tagtype(self, tagname):
311 '''
311 '''
312 return the type of the given tag. result can be:
312 return the type of the given tag. result can be:
313
313
314 'local' : a local tag
314 'local' : a local tag
315 'global' : a global tag
315 'global' : a global tag
316 None : tag does not exist
316 None : tag does not exist
317 '''
317 '''
318
318
319 self.tags()
319 self.tags()
320
320
321 return self._tagstypecache.get(tagname)
321 return self._tagstypecache.get(tagname)
322
322
323 def _hgtagsnodes(self):
323 def _hgtagsnodes(self):
324 heads = self.heads()
324 heads = self.heads()
325 heads.reverse()
325 heads.reverse()
326 last = {}
326 last = {}
327 ret = []
327 ret = []
328 for node in heads:
328 for node in heads:
329 c = self.changectx(node)
329 c = self.changectx(node)
330 rev = c.rev()
330 rev = c.rev()
331 try:
331 try:
332 fnode = c.filenode('.hgtags')
332 fnode = c.filenode('.hgtags')
333 except revlog.LookupError:
333 except revlog.LookupError:
334 continue
334 continue
335 ret.append((rev, node, fnode))
335 ret.append((rev, node, fnode))
336 if fnode in last:
336 if fnode in last:
337 ret[last[fnode]] = None
337 ret[last[fnode]] = None
338 last[fnode] = len(ret) - 1
338 last[fnode] = len(ret) - 1
339 return [item for item in ret if item]
339 return [item for item in ret if item]
340
340
341 def tagslist(self):
341 def tagslist(self):
342 '''return a list of tags ordered by revision'''
342 '''return a list of tags ordered by revision'''
343 l = []
343 l = []
344 for t, n in self.tags().items():
344 for t, n in self.tags().items():
345 try:
345 try:
346 r = self.changelog.rev(n)
346 r = self.changelog.rev(n)
347 except:
347 except:
348 r = -2 # sort to the beginning of the list if unknown
348 r = -2 # sort to the beginning of the list if unknown
349 l.append((r, t, n))
349 l.append((r, t, n))
350 l.sort()
350 l.sort()
351 return [(t, n) for r, t, n in l]
351 return [(t, n) for r, t, n in l]
352
352
353 def nodetags(self, node):
353 def nodetags(self, node):
354 '''return the tags associated with a node'''
354 '''return the tags associated with a node'''
355 if not self.nodetagscache:
355 if not self.nodetagscache:
356 self.nodetagscache = {}
356 self.nodetagscache = {}
357 for t, n in self.tags().items():
357 for t, n in self.tags().items():
358 self.nodetagscache.setdefault(n, []).append(t)
358 self.nodetagscache.setdefault(n, []).append(t)
359 return self.nodetagscache.get(node, [])
359 return self.nodetagscache.get(node, [])
360
360
361 def _branchtags(self, partial, lrev):
361 def _branchtags(self, partial, lrev):
362 tiprev = self.changelog.count() - 1
362 tiprev = self.changelog.count() - 1
363 if lrev != tiprev:
363 if lrev != tiprev:
364 self._updatebranchcache(partial, lrev+1, tiprev+1)
364 self._updatebranchcache(partial, lrev+1, tiprev+1)
365 self._writebranchcache(partial, self.changelog.tip(), tiprev)
365 self._writebranchcache(partial, self.changelog.tip(), tiprev)
366
366
367 return partial
367 return partial
368
368
369 def branchtags(self):
369 def branchtags(self):
370 tip = self.changelog.tip()
370 tip = self.changelog.tip()
371 if self.branchcache is not None and self._branchcachetip == tip:
371 if self.branchcache is not None and self._branchcachetip == tip:
372 return self.branchcache
372 return self.branchcache
373
373
374 oldtip = self._branchcachetip
374 oldtip = self._branchcachetip
375 self._branchcachetip = tip
375 self._branchcachetip = tip
376 if self.branchcache is None:
376 if self.branchcache is None:
377 self.branchcache = {} # avoid recursion in changectx
377 self.branchcache = {} # avoid recursion in changectx
378 else:
378 else:
379 self.branchcache.clear() # keep using the same dict
379 self.branchcache.clear() # keep using the same dict
380 if oldtip is None or oldtip not in self.changelog.nodemap:
380 if oldtip is None or oldtip not in self.changelog.nodemap:
381 partial, last, lrev = self._readbranchcache()
381 partial, last, lrev = self._readbranchcache()
382 else:
382 else:
383 lrev = self.changelog.rev(oldtip)
383 lrev = self.changelog.rev(oldtip)
384 partial = self._ubranchcache
384 partial = self._ubranchcache
385
385
386 self._branchtags(partial, lrev)
386 self._branchtags(partial, lrev)
387
387
388 # the branch cache is stored on disk as UTF-8, but in the local
388 # the branch cache is stored on disk as UTF-8, but in the local
389 # charset internally
389 # charset internally
390 for k, v in partial.items():
390 for k, v in partial.items():
391 self.branchcache[util.tolocal(k)] = v
391 self.branchcache[util.tolocal(k)] = v
392 self._ubranchcache = partial
392 self._ubranchcache = partial
393 return self.branchcache
393 return self.branchcache
394
394
395 def _readbranchcache(self):
395 def _readbranchcache(self):
396 partial = {}
396 partial = {}
397 try:
397 try:
398 f = self.opener("branch.cache")
398 f = self.opener("branch.cache")
399 lines = f.read().split('\n')
399 lines = f.read().split('\n')
400 f.close()
400 f.close()
401 except (IOError, OSError):
401 except (IOError, OSError):
402 return {}, nullid, nullrev
402 return {}, nullid, nullrev
403
403
404 try:
404 try:
405 last, lrev = lines.pop(0).split(" ", 1)
405 last, lrev = lines.pop(0).split(" ", 1)
406 last, lrev = bin(last), int(lrev)
406 last, lrev = bin(last), int(lrev)
407 if not (lrev < self.changelog.count() and
407 if not (lrev < self.changelog.count() and
408 self.changelog.node(lrev) == last): # sanity check
408 self.changelog.node(lrev) == last): # sanity check
409 # invalidate the cache
409 # invalidate the cache
410 raise ValueError('invalidating branch cache (tip differs)')
410 raise ValueError('invalidating branch cache (tip differs)')
411 for l in lines:
411 for l in lines:
412 if not l: continue
412 if not l: continue
413 node, label = l.split(" ", 1)
413 node, label = l.split(" ", 1)
414 partial[label.strip()] = bin(node)
414 partial[label.strip()] = bin(node)
415 except (KeyboardInterrupt, util.SignalInterrupt):
415 except (KeyboardInterrupt, util.SignalInterrupt):
416 raise
416 raise
417 except Exception, inst:
417 except Exception, inst:
418 if self.ui.debugflag:
418 if self.ui.debugflag:
419 self.ui.warn(str(inst), '\n')
419 self.ui.warn(str(inst), '\n')
420 partial, last, lrev = {}, nullid, nullrev
420 partial, last, lrev = {}, nullid, nullrev
421 return partial, last, lrev
421 return partial, last, lrev
422
422
423 def _writebranchcache(self, branches, tip, tiprev):
423 def _writebranchcache(self, branches, tip, tiprev):
424 try:
424 try:
425 f = self.opener("branch.cache", "w", atomictemp=True)
425 f = self.opener("branch.cache", "w", atomictemp=True)
426 f.write("%s %s\n" % (hex(tip), tiprev))
426 f.write("%s %s\n" % (hex(tip), tiprev))
427 for label, node in branches.iteritems():
427 for label, node in branches.iteritems():
428 f.write("%s %s\n" % (hex(node), label))
428 f.write("%s %s\n" % (hex(node), label))
429 f.rename()
429 f.rename()
430 except (IOError, OSError):
430 except (IOError, OSError):
431 pass
431 pass
432
432
433 def _updatebranchcache(self, partial, start, end):
433 def _updatebranchcache(self, partial, start, end):
434 for r in xrange(start, end):
434 for r in xrange(start, end):
435 c = self.changectx(r)
435 c = self.changectx(r)
436 b = c.branch()
436 b = c.branch()
437 partial[b] = c.node()
437 partial[b] = c.node()
438
438
439 def lookup(self, key):
439 def lookup(self, key):
440 if key == '.':
440 if key == '.':
441 key, second = self.dirstate.parents()
441 key, second = self.dirstate.parents()
442 if key == nullid:
442 if key == nullid:
443 raise repo.RepoError(_("no revision checked out"))
443 raise repo.RepoError(_("no revision checked out"))
444 if second != nullid:
444 if second != nullid:
445 self.ui.warn(_("warning: working directory has two parents, "
445 self.ui.warn(_("warning: working directory has two parents, "
446 "tag '.' uses the first\n"))
446 "tag '.' uses the first\n"))
447 elif key == 'null':
447 elif key == 'null':
448 return nullid
448 return nullid
449 n = self.changelog._match(key)
449 n = self.changelog._match(key)
450 if n:
450 if n:
451 return n
451 return n
452 if key in self.tags():
452 if key in self.tags():
453 return self.tags()[key]
453 return self.tags()[key]
454 if key in self.branchtags():
454 if key in self.branchtags():
455 return self.branchtags()[key]
455 return self.branchtags()[key]
456 n = self.changelog._partialmatch(key)
456 n = self.changelog._partialmatch(key)
457 if n:
457 if n:
458 return n
458 return n
459 try:
459 try:
460 if len(key) == 20:
460 if len(key) == 20:
461 key = hex(key)
461 key = hex(key)
462 except:
462 except:
463 pass
463 pass
464 raise repo.RepoError(_("unknown revision '%s'") % key)
464 raise repo.RepoError(_("unknown revision '%s'") % key)
465
465
466 def local(self):
466 def local(self):
467 return True
467 return True
468
468
469 def join(self, f):
469 def join(self, f):
470 return os.path.join(self.path, f)
470 return os.path.join(self.path, f)
471
471
472 def sjoin(self, f):
472 def sjoin(self, f):
473 f = self.encodefn(f)
473 f = self.encodefn(f)
474 return os.path.join(self.spath, f)
474 return os.path.join(self.spath, f)
475
475
476 def wjoin(self, f):
476 def wjoin(self, f):
477 return os.path.join(self.root, f)
477 return os.path.join(self.root, f)
478
478
479 def rjoin(self, f):
479 def rjoin(self, f):
480 return os.path.join(self.root, util.pconvert(f))
480 return os.path.join(self.root, util.pconvert(f))
481
481
482 def file(self, f):
482 def file(self, f):
483 if f[0] == '/':
483 if f[0] == '/':
484 f = f[1:]
484 f = f[1:]
485 return filelog.filelog(self.sopener, f)
485 return filelog.filelog(self.sopener, f)
486
486
487 def changectx(self, changeid=None):
487 def changectx(self, changeid=None):
488 return context.changectx(self, changeid)
488 return context.changectx(self, changeid)
489
489
490 def workingctx(self):
490 def workingctx(self):
491 return context.workingctx(self)
491 return context.workingctx(self)
492
492
493 def parents(self, changeid=None):
493 def parents(self, changeid=None):
494 '''
494 '''
495 get list of changectxs for parents of changeid or working directory
495 get list of changectxs for parents of changeid or working directory
496 '''
496 '''
497 if changeid is None:
497 if changeid is None:
498 pl = self.dirstate.parents()
498 pl = self.dirstate.parents()
499 else:
499 else:
500 n = self.changelog.lookup(changeid)
500 n = self.changelog.lookup(changeid)
501 pl = self.changelog.parents(n)
501 pl = self.changelog.parents(n)
502 if pl[1] == nullid:
502 if pl[1] == nullid:
503 return [self.changectx(pl[0])]
503 return [self.changectx(pl[0])]
504 return [self.changectx(pl[0]), self.changectx(pl[1])]
504 return [self.changectx(pl[0]), self.changectx(pl[1])]
505
505
506 def filectx(self, path, changeid=None, fileid=None):
506 def filectx(self, path, changeid=None, fileid=None):
507 """changeid can be a changeset revision, node, or tag.
507 """changeid can be a changeset revision, node, or tag.
508 fileid can be a file revision or node."""
508 fileid can be a file revision or node."""
509 return context.filectx(self, path, changeid, fileid)
509 return context.filectx(self, path, changeid, fileid)
510
510
511 def getcwd(self):
511 def getcwd(self):
512 return self.dirstate.getcwd()
512 return self.dirstate.getcwd()
513
513
514 def pathto(self, f, cwd=None):
514 def pathto(self, f, cwd=None):
515 return self.dirstate.pathto(f, cwd)
515 return self.dirstate.pathto(f, cwd)
516
516
517 def wfile(self, f, mode='r'):
517 def wfile(self, f, mode='r'):
518 return self.wopener(f, mode)
518 return self.wopener(f, mode)
519
519
520 def _link(self, f):
520 def _link(self, f):
521 return os.path.islink(self.wjoin(f))
521 return os.path.islink(self.wjoin(f))
522
522
523 def _filter(self, filter, filename, data):
523 def _filter(self, filter, filename, data):
524 if filter not in self.filterpats:
524 if filter not in self.filterpats:
525 l = []
525 l = []
526 for pat, cmd in self.ui.configitems(filter):
526 for pat, cmd in self.ui.configitems(filter):
527 mf = util.matcher(self.root, "", [pat], [], [])[1]
527 mf = util.matcher(self.root, "", [pat], [], [])[1]
528 fn = None
528 fn = None
529 params = cmd
529 params = cmd
530 for name, filterfn in self._datafilters.iteritems():
530 for name, filterfn in self._datafilters.iteritems():
531 if cmd.startswith(name):
531 if cmd.startswith(name):
532 fn = filterfn
532 fn = filterfn
533 params = cmd[len(name):].lstrip()
533 params = cmd[len(name):].lstrip()
534 break
534 break
535 if not fn:
535 if not fn:
536 fn = lambda s, c, **kwargs: util.filter(s, c)
536 fn = lambda s, c, **kwargs: util.filter(s, c)
537 # Wrap old filters not supporting keyword arguments
537 # Wrap old filters not supporting keyword arguments
538 if not inspect.getargspec(fn)[2]:
538 if not inspect.getargspec(fn)[2]:
539 oldfn = fn
539 oldfn = fn
540 fn = lambda s, c, **kwargs: oldfn(s, c)
540 fn = lambda s, c, **kwargs: oldfn(s, c)
541 l.append((mf, fn, params))
541 l.append((mf, fn, params))
542 self.filterpats[filter] = l
542 self.filterpats[filter] = l
543
543
544 for mf, fn, cmd in self.filterpats[filter]:
544 for mf, fn, cmd in self.filterpats[filter]:
545 if mf(filename):
545 if mf(filename):
546 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
546 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
547 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
547 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
548 break
548 break
549
549
550 return data
550 return data
551
551
552 def adddatafilter(self, name, filter):
552 def adddatafilter(self, name, filter):
553 self._datafilters[name] = filter
553 self._datafilters[name] = filter
554
554
555 def wread(self, filename):
555 def wread(self, filename):
556 if self._link(filename):
556 if self._link(filename):
557 data = os.readlink(self.wjoin(filename))
557 data = os.readlink(self.wjoin(filename))
558 else:
558 else:
559 data = self.wopener(filename, 'r').read()
559 data = self.wopener(filename, 'r').read()
560 return self._filter("encode", filename, data)
560 return self._filter("encode", filename, data)
561
561
562 def wwrite(self, filename, data, flags):
562 def wwrite(self, filename, data, flags):
563 data = self._filter("decode", filename, data)
563 data = self._filter("decode", filename, data)
564 try:
564 try:
565 os.unlink(self.wjoin(filename))
565 os.unlink(self.wjoin(filename))
566 except OSError:
566 except OSError:
567 pass
567 pass
568 self.wopener(filename, 'w').write(data)
568 self.wopener(filename, 'w').write(data)
569 util.set_flags(self.wjoin(filename), flags)
569 util.set_flags(self.wjoin(filename), flags)
570
570
571 def wwritedata(self, filename, data):
571 def wwritedata(self, filename, data):
572 return self._filter("decode", filename, data)
572 return self._filter("decode", filename, data)
573
573
574 def transaction(self):
574 def transaction(self):
575 if self._transref and self._transref():
575 if self._transref and self._transref():
576 return self._transref().nest()
576 return self._transref().nest()
577
577
578 # abort here if the journal already exists
578 # abort here if the journal already exists
579 if os.path.exists(self.sjoin("journal")):
579 if os.path.exists(self.sjoin("journal")):
580 raise repo.RepoError(_("journal already exists - run hg recover"))
580 raise repo.RepoError(_("journal already exists - run hg recover"))
581
581
582 # save dirstate for rollback
582 # save dirstate for rollback
583 try:
583 try:
584 ds = self.opener("dirstate").read()
584 ds = self.opener("dirstate").read()
585 except IOError:
585 except IOError:
586 ds = ""
586 ds = ""
587 self.opener("journal.dirstate", "w").write(ds)
587 self.opener("journal.dirstate", "w").write(ds)
588 self.opener("journal.branch", "w").write(self.dirstate.branch())
588 self.opener("journal.branch", "w").write(self.dirstate.branch())
589
589
590 renames = [(self.sjoin("journal"), self.sjoin("undo")),
590 renames = [(self.sjoin("journal"), self.sjoin("undo")),
591 (self.join("journal.dirstate"), self.join("undo.dirstate")),
591 (self.join("journal.dirstate"), self.join("undo.dirstate")),
592 (self.join("journal.branch"), self.join("undo.branch"))]
592 (self.join("journal.branch"), self.join("undo.branch"))]
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._createmode)
596 self._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 l = self.lock()
601 l = 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.invalidate()
606 self.invalidate()
607 return True
607 return True
608 else:
608 else:
609 self.ui.warn(_("no interrupted transaction available\n"))
609 self.ui.warn(_("no interrupted transaction available\n"))
610 return False
610 return False
611 finally:
611 finally:
612 del l
612 del l
613
613
614 def rollback(self):
614 def rollback(self):
615 wlock = lock = None
615 wlock = lock = None
616 try:
616 try:
617 wlock = self.wlock()
617 wlock = self.wlock()
618 lock = self.lock()
618 lock = self.lock()
619 if os.path.exists(self.sjoin("undo")):
619 if os.path.exists(self.sjoin("undo")):
620 self.ui.status(_("rolling back last transaction\n"))
620 self.ui.status(_("rolling back last transaction\n"))
621 transaction.rollback(self.sopener, self.sjoin("undo"))
621 transaction.rollback(self.sopener, self.sjoin("undo"))
622 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
622 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
623 try:
623 try:
624 branch = self.opener("undo.branch").read()
624 branch = self.opener("undo.branch").read()
625 self.dirstate.setbranch(branch)
625 self.dirstate.setbranch(branch)
626 except IOError:
626 except IOError:
627 self.ui.warn(_("Named branch could not be reset, "
627 self.ui.warn(_("Named branch could not be reset, "
628 "current branch still is: %s\n")
628 "current branch still is: %s\n")
629 % util.tolocal(self.dirstate.branch()))
629 % util.tolocal(self.dirstate.branch()))
630 self.invalidate()
630 self.invalidate()
631 self.dirstate.invalidate()
631 self.dirstate.invalidate()
632 else:
632 else:
633 self.ui.warn(_("no rollback information available\n"))
633 self.ui.warn(_("no rollback information available\n"))
634 finally:
634 finally:
635 del lock, wlock
635 del lock, wlock
636
636
637 def invalidate(self):
637 def invalidate(self):
638 for a in "changelog manifest".split():
638 for a in "changelog manifest".split():
639 if a in self.__dict__:
639 if a in self.__dict__:
640 delattr(self, a)
640 delattr(self, a)
641 self.tagscache = None
641 self.tagscache = None
642 self._tagstypecache = None
642 self._tagstypecache = None
643 self.nodetagscache = None
643 self.nodetagscache = None
644 self.branchcache = None
644 self.branchcache = None
645 self._ubranchcache = None
645 self._ubranchcache = None
646 self._branchcachetip = None
646 self._branchcachetip = None
647
647
648 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
648 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
649 try:
649 try:
650 l = lock.lock(lockname, 0, releasefn, desc=desc)
650 l = lock.lock(lockname, 0, releasefn, desc=desc)
651 except lock.LockHeld, inst:
651 except lock.LockHeld, inst:
652 if not wait:
652 if not wait:
653 raise
653 raise
654 self.ui.warn(_("waiting for lock on %s held by %r\n") %
654 self.ui.warn(_("waiting for lock on %s held by %r\n") %
655 (desc, inst.locker))
655 (desc, inst.locker))
656 # default to 600 seconds timeout
656 # default to 600 seconds timeout
657 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
657 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
658 releasefn, desc=desc)
658 releasefn, desc=desc)
659 if acquirefn:
659 if acquirefn:
660 acquirefn()
660 acquirefn()
661 return l
661 return l
662
662
663 def lock(self, wait=True):
663 def lock(self, wait=True):
664 if self._lockref and self._lockref():
664 if self._lockref and self._lockref():
665 return self._lockref()
665 return self._lockref()
666
666
667 l = self._lock(self.sjoin("lock"), wait, None, self.invalidate,
667 l = self._lock(self.sjoin("lock"), wait, None, self.invalidate,
668 _('repository %s') % self.origroot)
668 _('repository %s') % self.origroot)
669 self._lockref = weakref.ref(l)
669 self._lockref = weakref.ref(l)
670 return l
670 return l
671
671
672 def wlock(self, wait=True):
672 def wlock(self, wait=True):
673 if self._wlockref and self._wlockref():
673 if self._wlockref and self._wlockref():
674 return self._wlockref()
674 return self._wlockref()
675
675
676 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
676 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
677 self.dirstate.invalidate, _('working directory of %s') %
677 self.dirstate.invalidate, _('working directory of %s') %
678 self.origroot)
678 self.origroot)
679 self._wlockref = weakref.ref(l)
679 self._wlockref = weakref.ref(l)
680 return l
680 return l
681
681
682 def filecommit(self, fn, manifest1, manifest2, linkrev, tr, changelist):
682 def filecommit(self, fn, manifest1, manifest2, linkrev, tr, changelist):
683 """
683 """
684 commit an individual file as part of a larger transaction
684 commit an individual file as part of a larger transaction
685 """
685 """
686
686
687 t = self.wread(fn)
687 t = self.wread(fn)
688 fl = self.file(fn)
688 fl = self.file(fn)
689 fp1 = manifest1.get(fn, nullid)
689 fp1 = manifest1.get(fn, nullid)
690 fp2 = manifest2.get(fn, nullid)
690 fp2 = manifest2.get(fn, nullid)
691
691
692 meta = {}
692 meta = {}
693 cp = self.dirstate.copied(fn)
693 cp = self.dirstate.copied(fn)
694 if cp:
694 if cp:
695 # Mark the new revision of this file as a copy of another
695 # Mark the new revision of this file as a copy of another
696 # file. This copy data will effectively act as a parent
696 # file. This copy data will effectively act as a parent
697 # of this new revision. If this is a merge, the first
697 # of this new revision. If this is a merge, the first
698 # parent will be the nullid (meaning "look up the copy data")
698 # parent will be the nullid (meaning "look up the copy data")
699 # and the second one will be the other parent. For example:
699 # and the second one will be the other parent. For example:
700 #
700 #
701 # 0 --- 1 --- 3 rev1 changes file foo
701 # 0 --- 1 --- 3 rev1 changes file foo
702 # \ / rev2 renames foo to bar and changes it
702 # \ / rev2 renames foo to bar and changes it
703 # \- 2 -/ rev3 should have bar with all changes and
703 # \- 2 -/ rev3 should have bar with all changes and
704 # should record that bar descends from
704 # should record that bar descends from
705 # bar in rev2 and foo in rev1
705 # bar in rev2 and foo in rev1
706 #
706 #
707 # this allows this merge to succeed:
707 # this allows this merge to succeed:
708 #
708 #
709 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
709 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
710 # \ / merging rev3 and rev4 should use bar@rev2
710 # \ / merging rev3 and rev4 should use bar@rev2
711 # \- 2 --- 4 as the merge base
711 # \- 2 --- 4 as the merge base
712 #
712 #
713 meta["copy"] = cp
713 meta["copy"] = cp
714 if not manifest2: # not a branch merge
714 if not manifest2: # not a branch merge
715 meta["copyrev"] = hex(manifest1[cp])
715 meta["copyrev"] = hex(manifest1[cp])
716 fp2 = nullid
716 fp2 = nullid
717 elif fp2 != nullid: # copied on remote side
717 elif fp2 != nullid: # copied on remote side
718 meta["copyrev"] = hex(manifest1[cp])
718 meta["copyrev"] = hex(manifest1[cp])
719 elif fp1 != nullid: # copied on local side, reversed
719 elif fp1 != nullid: # copied on local side, reversed
720 meta["copyrev"] = hex(manifest2[cp])
720 meta["copyrev"] = hex(manifest2[cp])
721 fp2 = fp1
721 fp2 = fp1
722 elif cp in manifest2: # directory rename on local side
722 elif cp in manifest2: # directory rename on local side
723 meta["copyrev"] = hex(manifest2[cp])
723 meta["copyrev"] = hex(manifest2[cp])
724 else: # directory rename on remote side
724 else: # directory rename on remote side
725 meta["copyrev"] = hex(manifest1[cp])
725 meta["copyrev"] = hex(manifest1[cp])
726 self.ui.debug(_(" %s: copy %s:%s\n") %
726 self.ui.debug(_(" %s: copy %s:%s\n") %
727 (fn, cp, meta["copyrev"]))
727 (fn, cp, meta["copyrev"]))
728 fp1 = nullid
728 fp1 = nullid
729 elif fp2 != nullid:
729 elif fp2 != nullid:
730 # is one parent an ancestor of the other?
730 # is one parent an ancestor of the other?
731 fpa = fl.ancestor(fp1, fp2)
731 fpa = fl.ancestor(fp1, fp2)
732 if fpa == fp1:
732 if fpa == fp1:
733 fp1, fp2 = fp2, nullid
733 fp1, fp2 = fp2, nullid
734 elif fpa == fp2:
734 elif fpa == fp2:
735 fp2 = nullid
735 fp2 = nullid
736
736
737 # is the file unmodified from the parent? report existing entry
737 # is the file unmodified from the parent? report existing entry
738 if fp2 == nullid and not fl.cmp(fp1, t) and not meta:
738 if fp2 == nullid and not fl.cmp(fp1, t) and not meta:
739 return fp1
739 return fp1
740
740
741 changelist.append(fn)
741 changelist.append(fn)
742 return fl.add(t, meta, tr, linkrev, fp1, fp2)
742 return fl.add(t, meta, tr, linkrev, fp1, fp2)
743
743
744 def rawcommit(self, files, text, user, date, p1=None, p2=None, extra={}):
744 def rawcommit(self, files, text, user, date, p1=None, p2=None, extra={}):
745 if p1 is None:
745 if p1 is None:
746 p1, p2 = self.dirstate.parents()
746 p1, p2 = self.dirstate.parents()
747 return self.commit(files=files, text=text, user=user, date=date,
747 return self.commit(files=files, text=text, user=user, date=date,
748 p1=p1, p2=p2, extra=extra, empty_ok=True)
748 p1=p1, p2=p2, extra=extra, empty_ok=True)
749
749
750 def commit(self, files=None, text="", user=None, date=None,
750 def commit(self, files=None, text="", user=None, date=None,
751 match=util.always, force=False, force_editor=False,
751 match=util.always, force=False, force_editor=False,
752 p1=None, p2=None, extra={}, empty_ok=False):
752 p1=None, p2=None, extra={}, empty_ok=False):
753 wlock = lock = tr = None
753 wlock = lock = tr = None
754 valid = 0 # don't save the dirstate if this isn't set
754 valid = 0 # don't save the dirstate if this isn't set
755 if files:
755 if files:
756 files = util.unique(files)
756 files = util.unique(files)
757 try:
757 try:
758 wlock = self.wlock()
758 wlock = self.wlock()
759 lock = self.lock()
759 lock = self.lock()
760 commit = []
760 commit = []
761 remove = []
761 remove = []
762 changed = []
762 changed = []
763 use_dirstate = (p1 is None) # not rawcommit
763 use_dirstate = (p1 is None) # not rawcommit
764 extra = extra.copy()
764 extra = extra.copy()
765
765
766 if use_dirstate:
766 if use_dirstate:
767 if files:
767 if files:
768 for f in files:
768 for f in files:
769 s = self.dirstate[f]
769 s = self.dirstate[f]
770 if s in 'nma':
770 if s in 'nma':
771 commit.append(f)
771 commit.append(f)
772 elif s == 'r':
772 elif s == 'r':
773 remove.append(f)
773 remove.append(f)
774 else:
774 else:
775 self.ui.warn(_("%s not tracked!\n") % f)
775 self.ui.warn(_("%s not tracked!\n") % f)
776 else:
776 else:
777 changes = self.status(match=match)[:5]
777 changes = self.status(match=match)[:5]
778 modified, added, removed, deleted, unknown = changes
778 modified, added, removed, deleted, unknown = changes
779 commit = modified + added
779 commit = modified + added
780 remove = removed
780 remove = removed
781 else:
781 else:
782 commit = files
782 commit = files
783
783
784 if use_dirstate:
784 if use_dirstate:
785 p1, p2 = self.dirstate.parents()
785 p1, p2 = self.dirstate.parents()
786 update_dirstate = True
786 update_dirstate = True
787
787
788 if (not force and p2 != nullid and
788 if (not force and p2 != nullid and
789 (match.files() or match.anypats())):
789 (match.files() or match.anypats())):
790 raise util.Abort(_('cannot partially commit a merge '
790 raise util.Abort(_('cannot partially commit a merge '
791 '(do not specify files or patterns)'))
791 '(do not specify files or patterns)'))
792 else:
792 else:
793 p1, p2 = p1, p2 or nullid
793 p1, p2 = p1, p2 or nullid
794 update_dirstate = (self.dirstate.parents()[0] == p1)
794 update_dirstate = (self.dirstate.parents()[0] == p1)
795
795
796 c1 = self.changelog.read(p1)
796 c1 = self.changelog.read(p1)
797 c2 = self.changelog.read(p2)
797 c2 = self.changelog.read(p2)
798 m1 = self.manifest.read(c1[0]).copy()
798 m1 = self.manifest.read(c1[0]).copy()
799 m2 = self.manifest.read(c2[0])
799 m2 = self.manifest.read(c2[0])
800
800
801 if use_dirstate:
801 if use_dirstate:
802 branchname = self.workingctx().branch()
802 branchname = self.workingctx().branch()
803 try:
803 try:
804 branchname = branchname.decode('UTF-8').encode('UTF-8')
804 branchname = branchname.decode('UTF-8').encode('UTF-8')
805 except UnicodeDecodeError:
805 except UnicodeDecodeError:
806 raise util.Abort(_('branch name not in UTF-8!'))
806 raise util.Abort(_('branch name not in UTF-8!'))
807 else:
807 else:
808 branchname = ""
808 branchname = ""
809
809
810 if use_dirstate:
810 if use_dirstate:
811 oldname = c1[5].get("branch") # stored in UTF-8
811 oldname = c1[5].get("branch") # stored in UTF-8
812 if (not commit and not remove and not force and p2 == nullid
812 if (not commit and not remove and not force and p2 == nullid
813 and branchname == oldname):
813 and branchname == oldname):
814 self.ui.status(_("nothing changed\n"))
814 self.ui.status(_("nothing changed\n"))
815 return None
815 return None
816
816
817 xp1 = hex(p1)
817 xp1 = hex(p1)
818 if p2 == nullid: xp2 = ''
818 if p2 == nullid: xp2 = ''
819 else: xp2 = hex(p2)
819 else: xp2 = hex(p2)
820
820
821 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
821 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
822
822
823 tr = self.transaction()
823 tr = self.transaction()
824 trp = weakref.proxy(tr)
824 trp = weakref.proxy(tr)
825
825
826 # check in files
826 # check in files
827 new = {}
827 new = {}
828 linkrev = self.changelog.count()
828 linkrev = self.changelog.count()
829 commit.sort()
829 commit.sort()
830 is_exec = util.execfunc(self.root, m1.execf)
830 is_exec = util.execfunc(self.root, m1.execf)
831 is_link = util.linkfunc(self.root, m1.linkf)
831 is_link = util.linkfunc(self.root, m1.linkf)
832 for f in commit:
832 for f in commit:
833 self.ui.note(f + "\n")
833 self.ui.note(f + "\n")
834 try:
834 try:
835 new[f] = self.filecommit(f, m1, m2, linkrev, trp, changed)
835 new[f] = self.filecommit(f, m1, m2, linkrev, trp, changed)
836 new_exec = is_exec(f)
836 new_exec = is_exec(f)
837 new_link = is_link(f)
837 new_link = is_link(f)
838 if ((not changed or changed[-1] != f) and
838 if ((not changed or changed[-1] != f) and
839 m2.get(f) != new[f]):
839 m2.get(f) != new[f]):
840 # mention the file in the changelog if some
840 # mention the file in the changelog if some
841 # flag changed, even if there was no content
841 # flag changed, even if there was no content
842 # change.
842 # change.
843 old_exec = m1.execf(f)
843 old_exec = m1.execf(f)
844 old_link = m1.linkf(f)
844 old_link = m1.linkf(f)
845 if old_exec != new_exec or old_link != new_link:
845 if old_exec != new_exec or old_link != new_link:
846 changed.append(f)
846 changed.append(f)
847 m1.set(f, new_exec, new_link)
847 m1.set(f, new_exec, new_link)
848 if use_dirstate:
848 if use_dirstate:
849 self.dirstate.normal(f)
849 self.dirstate.normal(f)
850
850
851 except (OSError, IOError):
851 except (OSError, IOError):
852 if use_dirstate:
852 if use_dirstate:
853 self.ui.warn(_("trouble committing %s!\n") % f)
853 self.ui.warn(_("trouble committing %s!\n") % f)
854 raise
854 raise
855 else:
855 else:
856 remove.append(f)
856 remove.append(f)
857
857
858 # update manifest
858 # update manifest
859 m1.update(new)
859 m1.update(new)
860 remove.sort()
860 remove.sort()
861 removed = []
861 removed = []
862
862
863 for f in remove:
863 for f in remove:
864 if f in m1:
864 if f in m1:
865 del m1[f]
865 del m1[f]
866 removed.append(f)
866 removed.append(f)
867 elif f in m2:
867 elif f in m2:
868 removed.append(f)
868 removed.append(f)
869 mn = self.manifest.add(m1, trp, linkrev, c1[0], c2[0],
869 mn = self.manifest.add(m1, trp, linkrev, c1[0], c2[0],
870 (new, removed))
870 (new, removed))
871
871
872 # add changeset
872 # add changeset
873 new = new.keys()
873 new = new.keys()
874 new.sort()
874 new.sort()
875
875
876 user = user or self.ui.username()
876 user = user or self.ui.username()
877 if (not empty_ok and not text) or force_editor:
877 if (not empty_ok and not text) or force_editor:
878 edittext = []
878 edittext = []
879 if text:
879 if text:
880 edittext.append(text)
880 edittext.append(text)
881 edittext.append("")
881 edittext.append("")
882 edittext.append(_("HG: Enter commit message."
882 edittext.append(_("HG: Enter commit message."
883 " Lines beginning with 'HG:' are removed."))
883 " Lines beginning with 'HG:' are removed."))
884 edittext.append("HG: --")
884 edittext.append("HG: --")
885 edittext.append("HG: user: %s" % user)
885 edittext.append("HG: user: %s" % user)
886 if p2 != nullid:
886 if p2 != nullid:
887 edittext.append("HG: branch merge")
887 edittext.append("HG: branch merge")
888 if branchname:
888 if branchname:
889 edittext.append("HG: branch '%s'" % util.tolocal(branchname))
889 edittext.append("HG: branch '%s'" % util.tolocal(branchname))
890 edittext.extend(["HG: changed %s" % f for f in changed])
890 edittext.extend(["HG: changed %s" % f for f in changed])
891 edittext.extend(["HG: removed %s" % f for f in removed])
891 edittext.extend(["HG: removed %s" % f for f in removed])
892 if not changed and not remove:
892 if not changed and not remove:
893 edittext.append("HG: no files changed")
893 edittext.append("HG: no files changed")
894 edittext.append("")
894 edittext.append("")
895 # run editor in the repository root
895 # run editor in the repository root
896 olddir = os.getcwd()
896 olddir = os.getcwd()
897 os.chdir(self.root)
897 os.chdir(self.root)
898 text = self.ui.edit("\n".join(edittext), user)
898 text = self.ui.edit("\n".join(edittext), user)
899 os.chdir(olddir)
899 os.chdir(olddir)
900
900
901 if branchname:
901 if branchname:
902 extra["branch"] = branchname
902 extra["branch"] = branchname
903
903
904 lines = [line.rstrip() for line in text.rstrip().splitlines()]
904 lines = [line.rstrip() for line in text.rstrip().splitlines()]
905 while lines and not lines[0]:
905 while lines and not lines[0]:
906 del lines[0]
906 del lines[0]
907 if not lines and use_dirstate:
907 if not lines and use_dirstate:
908 raise util.Abort(_("empty commit message"))
908 raise util.Abort(_("empty commit message"))
909 text = '\n'.join(lines)
909 text = '\n'.join(lines)
910
910
911 n = self.changelog.add(mn, changed + removed, text, trp, p1, p2,
911 n = self.changelog.add(mn, changed + removed, text, trp, p1, p2,
912 user, date, extra)
912 user, date, extra)
913 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
913 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
914 parent2=xp2)
914 parent2=xp2)
915 tr.close()
915 tr.close()
916
916
917 if self.branchcache:
917 if self.branchcache:
918 self.branchtags()
918 self.branchtags()
919
919
920 if use_dirstate or update_dirstate:
920 if use_dirstate or update_dirstate:
921 self.dirstate.setparents(n)
921 self.dirstate.setparents(n)
922 if use_dirstate:
922 if use_dirstate:
923 for f in removed:
923 for f in removed:
924 self.dirstate.forget(f)
924 self.dirstate.forget(f)
925 valid = 1 # our dirstate updates are complete
925 valid = 1 # our dirstate updates are complete
926
926
927 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
927 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
928 return n
928 return n
929 finally:
929 finally:
930 if not valid: # don't save our updated dirstate
930 if not valid: # don't save our updated dirstate
931 self.dirstate.invalidate()
931 self.dirstate.invalidate()
932 del tr, lock, wlock
932 del tr, lock, wlock
933
933
934 def walk(self, node, match):
934 def walk(self, match, node=None):
935 '''
935 '''
936 walk recursively through the directory tree or a given
936 walk recursively through the directory tree or a given
937 changeset, finding all files matched by the match
937 changeset, finding all files matched by the match
938 function
938 function
939
939
940 results are yielded in a tuple (src, filename), where src
940 results are yielded in a tuple (src, filename), where src
941 is one of:
941 is one of:
942 'f' the file was found in the directory tree
942 'f' the file was found in the directory tree
943 'm' the file was only in the dirstate and not in the tree
943 'm' the file was only in the dirstate and not in the tree
944 '''
944 '''
945
945
946 if node:
946 if node:
947 fdict = dict.fromkeys(match.files())
947 fdict = dict.fromkeys(match.files())
948 # for dirstate.walk, files=['.'] means "walk the whole tree".
948 # for dirstate.walk, files=['.'] means "walk the whole tree".
949 # follow that here, too
949 # follow that here, too
950 fdict.pop('.', None)
950 fdict.pop('.', None)
951 mdict = self.manifest.read(self.changelog.read(node)[0])
951 mdict = self.manifest.read(self.changelog.read(node)[0])
952 mfiles = mdict.keys()
952 mfiles = mdict.keys()
953 mfiles.sort()
953 mfiles.sort()
954 for fn in mfiles:
954 for fn in mfiles:
955 for ffn in fdict:
955 for ffn in fdict:
956 # match if the file is the exact name or a directory
956 # match if the file is the exact name or a directory
957 if ffn == fn or fn.startswith("%s/" % ffn):
957 if ffn == fn or fn.startswith("%s/" % ffn):
958 del fdict[ffn]
958 del fdict[ffn]
959 break
959 break
960 if match(fn):
960 if match(fn):
961 yield 'm', fn
961 yield 'm', fn
962 ffiles = fdict.keys()
962 ffiles = fdict.keys()
963 ffiles.sort()
963 ffiles.sort()
964 for fn in ffiles:
964 for fn in ffiles:
965 if match.bad(fn, 'No such file in rev ' + short(node)) \
965 if match.bad(fn, 'No such file in rev ' + short(node)) \
966 and match(fn):
966 and match(fn):
967 yield 'f', fn
967 yield 'f', fn
968 else:
968 else:
969 for src, fn in self.dirstate.walk(match):
969 for src, fn in self.dirstate.walk(match):
970 yield src, fn
970 yield src, fn
971
971
972 def status(self, node1=None, node2=None, files=[], match=util.always,
972 def status(self, node1=None, node2=None, files=[], match=util.always,
973 list_ignored=False, list_clean=False, list_unknown=True):
973 list_ignored=False, list_clean=False, list_unknown=True):
974 """return status of files between two nodes or node and working directory
974 """return status of files between two nodes or node and working directory
975
975
976 If node1 is None, use the first dirstate parent instead.
976 If node1 is None, use the first dirstate parent instead.
977 If node2 is None, compare node1 with working directory.
977 If node2 is None, compare node1 with working directory.
978 """
978 """
979
979
980 def fcmp(fn, getnode):
980 def fcmp(fn, getnode):
981 t1 = self.wread(fn)
981 t1 = self.wread(fn)
982 return self.file(fn).cmp(getnode(fn), t1)
982 return self.file(fn).cmp(getnode(fn), t1)
983
983
984 def mfmatches(node):
984 def mfmatches(node):
985 change = self.changelog.read(node)
985 change = self.changelog.read(node)
986 mf = self.manifest.read(change[0]).copy()
986 mf = self.manifest.read(change[0]).copy()
987 for fn in mf.keys():
987 for fn in mf.keys():
988 if not match(fn):
988 if not match(fn):
989 del mf[fn]
989 del mf[fn]
990 return mf
990 return mf
991
991
992 modified, added, removed, deleted, unknown = [], [], [], [], []
992 modified, added, removed, deleted, unknown = [], [], [], [], []
993 ignored, clean = [], []
993 ignored, clean = [], []
994
994
995 compareworking = False
995 compareworking = False
996 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
996 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
997 compareworking = True
997 compareworking = True
998
998
999 if not compareworking:
999 if not compareworking:
1000 # read the manifest from node1 before the manifest from node2,
1000 # read the manifest from node1 before the manifest from node2,
1001 # so that we'll hit the manifest cache if we're going through
1001 # so that we'll hit the manifest cache if we're going through
1002 # all the revisions in parent->child order.
1002 # all the revisions in parent->child order.
1003 mf1 = mfmatches(node1)
1003 mf1 = mfmatches(node1)
1004
1004
1005 # are we comparing the working directory?
1005 # are we comparing the working directory?
1006 if not node2:
1006 if not node2:
1007 (lookup, modified, added, removed, deleted, unknown,
1007 (lookup, modified, added, removed, deleted, unknown,
1008 ignored, clean) = self.dirstate.status(files, match,
1008 ignored, clean) = self.dirstate.status(files, match,
1009 list_ignored, list_clean,
1009 list_ignored, list_clean,
1010 list_unknown)
1010 list_unknown)
1011
1011
1012 # are we comparing working dir against its parent?
1012 # are we comparing working dir against its parent?
1013 if compareworking:
1013 if compareworking:
1014 if lookup:
1014 if lookup:
1015 fixup = []
1015 fixup = []
1016 # do a full compare of any files that might have changed
1016 # do a full compare of any files that might have changed
1017 ctx = self.changectx()
1017 ctx = self.changectx()
1018 mexec = lambda f: 'x' in ctx.fileflags(f)
1018 mexec = lambda f: 'x' in ctx.fileflags(f)
1019 mlink = lambda f: 'l' in ctx.fileflags(f)
1019 mlink = lambda f: 'l' in ctx.fileflags(f)
1020 is_exec = util.execfunc(self.root, mexec)
1020 is_exec = util.execfunc(self.root, mexec)
1021 is_link = util.linkfunc(self.root, mlink)
1021 is_link = util.linkfunc(self.root, mlink)
1022 def flags(f):
1022 def flags(f):
1023 return is_link(f) and 'l' or is_exec(f) and 'x' or ''
1023 return is_link(f) and 'l' or is_exec(f) and 'x' or ''
1024 for f in lookup:
1024 for f in lookup:
1025 if (f not in ctx or flags(f) != ctx.fileflags(f)
1025 if (f not in ctx or flags(f) != ctx.fileflags(f)
1026 or ctx[f].cmp(self.wread(f))):
1026 or ctx[f].cmp(self.wread(f))):
1027 modified.append(f)
1027 modified.append(f)
1028 else:
1028 else:
1029 fixup.append(f)
1029 fixup.append(f)
1030 if list_clean:
1030 if list_clean:
1031 clean.append(f)
1031 clean.append(f)
1032
1032
1033 # update dirstate for files that are actually clean
1033 # update dirstate for files that are actually clean
1034 if fixup:
1034 if fixup:
1035 wlock = None
1035 wlock = None
1036 try:
1036 try:
1037 try:
1037 try:
1038 wlock = self.wlock(False)
1038 wlock = self.wlock(False)
1039 except lock.LockException:
1039 except lock.LockException:
1040 pass
1040 pass
1041 if wlock:
1041 if wlock:
1042 for f in fixup:
1042 for f in fixup:
1043 self.dirstate.normal(f)
1043 self.dirstate.normal(f)
1044 finally:
1044 finally:
1045 del wlock
1045 del wlock
1046 else:
1046 else:
1047 # we are comparing working dir against non-parent
1047 # we are comparing working dir against non-parent
1048 # generate a pseudo-manifest for the working dir
1048 # generate a pseudo-manifest for the working dir
1049 # XXX: create it in dirstate.py ?
1049 # XXX: create it in dirstate.py ?
1050 mf2 = mfmatches(self.dirstate.parents()[0])
1050 mf2 = mfmatches(self.dirstate.parents()[0])
1051 is_exec = util.execfunc(self.root, mf2.execf)
1051 is_exec = util.execfunc(self.root, mf2.execf)
1052 is_link = util.linkfunc(self.root, mf2.linkf)
1052 is_link = util.linkfunc(self.root, mf2.linkf)
1053 for f in lookup + modified + added:
1053 for f in lookup + modified + added:
1054 mf2[f] = ""
1054 mf2[f] = ""
1055 mf2.set(f, is_exec(f), is_link(f))
1055 mf2.set(f, is_exec(f), is_link(f))
1056 for f in removed:
1056 for f in removed:
1057 if f in mf2:
1057 if f in mf2:
1058 del mf2[f]
1058 del mf2[f]
1059
1059
1060 else:
1060 else:
1061 # we are comparing two revisions
1061 # we are comparing two revisions
1062 mf2 = mfmatches(node2)
1062 mf2 = mfmatches(node2)
1063
1063
1064 if not compareworking:
1064 if not compareworking:
1065 # flush lists from dirstate before comparing manifests
1065 # flush lists from dirstate before comparing manifests
1066 modified, added, clean = [], [], []
1066 modified, added, clean = [], [], []
1067
1067
1068 # make sure to sort the files so we talk to the disk in a
1068 # make sure to sort the files so we talk to the disk in a
1069 # reasonable order
1069 # reasonable order
1070 mf2keys = mf2.keys()
1070 mf2keys = mf2.keys()
1071 mf2keys.sort()
1071 mf2keys.sort()
1072 getnode = lambda fn: mf1.get(fn, nullid)
1072 getnode = lambda fn: mf1.get(fn, nullid)
1073 for fn in mf2keys:
1073 for fn in mf2keys:
1074 if fn in mf1:
1074 if fn in mf1:
1075 if (mf1.flags(fn) != mf2.flags(fn) or
1075 if (mf1.flags(fn) != mf2.flags(fn) or
1076 (mf1[fn] != mf2[fn] and
1076 (mf1[fn] != mf2[fn] and
1077 (mf2[fn] != "" or fcmp(fn, getnode)))):
1077 (mf2[fn] != "" or fcmp(fn, getnode)))):
1078 modified.append(fn)
1078 modified.append(fn)
1079 elif list_clean:
1079 elif list_clean:
1080 clean.append(fn)
1080 clean.append(fn)
1081 del mf1[fn]
1081 del mf1[fn]
1082 else:
1082 else:
1083 added.append(fn)
1083 added.append(fn)
1084
1084
1085 removed = mf1.keys()
1085 removed = mf1.keys()
1086
1086
1087 # sort and return results:
1087 # sort and return results:
1088 for l in modified, added, removed, deleted, unknown, ignored, clean:
1088 for l in modified, added, removed, deleted, unknown, ignored, clean:
1089 l.sort()
1089 l.sort()
1090 return (modified, added, removed, deleted, unknown, ignored, clean)
1090 return (modified, added, removed, deleted, unknown, ignored, clean)
1091
1091
1092 def add(self, list):
1092 def add(self, list):
1093 wlock = self.wlock()
1093 wlock = self.wlock()
1094 try:
1094 try:
1095 rejected = []
1095 rejected = []
1096 for f in list:
1096 for f in list:
1097 p = self.wjoin(f)
1097 p = self.wjoin(f)
1098 try:
1098 try:
1099 st = os.lstat(p)
1099 st = os.lstat(p)
1100 except:
1100 except:
1101 self.ui.warn(_("%s does not exist!\n") % f)
1101 self.ui.warn(_("%s does not exist!\n") % f)
1102 rejected.append(f)
1102 rejected.append(f)
1103 continue
1103 continue
1104 if st.st_size > 10000000:
1104 if st.st_size > 10000000:
1105 self.ui.warn(_("%s: files over 10MB may cause memory and"
1105 self.ui.warn(_("%s: files over 10MB may cause memory and"
1106 " performance problems\n"
1106 " performance problems\n"
1107 "(use 'hg revert %s' to unadd the file)\n")
1107 "(use 'hg revert %s' to unadd the file)\n")
1108 % (f, f))
1108 % (f, f))
1109 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1109 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1110 self.ui.warn(_("%s not added: only files and symlinks "
1110 self.ui.warn(_("%s not added: only files and symlinks "
1111 "supported currently\n") % f)
1111 "supported currently\n") % f)
1112 rejected.append(p)
1112 rejected.append(p)
1113 elif self.dirstate[f] in 'amn':
1113 elif self.dirstate[f] in 'amn':
1114 self.ui.warn(_("%s already tracked!\n") % f)
1114 self.ui.warn(_("%s already tracked!\n") % f)
1115 elif self.dirstate[f] == 'r':
1115 elif self.dirstate[f] == 'r':
1116 self.dirstate.normallookup(f)
1116 self.dirstate.normallookup(f)
1117 else:
1117 else:
1118 self.dirstate.add(f)
1118 self.dirstate.add(f)
1119 return rejected
1119 return rejected
1120 finally:
1120 finally:
1121 del wlock
1121 del wlock
1122
1122
1123 def forget(self, list):
1123 def forget(self, list):
1124 wlock = self.wlock()
1124 wlock = self.wlock()
1125 try:
1125 try:
1126 for f in list:
1126 for f in list:
1127 if self.dirstate[f] != 'a':
1127 if self.dirstate[f] != 'a':
1128 self.ui.warn(_("%s not added!\n") % f)
1128 self.ui.warn(_("%s not added!\n") % f)
1129 else:
1129 else:
1130 self.dirstate.forget(f)
1130 self.dirstate.forget(f)
1131 finally:
1131 finally:
1132 del wlock
1132 del wlock
1133
1133
1134 def remove(self, list, unlink=False):
1134 def remove(self, list, unlink=False):
1135 wlock = None
1135 wlock = None
1136 try:
1136 try:
1137 if unlink:
1137 if unlink:
1138 for f in list:
1138 for f in list:
1139 try:
1139 try:
1140 util.unlink(self.wjoin(f))
1140 util.unlink(self.wjoin(f))
1141 except OSError, inst:
1141 except OSError, inst:
1142 if inst.errno != errno.ENOENT:
1142 if inst.errno != errno.ENOENT:
1143 raise
1143 raise
1144 wlock = self.wlock()
1144 wlock = self.wlock()
1145 for f in list:
1145 for f in list:
1146 if unlink and os.path.exists(self.wjoin(f)):
1146 if unlink and os.path.exists(self.wjoin(f)):
1147 self.ui.warn(_("%s still exists!\n") % f)
1147 self.ui.warn(_("%s still exists!\n") % f)
1148 elif self.dirstate[f] == 'a':
1148 elif self.dirstate[f] == 'a':
1149 self.dirstate.forget(f)
1149 self.dirstate.forget(f)
1150 elif f not in self.dirstate:
1150 elif f not in self.dirstate:
1151 self.ui.warn(_("%s not tracked!\n") % f)
1151 self.ui.warn(_("%s not tracked!\n") % f)
1152 else:
1152 else:
1153 self.dirstate.remove(f)
1153 self.dirstate.remove(f)
1154 finally:
1154 finally:
1155 del wlock
1155 del wlock
1156
1156
1157 def undelete(self, list):
1157 def undelete(self, list):
1158 wlock = None
1158 wlock = None
1159 try:
1159 try:
1160 manifests = [self.manifest.read(self.changelog.read(p)[0])
1160 manifests = [self.manifest.read(self.changelog.read(p)[0])
1161 for p in self.dirstate.parents() if p != nullid]
1161 for p in self.dirstate.parents() if p != nullid]
1162 wlock = self.wlock()
1162 wlock = self.wlock()
1163 for f in list:
1163 for f in list:
1164 if self.dirstate[f] != 'r':
1164 if self.dirstate[f] != 'r':
1165 self.ui.warn("%s not removed!\n" % f)
1165 self.ui.warn("%s not removed!\n" % f)
1166 else:
1166 else:
1167 m = f in manifests[0] and manifests[0] or manifests[1]
1167 m = f in manifests[0] and manifests[0] or manifests[1]
1168 t = self.file(f).read(m[f])
1168 t = self.file(f).read(m[f])
1169 self.wwrite(f, t, m.flags(f))
1169 self.wwrite(f, t, m.flags(f))
1170 self.dirstate.normal(f)
1170 self.dirstate.normal(f)
1171 finally:
1171 finally:
1172 del wlock
1172 del wlock
1173
1173
1174 def copy(self, source, dest):
1174 def copy(self, source, dest):
1175 wlock = None
1175 wlock = None
1176 try:
1176 try:
1177 p = self.wjoin(dest)
1177 p = self.wjoin(dest)
1178 if not (os.path.exists(p) or os.path.islink(p)):
1178 if not (os.path.exists(p) or os.path.islink(p)):
1179 self.ui.warn(_("%s does not exist!\n") % dest)
1179 self.ui.warn(_("%s does not exist!\n") % dest)
1180 elif not (os.path.isfile(p) or os.path.islink(p)):
1180 elif not (os.path.isfile(p) or os.path.islink(p)):
1181 self.ui.warn(_("copy failed: %s is not a file or a "
1181 self.ui.warn(_("copy failed: %s is not a file or a "
1182 "symbolic link\n") % dest)
1182 "symbolic link\n") % dest)
1183 else:
1183 else:
1184 wlock = self.wlock()
1184 wlock = self.wlock()
1185 if dest not in self.dirstate:
1185 if dest not in self.dirstate:
1186 self.dirstate.add(dest)
1186 self.dirstate.add(dest)
1187 self.dirstate.copy(source, dest)
1187 self.dirstate.copy(source, dest)
1188 finally:
1188 finally:
1189 del wlock
1189 del wlock
1190
1190
1191 def heads(self, start=None):
1191 def heads(self, start=None):
1192 heads = self.changelog.heads(start)
1192 heads = self.changelog.heads(start)
1193 # sort the output in rev descending order
1193 # sort the output in rev descending order
1194 heads = [(-self.changelog.rev(h), h) for h in heads]
1194 heads = [(-self.changelog.rev(h), h) for h in heads]
1195 heads.sort()
1195 heads.sort()
1196 return [n for (r, n) in heads]
1196 return [n for (r, n) in heads]
1197
1197
1198 def branchheads(self, branch, start=None):
1198 def branchheads(self, branch, start=None):
1199 branches = self.branchtags()
1199 branches = self.branchtags()
1200 if branch not in branches:
1200 if branch not in branches:
1201 return []
1201 return []
1202 # The basic algorithm is this:
1202 # The basic algorithm is this:
1203 #
1203 #
1204 # Start from the branch tip since there are no later revisions that can
1204 # Start from the branch tip since there are no later revisions that can
1205 # possibly be in this branch, and the tip is a guaranteed head.
1205 # possibly be in this branch, and the tip is a guaranteed head.
1206 #
1206 #
1207 # Remember the tip's parents as the first ancestors, since these by
1207 # Remember the tip's parents as the first ancestors, since these by
1208 # definition are not heads.
1208 # definition are not heads.
1209 #
1209 #
1210 # Step backwards from the brach tip through all the revisions. We are
1210 # Step backwards from the brach tip through all the revisions. We are
1211 # guaranteed by the rules of Mercurial that we will now be visiting the
1211 # guaranteed by the rules of Mercurial that we will now be visiting the
1212 # nodes in reverse topological order (children before parents).
1212 # nodes in reverse topological order (children before parents).
1213 #
1213 #
1214 # If a revision is one of the ancestors of a head then we can toss it
1214 # If a revision is one of the ancestors of a head then we can toss it
1215 # out of the ancestors set (we've already found it and won't be
1215 # out of the ancestors set (we've already found it and won't be
1216 # visiting it again) and put its parents in the ancestors set.
1216 # visiting it again) and put its parents in the ancestors set.
1217 #
1217 #
1218 # Otherwise, if a revision is in the branch it's another head, since it
1218 # Otherwise, if a revision is in the branch it's another head, since it
1219 # wasn't in the ancestor list of an existing head. So add it to the
1219 # wasn't in the ancestor list of an existing head. So add it to the
1220 # head list, and add its parents to the ancestor list.
1220 # head list, and add its parents to the ancestor list.
1221 #
1221 #
1222 # If it is not in the branch ignore it.
1222 # If it is not in the branch ignore it.
1223 #
1223 #
1224 # Once we have a list of heads, use nodesbetween to filter out all the
1224 # Once we have a list of heads, use nodesbetween to filter out all the
1225 # heads that cannot be reached from startrev. There may be a more
1225 # heads that cannot be reached from startrev. There may be a more
1226 # efficient way to do this as part of the previous algorithm.
1226 # efficient way to do this as part of the previous algorithm.
1227
1227
1228 set = util.set
1228 set = util.set
1229 heads = [self.changelog.rev(branches[branch])]
1229 heads = [self.changelog.rev(branches[branch])]
1230 # Don't care if ancestors contains nullrev or not.
1230 # Don't care if ancestors contains nullrev or not.
1231 ancestors = set(self.changelog.parentrevs(heads[0]))
1231 ancestors = set(self.changelog.parentrevs(heads[0]))
1232 for rev in xrange(heads[0] - 1, nullrev, -1):
1232 for rev in xrange(heads[0] - 1, nullrev, -1):
1233 if rev in ancestors:
1233 if rev in ancestors:
1234 ancestors.update(self.changelog.parentrevs(rev))
1234 ancestors.update(self.changelog.parentrevs(rev))
1235 ancestors.remove(rev)
1235 ancestors.remove(rev)
1236 elif self.changectx(rev).branch() == branch:
1236 elif self.changectx(rev).branch() == branch:
1237 heads.append(rev)
1237 heads.append(rev)
1238 ancestors.update(self.changelog.parentrevs(rev))
1238 ancestors.update(self.changelog.parentrevs(rev))
1239 heads = [self.changelog.node(rev) for rev in heads]
1239 heads = [self.changelog.node(rev) for rev in heads]
1240 if start is not None:
1240 if start is not None:
1241 heads = self.changelog.nodesbetween([start], heads)[2]
1241 heads = self.changelog.nodesbetween([start], heads)[2]
1242 return heads
1242 return heads
1243
1243
1244 def branches(self, nodes):
1244 def branches(self, nodes):
1245 if not nodes:
1245 if not nodes:
1246 nodes = [self.changelog.tip()]
1246 nodes = [self.changelog.tip()]
1247 b = []
1247 b = []
1248 for n in nodes:
1248 for n in nodes:
1249 t = n
1249 t = n
1250 while 1:
1250 while 1:
1251 p = self.changelog.parents(n)
1251 p = self.changelog.parents(n)
1252 if p[1] != nullid or p[0] == nullid:
1252 if p[1] != nullid or p[0] == nullid:
1253 b.append((t, n, p[0], p[1]))
1253 b.append((t, n, p[0], p[1]))
1254 break
1254 break
1255 n = p[0]
1255 n = p[0]
1256 return b
1256 return b
1257
1257
1258 def between(self, pairs):
1258 def between(self, pairs):
1259 r = []
1259 r = []
1260
1260
1261 for top, bottom in pairs:
1261 for top, bottom in pairs:
1262 n, l, i = top, [], 0
1262 n, l, i = top, [], 0
1263 f = 1
1263 f = 1
1264
1264
1265 while n != bottom:
1265 while n != bottom:
1266 p = self.changelog.parents(n)[0]
1266 p = self.changelog.parents(n)[0]
1267 if i == f:
1267 if i == f:
1268 l.append(n)
1268 l.append(n)
1269 f = f * 2
1269 f = f * 2
1270 n = p
1270 n = p
1271 i += 1
1271 i += 1
1272
1272
1273 r.append(l)
1273 r.append(l)
1274
1274
1275 return r
1275 return r
1276
1276
1277 def findincoming(self, remote, base=None, heads=None, force=False):
1277 def findincoming(self, remote, base=None, heads=None, force=False):
1278 """Return list of roots of the subsets of missing nodes from remote
1278 """Return list of roots of the subsets of missing nodes from remote
1279
1279
1280 If base dict is specified, assume that these nodes and their parents
1280 If base dict is specified, assume that these nodes and their parents
1281 exist on the remote side and that no child of a node of base exists
1281 exist on the remote side and that no child of a node of base exists
1282 in both remote and self.
1282 in both remote and self.
1283 Furthermore base will be updated to include the nodes that exists
1283 Furthermore base will be updated to include the nodes that exists
1284 in self and remote but no children exists in self and remote.
1284 in self and remote but no children exists in self and remote.
1285 If a list of heads is specified, return only nodes which are heads
1285 If a list of heads is specified, return only nodes which are heads
1286 or ancestors of these heads.
1286 or ancestors of these heads.
1287
1287
1288 All the ancestors of base are in self and in remote.
1288 All the ancestors of base are in self and in remote.
1289 All the descendants of the list returned are missing in self.
1289 All the descendants of the list returned are missing in self.
1290 (and so we know that the rest of the nodes are missing in remote, see
1290 (and so we know that the rest of the nodes are missing in remote, see
1291 outgoing)
1291 outgoing)
1292 """
1292 """
1293 m = self.changelog.nodemap
1293 m = self.changelog.nodemap
1294 search = []
1294 search = []
1295 fetch = {}
1295 fetch = {}
1296 seen = {}
1296 seen = {}
1297 seenbranch = {}
1297 seenbranch = {}
1298 if base == None:
1298 if base == None:
1299 base = {}
1299 base = {}
1300
1300
1301 if not heads:
1301 if not heads:
1302 heads = remote.heads()
1302 heads = remote.heads()
1303
1303
1304 if self.changelog.tip() == nullid:
1304 if self.changelog.tip() == nullid:
1305 base[nullid] = 1
1305 base[nullid] = 1
1306 if heads != [nullid]:
1306 if heads != [nullid]:
1307 return [nullid]
1307 return [nullid]
1308 return []
1308 return []
1309
1309
1310 # assume we're closer to the tip than the root
1310 # assume we're closer to the tip than the root
1311 # and start by examining the heads
1311 # and start by examining the heads
1312 self.ui.status(_("searching for changes\n"))
1312 self.ui.status(_("searching for changes\n"))
1313
1313
1314 unknown = []
1314 unknown = []
1315 for h in heads:
1315 for h in heads:
1316 if h not in m:
1316 if h not in m:
1317 unknown.append(h)
1317 unknown.append(h)
1318 else:
1318 else:
1319 base[h] = 1
1319 base[h] = 1
1320
1320
1321 if not unknown:
1321 if not unknown:
1322 return []
1322 return []
1323
1323
1324 req = dict.fromkeys(unknown)
1324 req = dict.fromkeys(unknown)
1325 reqcnt = 0
1325 reqcnt = 0
1326
1326
1327 # search through remote branches
1327 # search through remote branches
1328 # a 'branch' here is a linear segment of history, with four parts:
1328 # a 'branch' here is a linear segment of history, with four parts:
1329 # head, root, first parent, second parent
1329 # head, root, first parent, second parent
1330 # (a branch always has two parents (or none) by definition)
1330 # (a branch always has two parents (or none) by definition)
1331 unknown = remote.branches(unknown)
1331 unknown = remote.branches(unknown)
1332 while unknown:
1332 while unknown:
1333 r = []
1333 r = []
1334 while unknown:
1334 while unknown:
1335 n = unknown.pop(0)
1335 n = unknown.pop(0)
1336 if n[0] in seen:
1336 if n[0] in seen:
1337 continue
1337 continue
1338
1338
1339 self.ui.debug(_("examining %s:%s\n")
1339 self.ui.debug(_("examining %s:%s\n")
1340 % (short(n[0]), short(n[1])))
1340 % (short(n[0]), short(n[1])))
1341 if n[0] == nullid: # found the end of the branch
1341 if n[0] == nullid: # found the end of the branch
1342 pass
1342 pass
1343 elif n in seenbranch:
1343 elif n in seenbranch:
1344 self.ui.debug(_("branch already found\n"))
1344 self.ui.debug(_("branch already found\n"))
1345 continue
1345 continue
1346 elif n[1] and n[1] in m: # do we know the base?
1346 elif n[1] and n[1] in m: # do we know the base?
1347 self.ui.debug(_("found incomplete branch %s:%s\n")
1347 self.ui.debug(_("found incomplete branch %s:%s\n")
1348 % (short(n[0]), short(n[1])))
1348 % (short(n[0]), short(n[1])))
1349 search.append(n) # schedule branch range for scanning
1349 search.append(n) # schedule branch range for scanning
1350 seenbranch[n] = 1
1350 seenbranch[n] = 1
1351 else:
1351 else:
1352 if n[1] not in seen and n[1] not in fetch:
1352 if n[1] not in seen and n[1] not in fetch:
1353 if n[2] in m and n[3] in m:
1353 if n[2] in m and n[3] in m:
1354 self.ui.debug(_("found new changeset %s\n") %
1354 self.ui.debug(_("found new changeset %s\n") %
1355 short(n[1]))
1355 short(n[1]))
1356 fetch[n[1]] = 1 # earliest unknown
1356 fetch[n[1]] = 1 # earliest unknown
1357 for p in n[2:4]:
1357 for p in n[2:4]:
1358 if p in m:
1358 if p in m:
1359 base[p] = 1 # latest known
1359 base[p] = 1 # latest known
1360
1360
1361 for p in n[2:4]:
1361 for p in n[2:4]:
1362 if p not in req and p not in m:
1362 if p not in req and p not in m:
1363 r.append(p)
1363 r.append(p)
1364 req[p] = 1
1364 req[p] = 1
1365 seen[n[0]] = 1
1365 seen[n[0]] = 1
1366
1366
1367 if r:
1367 if r:
1368 reqcnt += 1
1368 reqcnt += 1
1369 self.ui.debug(_("request %d: %s\n") %
1369 self.ui.debug(_("request %d: %s\n") %
1370 (reqcnt, " ".join(map(short, r))))
1370 (reqcnt, " ".join(map(short, r))))
1371 for p in xrange(0, len(r), 10):
1371 for p in xrange(0, len(r), 10):
1372 for b in remote.branches(r[p:p+10]):
1372 for b in remote.branches(r[p:p+10]):
1373 self.ui.debug(_("received %s:%s\n") %
1373 self.ui.debug(_("received %s:%s\n") %
1374 (short(b[0]), short(b[1])))
1374 (short(b[0]), short(b[1])))
1375 unknown.append(b)
1375 unknown.append(b)
1376
1376
1377 # do binary search on the branches we found
1377 # do binary search on the branches we found
1378 while search:
1378 while search:
1379 n = search.pop(0)
1379 n = search.pop(0)
1380 reqcnt += 1
1380 reqcnt += 1
1381 l = remote.between([(n[0], n[1])])[0]
1381 l = remote.between([(n[0], n[1])])[0]
1382 l.append(n[1])
1382 l.append(n[1])
1383 p = n[0]
1383 p = n[0]
1384 f = 1
1384 f = 1
1385 for i in l:
1385 for i in l:
1386 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1386 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1387 if i in m:
1387 if i in m:
1388 if f <= 2:
1388 if f <= 2:
1389 self.ui.debug(_("found new branch changeset %s\n") %
1389 self.ui.debug(_("found new branch changeset %s\n") %
1390 short(p))
1390 short(p))
1391 fetch[p] = 1
1391 fetch[p] = 1
1392 base[i] = 1
1392 base[i] = 1
1393 else:
1393 else:
1394 self.ui.debug(_("narrowed branch search to %s:%s\n")
1394 self.ui.debug(_("narrowed branch search to %s:%s\n")
1395 % (short(p), short(i)))
1395 % (short(p), short(i)))
1396 search.append((p, i))
1396 search.append((p, i))
1397 break
1397 break
1398 p, f = i, f * 2
1398 p, f = i, f * 2
1399
1399
1400 # sanity check our fetch list
1400 # sanity check our fetch list
1401 for f in fetch.keys():
1401 for f in fetch.keys():
1402 if f in m:
1402 if f in m:
1403 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1403 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1404
1404
1405 if base.keys() == [nullid]:
1405 if base.keys() == [nullid]:
1406 if force:
1406 if force:
1407 self.ui.warn(_("warning: repository is unrelated\n"))
1407 self.ui.warn(_("warning: repository is unrelated\n"))
1408 else:
1408 else:
1409 raise util.Abort(_("repository is unrelated"))
1409 raise util.Abort(_("repository is unrelated"))
1410
1410
1411 self.ui.debug(_("found new changesets starting at ") +
1411 self.ui.debug(_("found new changesets starting at ") +
1412 " ".join([short(f) for f in fetch]) + "\n")
1412 " ".join([short(f) for f in fetch]) + "\n")
1413
1413
1414 self.ui.debug(_("%d total queries\n") % reqcnt)
1414 self.ui.debug(_("%d total queries\n") % reqcnt)
1415
1415
1416 return fetch.keys()
1416 return fetch.keys()
1417
1417
1418 def findoutgoing(self, remote, base=None, heads=None, force=False):
1418 def findoutgoing(self, remote, base=None, heads=None, force=False):
1419 """Return list of nodes that are roots of subsets not in remote
1419 """Return list of nodes that are roots of subsets not in remote
1420
1420
1421 If base dict is specified, assume that these nodes and their parents
1421 If base dict is specified, assume that these nodes and their parents
1422 exist on the remote side.
1422 exist on the remote side.
1423 If a list of heads is specified, return only nodes which are heads
1423 If a list of heads is specified, return only nodes which are heads
1424 or ancestors of these heads, and return a second element which
1424 or ancestors of these heads, and return a second element which
1425 contains all remote heads which get new children.
1425 contains all remote heads which get new children.
1426 """
1426 """
1427 if base == None:
1427 if base == None:
1428 base = {}
1428 base = {}
1429 self.findincoming(remote, base, heads, force=force)
1429 self.findincoming(remote, base, heads, force=force)
1430
1430
1431 self.ui.debug(_("common changesets up to ")
1431 self.ui.debug(_("common changesets up to ")
1432 + " ".join(map(short, base.keys())) + "\n")
1432 + " ".join(map(short, base.keys())) + "\n")
1433
1433
1434 remain = dict.fromkeys(self.changelog.nodemap)
1434 remain = dict.fromkeys(self.changelog.nodemap)
1435
1435
1436 # prune everything remote has from the tree
1436 # prune everything remote has from the tree
1437 del remain[nullid]
1437 del remain[nullid]
1438 remove = base.keys()
1438 remove = base.keys()
1439 while remove:
1439 while remove:
1440 n = remove.pop(0)
1440 n = remove.pop(0)
1441 if n in remain:
1441 if n in remain:
1442 del remain[n]
1442 del remain[n]
1443 for p in self.changelog.parents(n):
1443 for p in self.changelog.parents(n):
1444 remove.append(p)
1444 remove.append(p)
1445
1445
1446 # find every node whose parents have been pruned
1446 # find every node whose parents have been pruned
1447 subset = []
1447 subset = []
1448 # find every remote head that will get new children
1448 # find every remote head that will get new children
1449 updated_heads = {}
1449 updated_heads = {}
1450 for n in remain:
1450 for n in remain:
1451 p1, p2 = self.changelog.parents(n)
1451 p1, p2 = self.changelog.parents(n)
1452 if p1 not in remain and p2 not in remain:
1452 if p1 not in remain and p2 not in remain:
1453 subset.append(n)
1453 subset.append(n)
1454 if heads:
1454 if heads:
1455 if p1 in heads:
1455 if p1 in heads:
1456 updated_heads[p1] = True
1456 updated_heads[p1] = True
1457 if p2 in heads:
1457 if p2 in heads:
1458 updated_heads[p2] = True
1458 updated_heads[p2] = True
1459
1459
1460 # this is the set of all roots we have to push
1460 # this is the set of all roots we have to push
1461 if heads:
1461 if heads:
1462 return subset, updated_heads.keys()
1462 return subset, updated_heads.keys()
1463 else:
1463 else:
1464 return subset
1464 return subset
1465
1465
1466 def pull(self, remote, heads=None, force=False):
1466 def pull(self, remote, heads=None, force=False):
1467 lock = self.lock()
1467 lock = self.lock()
1468 try:
1468 try:
1469 fetch = self.findincoming(remote, heads=heads, force=force)
1469 fetch = self.findincoming(remote, heads=heads, force=force)
1470 if fetch == [nullid]:
1470 if fetch == [nullid]:
1471 self.ui.status(_("requesting all changes\n"))
1471 self.ui.status(_("requesting all changes\n"))
1472
1472
1473 if not fetch:
1473 if not fetch:
1474 self.ui.status(_("no changes found\n"))
1474 self.ui.status(_("no changes found\n"))
1475 return 0
1475 return 0
1476
1476
1477 if heads is None:
1477 if heads is None:
1478 cg = remote.changegroup(fetch, 'pull')
1478 cg = remote.changegroup(fetch, 'pull')
1479 else:
1479 else:
1480 if 'changegroupsubset' not in remote.capabilities:
1480 if 'changegroupsubset' not in remote.capabilities:
1481 raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
1481 raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
1482 cg = remote.changegroupsubset(fetch, heads, 'pull')
1482 cg = remote.changegroupsubset(fetch, heads, 'pull')
1483 return self.addchangegroup(cg, 'pull', remote.url())
1483 return self.addchangegroup(cg, 'pull', remote.url())
1484 finally:
1484 finally:
1485 del lock
1485 del lock
1486
1486
1487 def push(self, remote, force=False, revs=None):
1487 def push(self, remote, force=False, revs=None):
1488 # there are two ways to push to remote repo:
1488 # there are two ways to push to remote repo:
1489 #
1489 #
1490 # addchangegroup assumes local user can lock remote
1490 # addchangegroup assumes local user can lock remote
1491 # repo (local filesystem, old ssh servers).
1491 # repo (local filesystem, old ssh servers).
1492 #
1492 #
1493 # unbundle assumes local user cannot lock remote repo (new ssh
1493 # unbundle assumes local user cannot lock remote repo (new ssh
1494 # servers, http servers).
1494 # servers, http servers).
1495
1495
1496 if remote.capable('unbundle'):
1496 if remote.capable('unbundle'):
1497 return self.push_unbundle(remote, force, revs)
1497 return self.push_unbundle(remote, force, revs)
1498 return self.push_addchangegroup(remote, force, revs)
1498 return self.push_addchangegroup(remote, force, revs)
1499
1499
1500 def prepush(self, remote, force, revs):
1500 def prepush(self, remote, force, revs):
1501 base = {}
1501 base = {}
1502 remote_heads = remote.heads()
1502 remote_heads = remote.heads()
1503 inc = self.findincoming(remote, base, remote_heads, force=force)
1503 inc = self.findincoming(remote, base, remote_heads, force=force)
1504
1504
1505 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1505 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1506 if revs is not None:
1506 if revs is not None:
1507 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1507 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1508 else:
1508 else:
1509 bases, heads = update, self.changelog.heads()
1509 bases, heads = update, self.changelog.heads()
1510
1510
1511 if not bases:
1511 if not bases:
1512 self.ui.status(_("no changes found\n"))
1512 self.ui.status(_("no changes found\n"))
1513 return None, 1
1513 return None, 1
1514 elif not force:
1514 elif not force:
1515 # check if we're creating new remote heads
1515 # check if we're creating new remote heads
1516 # to be a remote head after push, node must be either
1516 # to be a remote head after push, node must be either
1517 # - unknown locally
1517 # - unknown locally
1518 # - a local outgoing head descended from update
1518 # - a local outgoing head descended from update
1519 # - a remote head that's known locally and not
1519 # - a remote head that's known locally and not
1520 # ancestral to an outgoing head
1520 # ancestral to an outgoing head
1521
1521
1522 warn = 0
1522 warn = 0
1523
1523
1524 if remote_heads == [nullid]:
1524 if remote_heads == [nullid]:
1525 warn = 0
1525 warn = 0
1526 elif not revs and len(heads) > len(remote_heads):
1526 elif not revs and len(heads) > len(remote_heads):
1527 warn = 1
1527 warn = 1
1528 else:
1528 else:
1529 newheads = list(heads)
1529 newheads = list(heads)
1530 for r in remote_heads:
1530 for r in remote_heads:
1531 if r in self.changelog.nodemap:
1531 if r in self.changelog.nodemap:
1532 desc = self.changelog.heads(r, heads)
1532 desc = self.changelog.heads(r, heads)
1533 l = [h for h in heads if h in desc]
1533 l = [h for h in heads if h in desc]
1534 if not l:
1534 if not l:
1535 newheads.append(r)
1535 newheads.append(r)
1536 else:
1536 else:
1537 newheads.append(r)
1537 newheads.append(r)
1538 if len(newheads) > len(remote_heads):
1538 if len(newheads) > len(remote_heads):
1539 warn = 1
1539 warn = 1
1540
1540
1541 if warn:
1541 if warn:
1542 self.ui.warn(_("abort: push creates new remote heads!\n"))
1542 self.ui.warn(_("abort: push creates new remote heads!\n"))
1543 self.ui.status(_("(did you forget to merge?"
1543 self.ui.status(_("(did you forget to merge?"
1544 " use push -f to force)\n"))
1544 " use push -f to force)\n"))
1545 return None, 0
1545 return None, 0
1546 elif inc:
1546 elif inc:
1547 self.ui.warn(_("note: unsynced remote changes!\n"))
1547 self.ui.warn(_("note: unsynced remote changes!\n"))
1548
1548
1549
1549
1550 if revs is None:
1550 if revs is None:
1551 cg = self.changegroup(update, 'push')
1551 cg = self.changegroup(update, 'push')
1552 else:
1552 else:
1553 cg = self.changegroupsubset(update, revs, 'push')
1553 cg = self.changegroupsubset(update, revs, 'push')
1554 return cg, remote_heads
1554 return cg, remote_heads
1555
1555
1556 def push_addchangegroup(self, remote, force, revs):
1556 def push_addchangegroup(self, remote, force, revs):
1557 lock = remote.lock()
1557 lock = remote.lock()
1558 try:
1558 try:
1559 ret = self.prepush(remote, force, revs)
1559 ret = self.prepush(remote, force, revs)
1560 if ret[0] is not None:
1560 if ret[0] is not None:
1561 cg, remote_heads = ret
1561 cg, remote_heads = ret
1562 return remote.addchangegroup(cg, 'push', self.url())
1562 return remote.addchangegroup(cg, 'push', self.url())
1563 return ret[1]
1563 return ret[1]
1564 finally:
1564 finally:
1565 del lock
1565 del lock
1566
1566
1567 def push_unbundle(self, remote, force, revs):
1567 def push_unbundle(self, remote, force, revs):
1568 # local repo finds heads on server, finds out what revs it
1568 # local repo finds heads on server, finds out what revs it
1569 # must push. once revs transferred, if server finds it has
1569 # must push. once revs transferred, if server finds it has
1570 # different heads (someone else won commit/push race), server
1570 # different heads (someone else won commit/push race), server
1571 # aborts.
1571 # aborts.
1572
1572
1573 ret = self.prepush(remote, force, revs)
1573 ret = self.prepush(remote, force, revs)
1574 if ret[0] is not None:
1574 if ret[0] is not None:
1575 cg, remote_heads = ret
1575 cg, remote_heads = ret
1576 if force: remote_heads = ['force']
1576 if force: remote_heads = ['force']
1577 return remote.unbundle(cg, remote_heads, 'push')
1577 return remote.unbundle(cg, remote_heads, 'push')
1578 return ret[1]
1578 return ret[1]
1579
1579
1580 def changegroupinfo(self, nodes, source):
1580 def changegroupinfo(self, nodes, source):
1581 if self.ui.verbose or source == 'bundle':
1581 if self.ui.verbose or source == 'bundle':
1582 self.ui.status(_("%d changesets found\n") % len(nodes))
1582 self.ui.status(_("%d changesets found\n") % len(nodes))
1583 if self.ui.debugflag:
1583 if self.ui.debugflag:
1584 self.ui.debug(_("List of changesets:\n"))
1584 self.ui.debug(_("List of changesets:\n"))
1585 for node in nodes:
1585 for node in nodes:
1586 self.ui.debug("%s\n" % hex(node))
1586 self.ui.debug("%s\n" % hex(node))
1587
1587
1588 def changegroupsubset(self, bases, heads, source, extranodes=None):
1588 def changegroupsubset(self, bases, heads, source, extranodes=None):
1589 """This function generates a changegroup consisting of all the nodes
1589 """This function generates a changegroup consisting of all the nodes
1590 that are descendents of any of the bases, and ancestors of any of
1590 that are descendents of any of the bases, and ancestors of any of
1591 the heads.
1591 the heads.
1592
1592
1593 It is fairly complex as determining which filenodes and which
1593 It is fairly complex as determining which filenodes and which
1594 manifest nodes need to be included for the changeset to be complete
1594 manifest nodes need to be included for the changeset to be complete
1595 is non-trivial.
1595 is non-trivial.
1596
1596
1597 Another wrinkle is doing the reverse, figuring out which changeset in
1597 Another wrinkle is doing the reverse, figuring out which changeset in
1598 the changegroup a particular filenode or manifestnode belongs to.
1598 the changegroup a particular filenode or manifestnode belongs to.
1599
1599
1600 The caller can specify some nodes that must be included in the
1600 The caller can specify some nodes that must be included in the
1601 changegroup using the extranodes argument. It should be a dict
1601 changegroup using the extranodes argument. It should be a dict
1602 where the keys are the filenames (or 1 for the manifest), and the
1602 where the keys are the filenames (or 1 for the manifest), and the
1603 values are lists of (node, linknode) tuples, where node is a wanted
1603 values are lists of (node, linknode) tuples, where node is a wanted
1604 node and linknode is the changelog node that should be transmitted as
1604 node and linknode is the changelog node that should be transmitted as
1605 the linkrev.
1605 the linkrev.
1606 """
1606 """
1607
1607
1608 self.hook('preoutgoing', throw=True, source=source)
1608 self.hook('preoutgoing', throw=True, source=source)
1609
1609
1610 # Set up some initial variables
1610 # Set up some initial variables
1611 # Make it easy to refer to self.changelog
1611 # Make it easy to refer to self.changelog
1612 cl = self.changelog
1612 cl = self.changelog
1613 # msng is short for missing - compute the list of changesets in this
1613 # msng is short for missing - compute the list of changesets in this
1614 # changegroup.
1614 # changegroup.
1615 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1615 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1616 self.changegroupinfo(msng_cl_lst, source)
1616 self.changegroupinfo(msng_cl_lst, source)
1617 # Some bases may turn out to be superfluous, and some heads may be
1617 # Some bases may turn out to be superfluous, and some heads may be
1618 # too. nodesbetween will return the minimal set of bases and heads
1618 # too. nodesbetween will return the minimal set of bases and heads
1619 # necessary to re-create the changegroup.
1619 # necessary to re-create the changegroup.
1620
1620
1621 # Known heads are the list of heads that it is assumed the recipient
1621 # Known heads are the list of heads that it is assumed the recipient
1622 # of this changegroup will know about.
1622 # of this changegroup will know about.
1623 knownheads = {}
1623 knownheads = {}
1624 # We assume that all parents of bases are known heads.
1624 # We assume that all parents of bases are known heads.
1625 for n in bases:
1625 for n in bases:
1626 for p in cl.parents(n):
1626 for p in cl.parents(n):
1627 if p != nullid:
1627 if p != nullid:
1628 knownheads[p] = 1
1628 knownheads[p] = 1
1629 knownheads = knownheads.keys()
1629 knownheads = knownheads.keys()
1630 if knownheads:
1630 if knownheads:
1631 # Now that we know what heads are known, we can compute which
1631 # Now that we know what heads are known, we can compute which
1632 # changesets are known. The recipient must know about all
1632 # changesets are known. The recipient must know about all
1633 # changesets required to reach the known heads from the null
1633 # changesets required to reach the known heads from the null
1634 # changeset.
1634 # changeset.
1635 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1635 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1636 junk = None
1636 junk = None
1637 # Transform the list into an ersatz set.
1637 # Transform the list into an ersatz set.
1638 has_cl_set = dict.fromkeys(has_cl_set)
1638 has_cl_set = dict.fromkeys(has_cl_set)
1639 else:
1639 else:
1640 # If there were no known heads, the recipient cannot be assumed to
1640 # If there were no known heads, the recipient cannot be assumed to
1641 # know about any changesets.
1641 # know about any changesets.
1642 has_cl_set = {}
1642 has_cl_set = {}
1643
1643
1644 # Make it easy to refer to self.manifest
1644 # Make it easy to refer to self.manifest
1645 mnfst = self.manifest
1645 mnfst = self.manifest
1646 # We don't know which manifests are missing yet
1646 # We don't know which manifests are missing yet
1647 msng_mnfst_set = {}
1647 msng_mnfst_set = {}
1648 # Nor do we know which filenodes are missing.
1648 # Nor do we know which filenodes are missing.
1649 msng_filenode_set = {}
1649 msng_filenode_set = {}
1650
1650
1651 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1651 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1652 junk = None
1652 junk = None
1653
1653
1654 # A changeset always belongs to itself, so the changenode lookup
1654 # A changeset always belongs to itself, so the changenode lookup
1655 # function for a changenode is identity.
1655 # function for a changenode is identity.
1656 def identity(x):
1656 def identity(x):
1657 return x
1657 return x
1658
1658
1659 # A function generating function. Sets up an environment for the
1659 # A function generating function. Sets up an environment for the
1660 # inner function.
1660 # inner function.
1661 def cmp_by_rev_func(revlog):
1661 def cmp_by_rev_func(revlog):
1662 # Compare two nodes by their revision number in the environment's
1662 # Compare two nodes by their revision number in the environment's
1663 # revision history. Since the revision number both represents the
1663 # revision history. Since the revision number both represents the
1664 # most efficient order to read the nodes in, and represents a
1664 # most efficient order to read the nodes in, and represents a
1665 # topological sorting of the nodes, this function is often useful.
1665 # topological sorting of the nodes, this function is often useful.
1666 def cmp_by_rev(a, b):
1666 def cmp_by_rev(a, b):
1667 return cmp(revlog.rev(a), revlog.rev(b))
1667 return cmp(revlog.rev(a), revlog.rev(b))
1668 return cmp_by_rev
1668 return cmp_by_rev
1669
1669
1670 # If we determine that a particular file or manifest node must be a
1670 # If we determine that a particular file or manifest node must be a
1671 # node that the recipient of the changegroup will already have, we can
1671 # node that the recipient of the changegroup will already have, we can
1672 # also assume the recipient will have all the parents. This function
1672 # also assume the recipient will have all the parents. This function
1673 # prunes them from the set of missing nodes.
1673 # prunes them from the set of missing nodes.
1674 def prune_parents(revlog, hasset, msngset):
1674 def prune_parents(revlog, hasset, msngset):
1675 haslst = hasset.keys()
1675 haslst = hasset.keys()
1676 haslst.sort(cmp_by_rev_func(revlog))
1676 haslst.sort(cmp_by_rev_func(revlog))
1677 for node in haslst:
1677 for node in haslst:
1678 parentlst = [p for p in revlog.parents(node) if p != nullid]
1678 parentlst = [p for p in revlog.parents(node) if p != nullid]
1679 while parentlst:
1679 while parentlst:
1680 n = parentlst.pop()
1680 n = parentlst.pop()
1681 if n not in hasset:
1681 if n not in hasset:
1682 hasset[n] = 1
1682 hasset[n] = 1
1683 p = [p for p in revlog.parents(n) if p != nullid]
1683 p = [p for p in revlog.parents(n) if p != nullid]
1684 parentlst.extend(p)
1684 parentlst.extend(p)
1685 for n in hasset:
1685 for n in hasset:
1686 msngset.pop(n, None)
1686 msngset.pop(n, None)
1687
1687
1688 # This is a function generating function used to set up an environment
1688 # This is a function generating function used to set up an environment
1689 # for the inner function to execute in.
1689 # for the inner function to execute in.
1690 def manifest_and_file_collector(changedfileset):
1690 def manifest_and_file_collector(changedfileset):
1691 # This is an information gathering function that gathers
1691 # This is an information gathering function that gathers
1692 # information from each changeset node that goes out as part of
1692 # information from each changeset node that goes out as part of
1693 # the changegroup. The information gathered is a list of which
1693 # the changegroup. The information gathered is a list of which
1694 # manifest nodes are potentially required (the recipient may
1694 # manifest nodes are potentially required (the recipient may
1695 # already have them) and total list of all files which were
1695 # already have them) and total list of all files which were
1696 # changed in any changeset in the changegroup.
1696 # changed in any changeset in the changegroup.
1697 #
1697 #
1698 # We also remember the first changenode we saw any manifest
1698 # We also remember the first changenode we saw any manifest
1699 # referenced by so we can later determine which changenode 'owns'
1699 # referenced by so we can later determine which changenode 'owns'
1700 # the manifest.
1700 # the manifest.
1701 def collect_manifests_and_files(clnode):
1701 def collect_manifests_and_files(clnode):
1702 c = cl.read(clnode)
1702 c = cl.read(clnode)
1703 for f in c[3]:
1703 for f in c[3]:
1704 # This is to make sure we only have one instance of each
1704 # This is to make sure we only have one instance of each
1705 # filename string for each filename.
1705 # filename string for each filename.
1706 changedfileset.setdefault(f, f)
1706 changedfileset.setdefault(f, f)
1707 msng_mnfst_set.setdefault(c[0], clnode)
1707 msng_mnfst_set.setdefault(c[0], clnode)
1708 return collect_manifests_and_files
1708 return collect_manifests_and_files
1709
1709
1710 # Figure out which manifest nodes (of the ones we think might be part
1710 # Figure out which manifest nodes (of the ones we think might be part
1711 # of the changegroup) the recipient must know about and remove them
1711 # of the changegroup) the recipient must know about and remove them
1712 # from the changegroup.
1712 # from the changegroup.
1713 def prune_manifests():
1713 def prune_manifests():
1714 has_mnfst_set = {}
1714 has_mnfst_set = {}
1715 for n in msng_mnfst_set:
1715 for n in msng_mnfst_set:
1716 # If a 'missing' manifest thinks it belongs to a changenode
1716 # If a 'missing' manifest thinks it belongs to a changenode
1717 # the recipient is assumed to have, obviously the recipient
1717 # the recipient is assumed to have, obviously the recipient
1718 # must have that manifest.
1718 # must have that manifest.
1719 linknode = cl.node(mnfst.linkrev(n))
1719 linknode = cl.node(mnfst.linkrev(n))
1720 if linknode in has_cl_set:
1720 if linknode in has_cl_set:
1721 has_mnfst_set[n] = 1
1721 has_mnfst_set[n] = 1
1722 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1722 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1723
1723
1724 # Use the information collected in collect_manifests_and_files to say
1724 # Use the information collected in collect_manifests_and_files to say
1725 # which changenode any manifestnode belongs to.
1725 # which changenode any manifestnode belongs to.
1726 def lookup_manifest_link(mnfstnode):
1726 def lookup_manifest_link(mnfstnode):
1727 return msng_mnfst_set[mnfstnode]
1727 return msng_mnfst_set[mnfstnode]
1728
1728
1729 # A function generating function that sets up the initial environment
1729 # A function generating function that sets up the initial environment
1730 # the inner function.
1730 # the inner function.
1731 def filenode_collector(changedfiles):
1731 def filenode_collector(changedfiles):
1732 next_rev = [0]
1732 next_rev = [0]
1733 # This gathers information from each manifestnode included in the
1733 # This gathers information from each manifestnode included in the
1734 # changegroup about which filenodes the manifest node references
1734 # changegroup about which filenodes the manifest node references
1735 # so we can include those in the changegroup too.
1735 # so we can include those in the changegroup too.
1736 #
1736 #
1737 # It also remembers which changenode each filenode belongs to. It
1737 # It also remembers which changenode each filenode belongs to. It
1738 # does this by assuming the a filenode belongs to the changenode
1738 # does this by assuming the a filenode belongs to the changenode
1739 # the first manifest that references it belongs to.
1739 # the first manifest that references it belongs to.
1740 def collect_msng_filenodes(mnfstnode):
1740 def collect_msng_filenodes(mnfstnode):
1741 r = mnfst.rev(mnfstnode)
1741 r = mnfst.rev(mnfstnode)
1742 if r == next_rev[0]:
1742 if r == next_rev[0]:
1743 # If the last rev we looked at was the one just previous,
1743 # If the last rev we looked at was the one just previous,
1744 # we only need to see a diff.
1744 # we only need to see a diff.
1745 deltamf = mnfst.readdelta(mnfstnode)
1745 deltamf = mnfst.readdelta(mnfstnode)
1746 # For each line in the delta
1746 # For each line in the delta
1747 for f, fnode in deltamf.items():
1747 for f, fnode in deltamf.items():
1748 f = changedfiles.get(f, None)
1748 f = changedfiles.get(f, None)
1749 # And if the file is in the list of files we care
1749 # And if the file is in the list of files we care
1750 # about.
1750 # about.
1751 if f is not None:
1751 if f is not None:
1752 # Get the changenode this manifest belongs to
1752 # Get the changenode this manifest belongs to
1753 clnode = msng_mnfst_set[mnfstnode]
1753 clnode = msng_mnfst_set[mnfstnode]
1754 # Create the set of filenodes for the file if
1754 # Create the set of filenodes for the file if
1755 # there isn't one already.
1755 # there isn't one already.
1756 ndset = msng_filenode_set.setdefault(f, {})
1756 ndset = msng_filenode_set.setdefault(f, {})
1757 # And set the filenode's changelog node to the
1757 # And set the filenode's changelog node to the
1758 # manifest's if it hasn't been set already.
1758 # manifest's if it hasn't been set already.
1759 ndset.setdefault(fnode, clnode)
1759 ndset.setdefault(fnode, clnode)
1760 else:
1760 else:
1761 # Otherwise we need a full manifest.
1761 # Otherwise we need a full manifest.
1762 m = mnfst.read(mnfstnode)
1762 m = mnfst.read(mnfstnode)
1763 # For every file in we care about.
1763 # For every file in we care about.
1764 for f in changedfiles:
1764 for f in changedfiles:
1765 fnode = m.get(f, None)
1765 fnode = m.get(f, None)
1766 # If it's in the manifest
1766 # If it's in the manifest
1767 if fnode is not None:
1767 if fnode is not None:
1768 # See comments above.
1768 # See comments above.
1769 clnode = msng_mnfst_set[mnfstnode]
1769 clnode = msng_mnfst_set[mnfstnode]
1770 ndset = msng_filenode_set.setdefault(f, {})
1770 ndset = msng_filenode_set.setdefault(f, {})
1771 ndset.setdefault(fnode, clnode)
1771 ndset.setdefault(fnode, clnode)
1772 # Remember the revision we hope to see next.
1772 # Remember the revision we hope to see next.
1773 next_rev[0] = r + 1
1773 next_rev[0] = r + 1
1774 return collect_msng_filenodes
1774 return collect_msng_filenodes
1775
1775
1776 # We have a list of filenodes we think we need for a file, lets remove
1776 # We have a list of filenodes we think we need for a file, lets remove
1777 # all those we now the recipient must have.
1777 # all those we now the recipient must have.
1778 def prune_filenodes(f, filerevlog):
1778 def prune_filenodes(f, filerevlog):
1779 msngset = msng_filenode_set[f]
1779 msngset = msng_filenode_set[f]
1780 hasset = {}
1780 hasset = {}
1781 # If a 'missing' filenode thinks it belongs to a changenode we
1781 # If a 'missing' filenode thinks it belongs to a changenode we
1782 # assume the recipient must have, then the recipient must have
1782 # assume the recipient must have, then the recipient must have
1783 # that filenode.
1783 # that filenode.
1784 for n in msngset:
1784 for n in msngset:
1785 clnode = cl.node(filerevlog.linkrev(n))
1785 clnode = cl.node(filerevlog.linkrev(n))
1786 if clnode in has_cl_set:
1786 if clnode in has_cl_set:
1787 hasset[n] = 1
1787 hasset[n] = 1
1788 prune_parents(filerevlog, hasset, msngset)
1788 prune_parents(filerevlog, hasset, msngset)
1789
1789
1790 # A function generator function that sets up the a context for the
1790 # A function generator function that sets up the a context for the
1791 # inner function.
1791 # inner function.
1792 def lookup_filenode_link_func(fname):
1792 def lookup_filenode_link_func(fname):
1793 msngset = msng_filenode_set[fname]
1793 msngset = msng_filenode_set[fname]
1794 # Lookup the changenode the filenode belongs to.
1794 # Lookup the changenode the filenode belongs to.
1795 def lookup_filenode_link(fnode):
1795 def lookup_filenode_link(fnode):
1796 return msngset[fnode]
1796 return msngset[fnode]
1797 return lookup_filenode_link
1797 return lookup_filenode_link
1798
1798
1799 # Add the nodes that were explicitly requested.
1799 # Add the nodes that were explicitly requested.
1800 def add_extra_nodes(name, nodes):
1800 def add_extra_nodes(name, nodes):
1801 if not extranodes or name not in extranodes:
1801 if not extranodes or name not in extranodes:
1802 return
1802 return
1803
1803
1804 for node, linknode in extranodes[name]:
1804 for node, linknode in extranodes[name]:
1805 if node not in nodes:
1805 if node not in nodes:
1806 nodes[node] = linknode
1806 nodes[node] = linknode
1807
1807
1808 # Now that we have all theses utility functions to help out and
1808 # Now that we have all theses utility functions to help out and
1809 # logically divide up the task, generate the group.
1809 # logically divide up the task, generate the group.
1810 def gengroup():
1810 def gengroup():
1811 # The set of changed files starts empty.
1811 # The set of changed files starts empty.
1812 changedfiles = {}
1812 changedfiles = {}
1813 # Create a changenode group generator that will call our functions
1813 # Create a changenode group generator that will call our functions
1814 # back to lookup the owning changenode and collect information.
1814 # back to lookup the owning changenode and collect information.
1815 group = cl.group(msng_cl_lst, identity,
1815 group = cl.group(msng_cl_lst, identity,
1816 manifest_and_file_collector(changedfiles))
1816 manifest_and_file_collector(changedfiles))
1817 for chnk in group:
1817 for chnk in group:
1818 yield chnk
1818 yield chnk
1819
1819
1820 # The list of manifests has been collected by the generator
1820 # The list of manifests has been collected by the generator
1821 # calling our functions back.
1821 # calling our functions back.
1822 prune_manifests()
1822 prune_manifests()
1823 add_extra_nodes(1, msng_mnfst_set)
1823 add_extra_nodes(1, msng_mnfst_set)
1824 msng_mnfst_lst = msng_mnfst_set.keys()
1824 msng_mnfst_lst = msng_mnfst_set.keys()
1825 # Sort the manifestnodes by revision number.
1825 # Sort the manifestnodes by revision number.
1826 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1826 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1827 # Create a generator for the manifestnodes that calls our lookup
1827 # Create a generator for the manifestnodes that calls our lookup
1828 # and data collection functions back.
1828 # and data collection functions back.
1829 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1829 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1830 filenode_collector(changedfiles))
1830 filenode_collector(changedfiles))
1831 for chnk in group:
1831 for chnk in group:
1832 yield chnk
1832 yield chnk
1833
1833
1834 # These are no longer needed, dereference and toss the memory for
1834 # These are no longer needed, dereference and toss the memory for
1835 # them.
1835 # them.
1836 msng_mnfst_lst = None
1836 msng_mnfst_lst = None
1837 msng_mnfst_set.clear()
1837 msng_mnfst_set.clear()
1838
1838
1839 if extranodes:
1839 if extranodes:
1840 for fname in extranodes:
1840 for fname in extranodes:
1841 if isinstance(fname, int):
1841 if isinstance(fname, int):
1842 continue
1842 continue
1843 add_extra_nodes(fname,
1843 add_extra_nodes(fname,
1844 msng_filenode_set.setdefault(fname, {}))
1844 msng_filenode_set.setdefault(fname, {}))
1845 changedfiles[fname] = 1
1845 changedfiles[fname] = 1
1846 changedfiles = changedfiles.keys()
1846 changedfiles = changedfiles.keys()
1847 changedfiles.sort()
1847 changedfiles.sort()
1848 # Go through all our files in order sorted by name.
1848 # Go through all our files in order sorted by name.
1849 for fname in changedfiles:
1849 for fname in changedfiles:
1850 filerevlog = self.file(fname)
1850 filerevlog = self.file(fname)
1851 if filerevlog.count() == 0:
1851 if filerevlog.count() == 0:
1852 raise util.Abort(_("empty or missing revlog for %s") % fname)
1852 raise util.Abort(_("empty or missing revlog for %s") % fname)
1853 # Toss out the filenodes that the recipient isn't really
1853 # Toss out the filenodes that the recipient isn't really
1854 # missing.
1854 # missing.
1855 if fname in msng_filenode_set:
1855 if fname in msng_filenode_set:
1856 prune_filenodes(fname, filerevlog)
1856 prune_filenodes(fname, filerevlog)
1857 msng_filenode_lst = msng_filenode_set[fname].keys()
1857 msng_filenode_lst = msng_filenode_set[fname].keys()
1858 else:
1858 else:
1859 msng_filenode_lst = []
1859 msng_filenode_lst = []
1860 # If any filenodes are left, generate the group for them,
1860 # If any filenodes are left, generate the group for them,
1861 # otherwise don't bother.
1861 # otherwise don't bother.
1862 if len(msng_filenode_lst) > 0:
1862 if len(msng_filenode_lst) > 0:
1863 yield changegroup.chunkheader(len(fname))
1863 yield changegroup.chunkheader(len(fname))
1864 yield fname
1864 yield fname
1865 # Sort the filenodes by their revision #
1865 # Sort the filenodes by their revision #
1866 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1866 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1867 # Create a group generator and only pass in a changenode
1867 # Create a group generator and only pass in a changenode
1868 # lookup function as we need to collect no information
1868 # lookup function as we need to collect no information
1869 # from filenodes.
1869 # from filenodes.
1870 group = filerevlog.group(msng_filenode_lst,
1870 group = filerevlog.group(msng_filenode_lst,
1871 lookup_filenode_link_func(fname))
1871 lookup_filenode_link_func(fname))
1872 for chnk in group:
1872 for chnk in group:
1873 yield chnk
1873 yield chnk
1874 if fname in msng_filenode_set:
1874 if fname in msng_filenode_set:
1875 # Don't need this anymore, toss it to free memory.
1875 # Don't need this anymore, toss it to free memory.
1876 del msng_filenode_set[fname]
1876 del msng_filenode_set[fname]
1877 # Signal that no more groups are left.
1877 # Signal that no more groups are left.
1878 yield changegroup.closechunk()
1878 yield changegroup.closechunk()
1879
1879
1880 if msng_cl_lst:
1880 if msng_cl_lst:
1881 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1881 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1882
1882
1883 return util.chunkbuffer(gengroup())
1883 return util.chunkbuffer(gengroup())
1884
1884
1885 def changegroup(self, basenodes, source):
1885 def changegroup(self, basenodes, source):
1886 """Generate a changegroup of all nodes that we have that a recipient
1886 """Generate a changegroup of all nodes that we have that a recipient
1887 doesn't.
1887 doesn't.
1888
1888
1889 This is much easier than the previous function as we can assume that
1889 This is much easier than the previous function as we can assume that
1890 the recipient has any changenode we aren't sending them."""
1890 the recipient has any changenode we aren't sending them."""
1891
1891
1892 self.hook('preoutgoing', throw=True, source=source)
1892 self.hook('preoutgoing', throw=True, source=source)
1893
1893
1894 cl = self.changelog
1894 cl = self.changelog
1895 nodes = cl.nodesbetween(basenodes, None)[0]
1895 nodes = cl.nodesbetween(basenodes, None)[0]
1896 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1896 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1897 self.changegroupinfo(nodes, source)
1897 self.changegroupinfo(nodes, source)
1898
1898
1899 def identity(x):
1899 def identity(x):
1900 return x
1900 return x
1901
1901
1902 def gennodelst(revlog):
1902 def gennodelst(revlog):
1903 for r in xrange(0, revlog.count()):
1903 for r in xrange(0, revlog.count()):
1904 n = revlog.node(r)
1904 n = revlog.node(r)
1905 if revlog.linkrev(n) in revset:
1905 if revlog.linkrev(n) in revset:
1906 yield n
1906 yield n
1907
1907
1908 def changed_file_collector(changedfileset):
1908 def changed_file_collector(changedfileset):
1909 def collect_changed_files(clnode):
1909 def collect_changed_files(clnode):
1910 c = cl.read(clnode)
1910 c = cl.read(clnode)
1911 for fname in c[3]:
1911 for fname in c[3]:
1912 changedfileset[fname] = 1
1912 changedfileset[fname] = 1
1913 return collect_changed_files
1913 return collect_changed_files
1914
1914
1915 def lookuprevlink_func(revlog):
1915 def lookuprevlink_func(revlog):
1916 def lookuprevlink(n):
1916 def lookuprevlink(n):
1917 return cl.node(revlog.linkrev(n))
1917 return cl.node(revlog.linkrev(n))
1918 return lookuprevlink
1918 return lookuprevlink
1919
1919
1920 def gengroup():
1920 def gengroup():
1921 # construct a list of all changed files
1921 # construct a list of all changed files
1922 changedfiles = {}
1922 changedfiles = {}
1923
1923
1924 for chnk in cl.group(nodes, identity,
1924 for chnk in cl.group(nodes, identity,
1925 changed_file_collector(changedfiles)):
1925 changed_file_collector(changedfiles)):
1926 yield chnk
1926 yield chnk
1927 changedfiles = changedfiles.keys()
1927 changedfiles = changedfiles.keys()
1928 changedfiles.sort()
1928 changedfiles.sort()
1929
1929
1930 mnfst = self.manifest
1930 mnfst = self.manifest
1931 nodeiter = gennodelst(mnfst)
1931 nodeiter = gennodelst(mnfst)
1932 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1932 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1933 yield chnk
1933 yield chnk
1934
1934
1935 for fname in changedfiles:
1935 for fname in changedfiles:
1936 filerevlog = self.file(fname)
1936 filerevlog = self.file(fname)
1937 if filerevlog.count() == 0:
1937 if filerevlog.count() == 0:
1938 raise util.Abort(_("empty or missing revlog for %s") % fname)
1938 raise util.Abort(_("empty or missing revlog for %s") % fname)
1939 nodeiter = gennodelst(filerevlog)
1939 nodeiter = gennodelst(filerevlog)
1940 nodeiter = list(nodeiter)
1940 nodeiter = list(nodeiter)
1941 if nodeiter:
1941 if nodeiter:
1942 yield changegroup.chunkheader(len(fname))
1942 yield changegroup.chunkheader(len(fname))
1943 yield fname
1943 yield fname
1944 lookup = lookuprevlink_func(filerevlog)
1944 lookup = lookuprevlink_func(filerevlog)
1945 for chnk in filerevlog.group(nodeiter, lookup):
1945 for chnk in filerevlog.group(nodeiter, lookup):
1946 yield chnk
1946 yield chnk
1947
1947
1948 yield changegroup.closechunk()
1948 yield changegroup.closechunk()
1949
1949
1950 if nodes:
1950 if nodes:
1951 self.hook('outgoing', node=hex(nodes[0]), source=source)
1951 self.hook('outgoing', node=hex(nodes[0]), source=source)
1952
1952
1953 return util.chunkbuffer(gengroup())
1953 return util.chunkbuffer(gengroup())
1954
1954
1955 def addchangegroup(self, source, srctype, url, emptyok=False):
1955 def addchangegroup(self, source, srctype, url, emptyok=False):
1956 """add changegroup to repo.
1956 """add changegroup to repo.
1957
1957
1958 return values:
1958 return values:
1959 - nothing changed or no source: 0
1959 - nothing changed or no source: 0
1960 - more heads than before: 1+added heads (2..n)
1960 - more heads than before: 1+added heads (2..n)
1961 - less heads than before: -1-removed heads (-2..-n)
1961 - less heads than before: -1-removed heads (-2..-n)
1962 - number of heads stays the same: 1
1962 - number of heads stays the same: 1
1963 """
1963 """
1964 def csmap(x):
1964 def csmap(x):
1965 self.ui.debug(_("add changeset %s\n") % short(x))
1965 self.ui.debug(_("add changeset %s\n") % short(x))
1966 return cl.count()
1966 return cl.count()
1967
1967
1968 def revmap(x):
1968 def revmap(x):
1969 return cl.rev(x)
1969 return cl.rev(x)
1970
1970
1971 if not source:
1971 if not source:
1972 return 0
1972 return 0
1973
1973
1974 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1974 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1975
1975
1976 changesets = files = revisions = 0
1976 changesets = files = revisions = 0
1977
1977
1978 # write changelog data to temp files so concurrent readers will not see
1978 # write changelog data to temp files so concurrent readers will not see
1979 # inconsistent view
1979 # inconsistent view
1980 cl = self.changelog
1980 cl = self.changelog
1981 cl.delayupdate()
1981 cl.delayupdate()
1982 oldheads = len(cl.heads())
1982 oldheads = len(cl.heads())
1983
1983
1984 tr = self.transaction()
1984 tr = self.transaction()
1985 try:
1985 try:
1986 trp = weakref.proxy(tr)
1986 trp = weakref.proxy(tr)
1987 # pull off the changeset group
1987 # pull off the changeset group
1988 self.ui.status(_("adding changesets\n"))
1988 self.ui.status(_("adding changesets\n"))
1989 cor = cl.count() - 1
1989 cor = cl.count() - 1
1990 chunkiter = changegroup.chunkiter(source)
1990 chunkiter = changegroup.chunkiter(source)
1991 if cl.addgroup(chunkiter, csmap, trp, 1) is None and not emptyok:
1991 if cl.addgroup(chunkiter, csmap, trp, 1) is None and not emptyok:
1992 raise util.Abort(_("received changelog group is empty"))
1992 raise util.Abort(_("received changelog group is empty"))
1993 cnr = cl.count() - 1
1993 cnr = cl.count() - 1
1994 changesets = cnr - cor
1994 changesets = cnr - cor
1995
1995
1996 # pull off the manifest group
1996 # pull off the manifest group
1997 self.ui.status(_("adding manifests\n"))
1997 self.ui.status(_("adding manifests\n"))
1998 chunkiter = changegroup.chunkiter(source)
1998 chunkiter = changegroup.chunkiter(source)
1999 # no need to check for empty manifest group here:
1999 # no need to check for empty manifest group here:
2000 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2000 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2001 # no new manifest will be created and the manifest group will
2001 # no new manifest will be created and the manifest group will
2002 # be empty during the pull
2002 # be empty during the pull
2003 self.manifest.addgroup(chunkiter, revmap, trp)
2003 self.manifest.addgroup(chunkiter, revmap, trp)
2004
2004
2005 # process the files
2005 # process the files
2006 self.ui.status(_("adding file changes\n"))
2006 self.ui.status(_("adding file changes\n"))
2007 while 1:
2007 while 1:
2008 f = changegroup.getchunk(source)
2008 f = changegroup.getchunk(source)
2009 if not f:
2009 if not f:
2010 break
2010 break
2011 self.ui.debug(_("adding %s revisions\n") % f)
2011 self.ui.debug(_("adding %s revisions\n") % f)
2012 fl = self.file(f)
2012 fl = self.file(f)
2013 o = fl.count()
2013 o = fl.count()
2014 chunkiter = changegroup.chunkiter(source)
2014 chunkiter = changegroup.chunkiter(source)
2015 if fl.addgroup(chunkiter, revmap, trp) is None:
2015 if fl.addgroup(chunkiter, revmap, trp) is None:
2016 raise util.Abort(_("received file revlog group is empty"))
2016 raise util.Abort(_("received file revlog group is empty"))
2017 revisions += fl.count() - o
2017 revisions += fl.count() - o
2018 files += 1
2018 files += 1
2019
2019
2020 # make changelog see real files again
2020 # make changelog see real files again
2021 cl.finalize(trp)
2021 cl.finalize(trp)
2022
2022
2023 newheads = len(self.changelog.heads())
2023 newheads = len(self.changelog.heads())
2024 heads = ""
2024 heads = ""
2025 if oldheads and newheads != oldheads:
2025 if oldheads and newheads != oldheads:
2026 heads = _(" (%+d heads)") % (newheads - oldheads)
2026 heads = _(" (%+d heads)") % (newheads - oldheads)
2027
2027
2028 self.ui.status(_("added %d changesets"
2028 self.ui.status(_("added %d changesets"
2029 " with %d changes to %d files%s\n")
2029 " with %d changes to %d files%s\n")
2030 % (changesets, revisions, files, heads))
2030 % (changesets, revisions, files, heads))
2031
2031
2032 if changesets > 0:
2032 if changesets > 0:
2033 self.hook('pretxnchangegroup', throw=True,
2033 self.hook('pretxnchangegroup', throw=True,
2034 node=hex(self.changelog.node(cor+1)), source=srctype,
2034 node=hex(self.changelog.node(cor+1)), source=srctype,
2035 url=url)
2035 url=url)
2036
2036
2037 tr.close()
2037 tr.close()
2038 finally:
2038 finally:
2039 del tr
2039 del tr
2040
2040
2041 if changesets > 0:
2041 if changesets > 0:
2042 # forcefully update the on-disk branch cache
2042 # forcefully update the on-disk branch cache
2043 self.ui.debug(_("updating the branch cache\n"))
2043 self.ui.debug(_("updating the branch cache\n"))
2044 self.branchtags()
2044 self.branchtags()
2045 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
2045 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
2046 source=srctype, url=url)
2046 source=srctype, url=url)
2047
2047
2048 for i in xrange(cor + 1, cnr + 1):
2048 for i in xrange(cor + 1, cnr + 1):
2049 self.hook("incoming", node=hex(self.changelog.node(i)),
2049 self.hook("incoming", node=hex(self.changelog.node(i)),
2050 source=srctype, url=url)
2050 source=srctype, url=url)
2051
2051
2052 # never return 0 here:
2052 # never return 0 here:
2053 if newheads < oldheads:
2053 if newheads < oldheads:
2054 return newheads - oldheads - 1
2054 return newheads - oldheads - 1
2055 else:
2055 else:
2056 return newheads - oldheads + 1
2056 return newheads - oldheads + 1
2057
2057
2058
2058
2059 def stream_in(self, remote):
2059 def stream_in(self, remote):
2060 fp = remote.stream_out()
2060 fp = remote.stream_out()
2061 l = fp.readline()
2061 l = fp.readline()
2062 try:
2062 try:
2063 resp = int(l)
2063 resp = int(l)
2064 except ValueError:
2064 except ValueError:
2065 raise util.UnexpectedOutput(
2065 raise util.UnexpectedOutput(
2066 _('Unexpected response from remote server:'), l)
2066 _('Unexpected response from remote server:'), l)
2067 if resp == 1:
2067 if resp == 1:
2068 raise util.Abort(_('operation forbidden by server'))
2068 raise util.Abort(_('operation forbidden by server'))
2069 elif resp == 2:
2069 elif resp == 2:
2070 raise util.Abort(_('locking the remote repository failed'))
2070 raise util.Abort(_('locking the remote repository failed'))
2071 elif resp != 0:
2071 elif resp != 0:
2072 raise util.Abort(_('the server sent an unknown error code'))
2072 raise util.Abort(_('the server sent an unknown error code'))
2073 self.ui.status(_('streaming all changes\n'))
2073 self.ui.status(_('streaming all changes\n'))
2074 l = fp.readline()
2074 l = fp.readline()
2075 try:
2075 try:
2076 total_files, total_bytes = map(int, l.split(' ', 1))
2076 total_files, total_bytes = map(int, l.split(' ', 1))
2077 except (ValueError, TypeError):
2077 except (ValueError, TypeError):
2078 raise util.UnexpectedOutput(
2078 raise util.UnexpectedOutput(
2079 _('Unexpected response from remote server:'), l)
2079 _('Unexpected response from remote server:'), l)
2080 self.ui.status(_('%d files to transfer, %s of data\n') %
2080 self.ui.status(_('%d files to transfer, %s of data\n') %
2081 (total_files, util.bytecount(total_bytes)))
2081 (total_files, util.bytecount(total_bytes)))
2082 start = time.time()
2082 start = time.time()
2083 for i in xrange(total_files):
2083 for i in xrange(total_files):
2084 # XXX doesn't support '\n' or '\r' in filenames
2084 # XXX doesn't support '\n' or '\r' in filenames
2085 l = fp.readline()
2085 l = fp.readline()
2086 try:
2086 try:
2087 name, size = l.split('\0', 1)
2087 name, size = l.split('\0', 1)
2088 size = int(size)
2088 size = int(size)
2089 except ValueError, TypeError:
2089 except ValueError, TypeError:
2090 raise util.UnexpectedOutput(
2090 raise util.UnexpectedOutput(
2091 _('Unexpected response from remote server:'), l)
2091 _('Unexpected response from remote server:'), l)
2092 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
2092 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
2093 ofp = self.sopener(name, 'w')
2093 ofp = self.sopener(name, 'w')
2094 for chunk in util.filechunkiter(fp, limit=size):
2094 for chunk in util.filechunkiter(fp, limit=size):
2095 ofp.write(chunk)
2095 ofp.write(chunk)
2096 ofp.close()
2096 ofp.close()
2097 elapsed = time.time() - start
2097 elapsed = time.time() - start
2098 if elapsed <= 0:
2098 if elapsed <= 0:
2099 elapsed = 0.001
2099 elapsed = 0.001
2100 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2100 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2101 (util.bytecount(total_bytes), elapsed,
2101 (util.bytecount(total_bytes), elapsed,
2102 util.bytecount(total_bytes / elapsed)))
2102 util.bytecount(total_bytes / elapsed)))
2103 self.invalidate()
2103 self.invalidate()
2104 return len(self.heads()) + 1
2104 return len(self.heads()) + 1
2105
2105
2106 def clone(self, remote, heads=[], stream=False):
2106 def clone(self, remote, heads=[], stream=False):
2107 '''clone remote repository.
2107 '''clone remote repository.
2108
2108
2109 keyword arguments:
2109 keyword arguments:
2110 heads: list of revs to clone (forces use of pull)
2110 heads: list of revs to clone (forces use of pull)
2111 stream: use streaming clone if possible'''
2111 stream: use streaming clone if possible'''
2112
2112
2113 # now, all clients that can request uncompressed clones can
2113 # now, all clients that can request uncompressed clones can
2114 # read repo formats supported by all servers that can serve
2114 # read repo formats supported by all servers that can serve
2115 # them.
2115 # them.
2116
2116
2117 # if revlog format changes, client will have to check version
2117 # if revlog format changes, client will have to check version
2118 # and format flags on "stream" capability, and use
2118 # and format flags on "stream" capability, and use
2119 # uncompressed only if compatible.
2119 # uncompressed only if compatible.
2120
2120
2121 if stream and not heads and remote.capable('stream'):
2121 if stream and not heads and remote.capable('stream'):
2122 return self.stream_in(remote)
2122 return self.stream_in(remote)
2123 return self.pull(remote, heads)
2123 return self.pull(remote, heads)
2124
2124
2125 # used to avoid circular references so destructors work
2125 # used to avoid circular references so destructors work
2126 def aftertrans(files):
2126 def aftertrans(files):
2127 renamefiles = [tuple(t) for t in files]
2127 renamefiles = [tuple(t) for t in files]
2128 def a():
2128 def a():
2129 for src, dest in renamefiles:
2129 for src, dest in renamefiles:
2130 util.rename(src, dest)
2130 util.rename(src, dest)
2131 return a
2131 return a
2132
2132
2133 def instance(ui, path, create):
2133 def instance(ui, path, create):
2134 return localrepository(ui, util.drop_scheme('file', path), create)
2134 return localrepository(ui, util.drop_scheme('file', path), create)
2135
2135
2136 def islocal(path):
2136 def islocal(path):
2137 return True
2137 return True
General Comments 0
You need to be logged in to leave comments. Login now