##// END OF EJS Templates
Read paths specified in .hg/hgrc relative to repo root, otherwise to home dir.
Thomas Arendsen Hein -
r1893:6569651a default
parent child Browse files
Show More
@@ -1,2974 +1,2974 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 demandload(globals(), "fancyopts ui hg util lock revlog")
12 demandload(globals(), "fancyopts ui hg util lock revlog")
13 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
13 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
14 demandload(globals(), "errno socket version struct atexit sets bz2")
14 demandload(globals(), "errno socket version struct atexit sets bz2")
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 filterfiles(filters, files):
21 def filterfiles(filters, files):
22 l = [x for x in files if x in filters]
22 l = [x for x in files if x in filters]
23
23
24 for t in filters:
24 for t in filters:
25 if t and t[-1] != "/":
25 if t and t[-1] != "/":
26 t += "/"
26 t += "/"
27 l += [x for x in files if x.startswith(t)]
27 l += [x for x in files if x.startswith(t)]
28 return l
28 return l
29
29
30 def relpath(repo, args):
30 def relpath(repo, args):
31 cwd = repo.getcwd()
31 cwd = repo.getcwd()
32 if cwd:
32 if cwd:
33 return [util.normpath(os.path.join(cwd, x)) for x in args]
33 return [util.normpath(os.path.join(cwd, x)) for x in args]
34 return args
34 return args
35
35
36 def matchpats(repo, pats=[], opts={}, head=''):
36 def matchpats(repo, pats=[], opts={}, head=''):
37 cwd = repo.getcwd()
37 cwd = repo.getcwd()
38 if not pats and cwd:
38 if not pats and cwd:
39 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
39 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
40 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
40 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
41 cwd = ''
41 cwd = ''
42 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
42 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
43 opts.get('exclude'), head)
43 opts.get('exclude'), head)
44
44
45 def makewalk(repo, pats, opts, node=None, head=''):
45 def makewalk(repo, pats, opts, node=None, head=''):
46 files, matchfn, anypats = matchpats(repo, pats, opts, head)
46 files, matchfn, anypats = matchpats(repo, pats, opts, head)
47 exact = dict(zip(files, files))
47 exact = dict(zip(files, files))
48 def walk():
48 def walk():
49 for src, fn in repo.walk(node=node, files=files, match=matchfn):
49 for src, fn in repo.walk(node=node, files=files, match=matchfn):
50 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
50 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
51 return files, matchfn, walk()
51 return files, matchfn, walk()
52
52
53 def walk(repo, pats, opts, node=None, head=''):
53 def walk(repo, pats, opts, node=None, head=''):
54 files, matchfn, results = makewalk(repo, pats, opts, node, head)
54 files, matchfn, results = makewalk(repo, pats, opts, node, head)
55 for r in results:
55 for r in results:
56 yield r
56 yield r
57
57
58 def walkchangerevs(ui, repo, pats, opts):
58 def walkchangerevs(ui, repo, pats, opts):
59 '''Iterate over files and the revs they changed in.
59 '''Iterate over files and the revs they changed in.
60
60
61 Callers most commonly need to iterate backwards over the history
61 Callers most commonly need to iterate backwards over the history
62 it is interested in. Doing so has awful (quadratic-looking)
62 it is interested in. Doing so has awful (quadratic-looking)
63 performance, so we use iterators in a "windowed" way.
63 performance, so we use iterators in a "windowed" way.
64
64
65 We walk a window of revisions in the desired order. Within the
65 We walk a window of revisions in the desired order. Within the
66 window, we first walk forwards to gather data, then in the desired
66 window, we first walk forwards to gather data, then in the desired
67 order (usually backwards) to display it.
67 order (usually backwards) to display it.
68
68
69 This function returns an (iterator, getchange, matchfn) tuple. The
69 This function returns an (iterator, getchange, matchfn) tuple. The
70 getchange function returns the changelog entry for a numeric
70 getchange function returns the changelog entry for a numeric
71 revision. The iterator yields 3-tuples. They will be of one of
71 revision. The iterator yields 3-tuples. They will be of one of
72 the following forms:
72 the following forms:
73
73
74 "window", incrementing, lastrev: stepping through a window,
74 "window", incrementing, lastrev: stepping through a window,
75 positive if walking forwards through revs, last rev in the
75 positive if walking forwards through revs, last rev in the
76 sequence iterated over - use to reset state for the current window
76 sequence iterated over - use to reset state for the current window
77
77
78 "add", rev, fns: out-of-order traversal of the given file names
78 "add", rev, fns: out-of-order traversal of the given file names
79 fns, which changed during revision rev - use to gather data for
79 fns, which changed during revision rev - use to gather data for
80 possible display
80 possible display
81
81
82 "iter", rev, None: in-order traversal of the revs earlier iterated
82 "iter", rev, None: in-order traversal of the revs earlier iterated
83 over with "add" - use to display data'''
83 over with "add" - use to display data'''
84
84
85 def increasing_windows(start, end, windowsize=8, sizelimit=512):
85 def increasing_windows(start, end, windowsize=8, sizelimit=512):
86 if start < end:
86 if start < end:
87 while start < end:
87 while start < end:
88 yield start, min(windowsize, end-start)
88 yield start, min(windowsize, end-start)
89 start += windowsize
89 start += windowsize
90 if windowsize < sizelimit:
90 if windowsize < sizelimit:
91 windowsize *= 2
91 windowsize *= 2
92 else:
92 else:
93 while start > end:
93 while start > end:
94 yield start, min(windowsize, start-end-1)
94 yield start, min(windowsize, start-end-1)
95 start -= windowsize
95 start -= windowsize
96 if windowsize < sizelimit:
96 if windowsize < sizelimit:
97 windowsize *= 2
97 windowsize *= 2
98
98
99
99
100 files, matchfn, anypats = matchpats(repo, pats, opts)
100 files, matchfn, anypats = matchpats(repo, pats, opts)
101
101
102 if repo.changelog.count() == 0:
102 if repo.changelog.count() == 0:
103 return [], False, matchfn
103 return [], False, matchfn
104
104
105 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
105 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
106 wanted = {}
106 wanted = {}
107 slowpath = anypats
107 slowpath = anypats
108 fncache = {}
108 fncache = {}
109
109
110 chcache = {}
110 chcache = {}
111 def getchange(rev):
111 def getchange(rev):
112 ch = chcache.get(rev)
112 ch = chcache.get(rev)
113 if ch is None:
113 if ch is None:
114 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
114 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
115 return ch
115 return ch
116
116
117 if not slowpath and not files:
117 if not slowpath and not files:
118 # No files, no patterns. Display all revs.
118 # No files, no patterns. Display all revs.
119 wanted = dict(zip(revs, revs))
119 wanted = dict(zip(revs, revs))
120 if not slowpath:
120 if not slowpath:
121 # Only files, no patterns. Check the history of each file.
121 # Only files, no patterns. Check the history of each file.
122 def filerevgen(filelog):
122 def filerevgen(filelog):
123 for i, window in increasing_windows(filelog.count()-1, -1):
123 for i, window in increasing_windows(filelog.count()-1, -1):
124 revs = []
124 revs = []
125 for j in xrange(i - window, i + 1):
125 for j in xrange(i - window, i + 1):
126 revs.append(filelog.linkrev(filelog.node(j)))
126 revs.append(filelog.linkrev(filelog.node(j)))
127 revs.reverse()
127 revs.reverse()
128 for rev in revs:
128 for rev in revs:
129 yield rev
129 yield rev
130
130
131 minrev, maxrev = min(revs), max(revs)
131 minrev, maxrev = min(revs), max(revs)
132 for file_ in files:
132 for file_ in files:
133 filelog = repo.file(file_)
133 filelog = repo.file(file_)
134 # A zero count may be a directory or deleted file, so
134 # A zero count may be a directory or deleted file, so
135 # try to find matching entries on the slow path.
135 # try to find matching entries on the slow path.
136 if filelog.count() == 0:
136 if filelog.count() == 0:
137 slowpath = True
137 slowpath = True
138 break
138 break
139 for rev in filerevgen(filelog):
139 for rev in filerevgen(filelog):
140 if rev <= maxrev:
140 if rev <= maxrev:
141 if rev < minrev:
141 if rev < minrev:
142 break
142 break
143 fncache.setdefault(rev, [])
143 fncache.setdefault(rev, [])
144 fncache[rev].append(file_)
144 fncache[rev].append(file_)
145 wanted[rev] = 1
145 wanted[rev] = 1
146 if slowpath:
146 if slowpath:
147 # The slow path checks files modified in every changeset.
147 # The slow path checks files modified in every changeset.
148 def changerevgen():
148 def changerevgen():
149 for i, window in increasing_windows(repo.changelog.count()-1, -1):
149 for i, window in increasing_windows(repo.changelog.count()-1, -1):
150 for j in xrange(i - window, i + 1):
150 for j in xrange(i - window, i + 1):
151 yield j, getchange(j)[3]
151 yield j, getchange(j)[3]
152
152
153 for rev, changefiles in changerevgen():
153 for rev, changefiles in changerevgen():
154 matches = filter(matchfn, changefiles)
154 matches = filter(matchfn, changefiles)
155 if matches:
155 if matches:
156 fncache[rev] = matches
156 fncache[rev] = matches
157 wanted[rev] = 1
157 wanted[rev] = 1
158
158
159 def iterate():
159 def iterate():
160 for i, window in increasing_windows(0, len(revs)):
160 for i, window in increasing_windows(0, len(revs)):
161 yield 'window', revs[0] < revs[-1], revs[-1]
161 yield 'window', revs[0] < revs[-1], revs[-1]
162 nrevs = [rev for rev in revs[i:i+window]
162 nrevs = [rev for rev in revs[i:i+window]
163 if rev in wanted]
163 if rev in wanted]
164 srevs = list(nrevs)
164 srevs = list(nrevs)
165 srevs.sort()
165 srevs.sort()
166 for rev in srevs:
166 for rev in srevs:
167 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
167 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
168 yield 'add', rev, fns
168 yield 'add', rev, fns
169 for rev in nrevs:
169 for rev in nrevs:
170 yield 'iter', rev, None
170 yield 'iter', rev, None
171 return iterate(), getchange, matchfn
171 return iterate(), getchange, matchfn
172
172
173 revrangesep = ':'
173 revrangesep = ':'
174
174
175 def revrange(ui, repo, revs, revlog=None):
175 def revrange(ui, repo, revs, revlog=None):
176 """Yield revision as strings from a list of revision specifications."""
176 """Yield revision as strings from a list of revision specifications."""
177 if revlog is None:
177 if revlog is None:
178 revlog = repo.changelog
178 revlog = repo.changelog
179 revcount = revlog.count()
179 revcount = revlog.count()
180 def fix(val, defval):
180 def fix(val, defval):
181 if not val:
181 if not val:
182 return defval
182 return defval
183 try:
183 try:
184 num = int(val)
184 num = int(val)
185 if str(num) != val:
185 if str(num) != val:
186 raise ValueError
186 raise ValueError
187 if num < 0:
187 if num < 0:
188 num += revcount
188 num += revcount
189 if num < 0:
189 if num < 0:
190 num = 0
190 num = 0
191 elif num >= revcount:
191 elif num >= revcount:
192 raise ValueError
192 raise ValueError
193 except ValueError:
193 except ValueError:
194 try:
194 try:
195 num = repo.changelog.rev(repo.lookup(val))
195 num = repo.changelog.rev(repo.lookup(val))
196 except KeyError:
196 except KeyError:
197 try:
197 try:
198 num = revlog.rev(revlog.lookup(val))
198 num = revlog.rev(revlog.lookup(val))
199 except KeyError:
199 except KeyError:
200 raise util.Abort(_('invalid revision identifier %s'), val)
200 raise util.Abort(_('invalid revision identifier %s'), val)
201 return num
201 return num
202 seen = {}
202 seen = {}
203 for spec in revs:
203 for spec in revs:
204 if spec.find(revrangesep) >= 0:
204 if spec.find(revrangesep) >= 0:
205 start, end = spec.split(revrangesep, 1)
205 start, end = spec.split(revrangesep, 1)
206 start = fix(start, 0)
206 start = fix(start, 0)
207 end = fix(end, revcount - 1)
207 end = fix(end, revcount - 1)
208 step = start > end and -1 or 1
208 step = start > end and -1 or 1
209 for rev in xrange(start, end+step, step):
209 for rev in xrange(start, end+step, step):
210 if rev in seen:
210 if rev in seen:
211 continue
211 continue
212 seen[rev] = 1
212 seen[rev] = 1
213 yield str(rev)
213 yield str(rev)
214 else:
214 else:
215 rev = fix(spec, None)
215 rev = fix(spec, None)
216 if rev in seen:
216 if rev in seen:
217 continue
217 continue
218 seen[rev] = 1
218 seen[rev] = 1
219 yield str(rev)
219 yield str(rev)
220
220
221 def make_filename(repo, r, pat, node=None,
221 def make_filename(repo, r, pat, node=None,
222 total=None, seqno=None, revwidth=None, pathname=None):
222 total=None, seqno=None, revwidth=None, pathname=None):
223 node_expander = {
223 node_expander = {
224 'H': lambda: hex(node),
224 'H': lambda: hex(node),
225 'R': lambda: str(r.rev(node)),
225 'R': lambda: str(r.rev(node)),
226 'h': lambda: short(node),
226 'h': lambda: short(node),
227 }
227 }
228 expander = {
228 expander = {
229 '%': lambda: '%',
229 '%': lambda: '%',
230 'b': lambda: os.path.basename(repo.root),
230 'b': lambda: os.path.basename(repo.root),
231 }
231 }
232
232
233 try:
233 try:
234 if node:
234 if node:
235 expander.update(node_expander)
235 expander.update(node_expander)
236 if node and revwidth is not None:
236 if node and revwidth is not None:
237 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
237 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
238 if total is not None:
238 if total is not None:
239 expander['N'] = lambda: str(total)
239 expander['N'] = lambda: str(total)
240 if seqno is not None:
240 if seqno is not None:
241 expander['n'] = lambda: str(seqno)
241 expander['n'] = lambda: str(seqno)
242 if total is not None and seqno is not None:
242 if total is not None and seqno is not None:
243 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
243 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
244 if pathname is not None:
244 if pathname is not None:
245 expander['s'] = lambda: os.path.basename(pathname)
245 expander['s'] = lambda: os.path.basename(pathname)
246 expander['d'] = lambda: os.path.dirname(pathname) or '.'
246 expander['d'] = lambda: os.path.dirname(pathname) or '.'
247 expander['p'] = lambda: pathname
247 expander['p'] = lambda: pathname
248
248
249 newname = []
249 newname = []
250 patlen = len(pat)
250 patlen = len(pat)
251 i = 0
251 i = 0
252 while i < patlen:
252 while i < patlen:
253 c = pat[i]
253 c = pat[i]
254 if c == '%':
254 if c == '%':
255 i += 1
255 i += 1
256 c = pat[i]
256 c = pat[i]
257 c = expander[c]()
257 c = expander[c]()
258 newname.append(c)
258 newname.append(c)
259 i += 1
259 i += 1
260 return ''.join(newname)
260 return ''.join(newname)
261 except KeyError, inst:
261 except KeyError, inst:
262 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
262 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
263 inst.args[0])
263 inst.args[0])
264
264
265 def make_file(repo, r, pat, node=None,
265 def make_file(repo, r, pat, node=None,
266 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
266 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
267 if not pat or pat == '-':
267 if not pat or pat == '-':
268 return 'w' in mode and sys.stdout or sys.stdin
268 return 'w' in mode and sys.stdout or sys.stdin
269 if hasattr(pat, 'write') and 'w' in mode:
269 if hasattr(pat, 'write') and 'w' in mode:
270 return pat
270 return pat
271 if hasattr(pat, 'read') and 'r' in mode:
271 if hasattr(pat, 'read') and 'r' in mode:
272 return pat
272 return pat
273 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
273 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
274 pathname),
274 pathname),
275 mode)
275 mode)
276
276
277 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
277 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
278 changes=None, text=False, opts={}):
278 changes=None, text=False, opts={}):
279 if not node1:
279 if not node1:
280 node1 = repo.dirstate.parents()[0]
280 node1 = repo.dirstate.parents()[0]
281 # reading the data for node1 early allows it to play nicely
281 # reading the data for node1 early allows it to play nicely
282 # with repo.changes and the revlog cache.
282 # with repo.changes and the revlog cache.
283 change = repo.changelog.read(node1)
283 change = repo.changelog.read(node1)
284 mmap = repo.manifest.read(change[0])
284 mmap = repo.manifest.read(change[0])
285 date1 = util.datestr(change[2])
285 date1 = util.datestr(change[2])
286
286
287 if not changes:
287 if not changes:
288 changes = repo.changes(node1, node2, files, match=match)
288 changes = repo.changes(node1, node2, files, match=match)
289 modified, added, removed, deleted, unknown = changes
289 modified, added, removed, deleted, unknown = changes
290 if files:
290 if files:
291 modified, added, removed = map(lambda x: filterfiles(files, x),
291 modified, added, removed = map(lambda x: filterfiles(files, x),
292 (modified, added, removed))
292 (modified, added, removed))
293
293
294 if not modified and not added and not removed:
294 if not modified and not added and not removed:
295 return
295 return
296
296
297 if node2:
297 if node2:
298 change = repo.changelog.read(node2)
298 change = repo.changelog.read(node2)
299 mmap2 = repo.manifest.read(change[0])
299 mmap2 = repo.manifest.read(change[0])
300 date2 = util.datestr(change[2])
300 date2 = util.datestr(change[2])
301 def read(f):
301 def read(f):
302 return repo.file(f).read(mmap2[f])
302 return repo.file(f).read(mmap2[f])
303 else:
303 else:
304 date2 = util.datestr()
304 date2 = util.datestr()
305 def read(f):
305 def read(f):
306 return repo.wread(f)
306 return repo.wread(f)
307
307
308 if ui.quiet:
308 if ui.quiet:
309 r = None
309 r = None
310 else:
310 else:
311 hexfunc = ui.verbose and hex or short
311 hexfunc = ui.verbose and hex or short
312 r = [hexfunc(node) for node in [node1, node2] if node]
312 r = [hexfunc(node) for node in [node1, node2] if node]
313
313
314 diffopts = ui.diffopts()
314 diffopts = ui.diffopts()
315 showfunc = opts.get('show_function') or diffopts['showfunc']
315 showfunc = opts.get('show_function') or diffopts['showfunc']
316 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
316 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
317 for f in modified:
317 for f in modified:
318 to = None
318 to = None
319 if f in mmap:
319 if f in mmap:
320 to = repo.file(f).read(mmap[f])
320 to = repo.file(f).read(mmap[f])
321 tn = read(f)
321 tn = read(f)
322 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
322 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
323 showfunc=showfunc, ignorews=ignorews))
323 showfunc=showfunc, ignorews=ignorews))
324 for f in added:
324 for f in added:
325 to = None
325 to = None
326 tn = read(f)
326 tn = read(f)
327 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
327 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
328 showfunc=showfunc, ignorews=ignorews))
328 showfunc=showfunc, ignorews=ignorews))
329 for f in removed:
329 for f in removed:
330 to = repo.file(f).read(mmap[f])
330 to = repo.file(f).read(mmap[f])
331 tn = None
331 tn = None
332 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
332 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
333 showfunc=showfunc, ignorews=ignorews))
333 showfunc=showfunc, ignorews=ignorews))
334
334
335 def trimuser(ui, name, rev, revcache):
335 def trimuser(ui, name, rev, revcache):
336 """trim the name of the user who committed a change"""
336 """trim the name of the user who committed a change"""
337 user = revcache.get(rev)
337 user = revcache.get(rev)
338 if user is None:
338 if user is None:
339 user = revcache[rev] = ui.shortuser(name)
339 user = revcache[rev] = ui.shortuser(name)
340 return user
340 return user
341
341
342 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
342 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
343 """show a single changeset or file revision"""
343 """show a single changeset or file revision"""
344 log = repo.changelog
344 log = repo.changelog
345 if changenode is None:
345 if changenode is None:
346 changenode = log.node(rev)
346 changenode = log.node(rev)
347 elif not rev:
347 elif not rev:
348 rev = log.rev(changenode)
348 rev = log.rev(changenode)
349
349
350 if ui.quiet:
350 if ui.quiet:
351 ui.write("%d:%s\n" % (rev, short(changenode)))
351 ui.write("%d:%s\n" % (rev, short(changenode)))
352 return
352 return
353
353
354 changes = log.read(changenode)
354 changes = log.read(changenode)
355 date = util.datestr(changes[2])
355 date = util.datestr(changes[2])
356
356
357 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
357 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
358 for p in log.parents(changenode)
358 for p in log.parents(changenode)
359 if ui.debugflag or p != nullid]
359 if ui.debugflag or p != nullid]
360 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
360 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
361 parents = []
361 parents = []
362
362
363 if ui.verbose:
363 if ui.verbose:
364 ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
364 ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
365 else:
365 else:
366 ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
366 ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
367
367
368 for tag in repo.nodetags(changenode):
368 for tag in repo.nodetags(changenode):
369 ui.status(_("tag: %s\n") % tag)
369 ui.status(_("tag: %s\n") % tag)
370 for parent in parents:
370 for parent in parents:
371 ui.write(_("parent: %d:%s\n") % parent)
371 ui.write(_("parent: %d:%s\n") % parent)
372
372
373 if brinfo and changenode in brinfo:
373 if brinfo and changenode in brinfo:
374 br = brinfo[changenode]
374 br = brinfo[changenode]
375 ui.write(_("branch: %s\n") % " ".join(br))
375 ui.write(_("branch: %s\n") % " ".join(br))
376
376
377 ui.debug(_("manifest: %d:%s\n") % (repo.manifest.rev(changes[0]),
377 ui.debug(_("manifest: %d:%s\n") % (repo.manifest.rev(changes[0]),
378 hex(changes[0])))
378 hex(changes[0])))
379 ui.status(_("user: %s\n") % changes[1])
379 ui.status(_("user: %s\n") % changes[1])
380 ui.status(_("date: %s\n") % date)
380 ui.status(_("date: %s\n") % date)
381
381
382 if ui.debugflag:
382 if ui.debugflag:
383 files = repo.changes(log.parents(changenode)[0], changenode)
383 files = repo.changes(log.parents(changenode)[0], changenode)
384 for key, value in zip([_("files:"), _("files+:"), _("files-:")], files):
384 for key, value in zip([_("files:"), _("files+:"), _("files-:")], files):
385 if value:
385 if value:
386 ui.note("%-12s %s\n" % (key, " ".join(value)))
386 ui.note("%-12s %s\n" % (key, " ".join(value)))
387 else:
387 else:
388 ui.note(_("files: %s\n") % " ".join(changes[3]))
388 ui.note(_("files: %s\n") % " ".join(changes[3]))
389
389
390 description = changes[4].strip()
390 description = changes[4].strip()
391 if description:
391 if description:
392 if ui.verbose:
392 if ui.verbose:
393 ui.status(_("description:\n"))
393 ui.status(_("description:\n"))
394 ui.status(description)
394 ui.status(description)
395 ui.status("\n\n")
395 ui.status("\n\n")
396 else:
396 else:
397 ui.status(_("summary: %s\n") % description.splitlines()[0])
397 ui.status(_("summary: %s\n") % description.splitlines()[0])
398 ui.status("\n")
398 ui.status("\n")
399
399
400 def show_version(ui):
400 def show_version(ui):
401 """output version and copyright information"""
401 """output version and copyright information"""
402 ui.write(_("Mercurial Distributed SCM (version %s)\n")
402 ui.write(_("Mercurial Distributed SCM (version %s)\n")
403 % version.get_version())
403 % version.get_version())
404 ui.status(_(
404 ui.status(_(
405 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
405 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
406 "This is free software; see the source for copying conditions. "
406 "This is free software; see the source for copying conditions. "
407 "There is NO\nwarranty; "
407 "There is NO\nwarranty; "
408 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
408 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
409 ))
409 ))
410
410
411 def help_(ui, cmd=None, with_version=False):
411 def help_(ui, cmd=None, with_version=False):
412 """show help for a given command or all commands"""
412 """show help for a given command or all commands"""
413 option_lists = []
413 option_lists = []
414 if cmd and cmd != 'shortlist':
414 if cmd and cmd != 'shortlist':
415 if with_version:
415 if with_version:
416 show_version(ui)
416 show_version(ui)
417 ui.write('\n')
417 ui.write('\n')
418 aliases, i = find(cmd)
418 aliases, i = find(cmd)
419 # synopsis
419 # synopsis
420 ui.write("%s\n\n" % i[2])
420 ui.write("%s\n\n" % i[2])
421
421
422 # description
422 # description
423 doc = i[0].__doc__
423 doc = i[0].__doc__
424 if not doc:
424 if not doc:
425 doc = _("(No help text available)")
425 doc = _("(No help text available)")
426 if ui.quiet:
426 if ui.quiet:
427 doc = doc.splitlines(0)[0]
427 doc = doc.splitlines(0)[0]
428 ui.write("%s\n" % doc.rstrip())
428 ui.write("%s\n" % doc.rstrip())
429
429
430 if not ui.quiet:
430 if not ui.quiet:
431 # aliases
431 # aliases
432 if len(aliases) > 1:
432 if len(aliases) > 1:
433 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
433 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
434
434
435 # options
435 # options
436 if i[1]:
436 if i[1]:
437 option_lists.append(("options", i[1]))
437 option_lists.append(("options", i[1]))
438
438
439 else:
439 else:
440 # program name
440 # program name
441 if ui.verbose or with_version:
441 if ui.verbose or with_version:
442 show_version(ui)
442 show_version(ui)
443 else:
443 else:
444 ui.status(_("Mercurial Distributed SCM\n"))
444 ui.status(_("Mercurial Distributed SCM\n"))
445 ui.status('\n')
445 ui.status('\n')
446
446
447 # list of commands
447 # list of commands
448 if cmd == "shortlist":
448 if cmd == "shortlist":
449 ui.status(_('basic commands (use "hg help" '
449 ui.status(_('basic commands (use "hg help" '
450 'for the full list or option "-v" for details):\n\n'))
450 'for the full list or option "-v" for details):\n\n'))
451 elif ui.verbose:
451 elif ui.verbose:
452 ui.status(_('list of commands:\n\n'))
452 ui.status(_('list of commands:\n\n'))
453 else:
453 else:
454 ui.status(_('list of commands (use "hg help -v" '
454 ui.status(_('list of commands (use "hg help -v" '
455 'to show aliases and global options):\n\n'))
455 'to show aliases and global options):\n\n'))
456
456
457 h = {}
457 h = {}
458 cmds = {}
458 cmds = {}
459 for c, e in table.items():
459 for c, e in table.items():
460 f = c.split("|")[0]
460 f = c.split("|")[0]
461 if cmd == "shortlist" and not f.startswith("^"):
461 if cmd == "shortlist" and not f.startswith("^"):
462 continue
462 continue
463 f = f.lstrip("^")
463 f = f.lstrip("^")
464 if not ui.debugflag and f.startswith("debug"):
464 if not ui.debugflag and f.startswith("debug"):
465 continue
465 continue
466 doc = e[0].__doc__
466 doc = e[0].__doc__
467 if not doc:
467 if not doc:
468 doc = _("(No help text available)")
468 doc = _("(No help text available)")
469 h[f] = doc.splitlines(0)[0].rstrip()
469 h[f] = doc.splitlines(0)[0].rstrip()
470 cmds[f] = c.lstrip("^")
470 cmds[f] = c.lstrip("^")
471
471
472 fns = h.keys()
472 fns = h.keys()
473 fns.sort()
473 fns.sort()
474 m = max(map(len, fns))
474 m = max(map(len, fns))
475 for f in fns:
475 for f in fns:
476 if ui.verbose:
476 if ui.verbose:
477 commands = cmds[f].replace("|",", ")
477 commands = cmds[f].replace("|",", ")
478 ui.write(" %s:\n %s\n"%(commands, h[f]))
478 ui.write(" %s:\n %s\n"%(commands, h[f]))
479 else:
479 else:
480 ui.write(' %-*s %s\n' % (m, f, h[f]))
480 ui.write(' %-*s %s\n' % (m, f, h[f]))
481
481
482 # global options
482 # global options
483 if ui.verbose:
483 if ui.verbose:
484 option_lists.append(("global options", globalopts))
484 option_lists.append(("global options", globalopts))
485
485
486 # list all option lists
486 # list all option lists
487 opt_output = []
487 opt_output = []
488 for title, options in option_lists:
488 for title, options in option_lists:
489 opt_output.append(("\n%s:\n" % title, None))
489 opt_output.append(("\n%s:\n" % title, None))
490 for shortopt, longopt, default, desc in options:
490 for shortopt, longopt, default, desc in options:
491 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
491 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
492 longopt and " --%s" % longopt),
492 longopt and " --%s" % longopt),
493 "%s%s" % (desc,
493 "%s%s" % (desc,
494 default
494 default
495 and _(" (default: %s)") % default
495 and _(" (default: %s)") % default
496 or "")))
496 or "")))
497
497
498 if opt_output:
498 if opt_output:
499 opts_len = max([len(line[0]) for line in opt_output if line[1]])
499 opts_len = max([len(line[0]) for line in opt_output if line[1]])
500 for first, second in opt_output:
500 for first, second in opt_output:
501 if second:
501 if second:
502 ui.write(" %-*s %s\n" % (opts_len, first, second))
502 ui.write(" %-*s %s\n" % (opts_len, first, second))
503 else:
503 else:
504 ui.write("%s\n" % first)
504 ui.write("%s\n" % first)
505
505
506 # Commands start here, listed alphabetically
506 # Commands start here, listed alphabetically
507
507
508 def add(ui, repo, *pats, **opts):
508 def add(ui, repo, *pats, **opts):
509 """add the specified files on the next commit
509 """add the specified files on the next commit
510
510
511 Schedule files to be version controlled and added to the repository.
511 Schedule files to be version controlled and added to the repository.
512
512
513 The files will be added to the repository at the next commit.
513 The files will be added to the repository at the next commit.
514
514
515 If no names are given, add all files in the repository.
515 If no names are given, add all files in the repository.
516 """
516 """
517
517
518 names = []
518 names = []
519 for src, abs, rel, exact in walk(repo, pats, opts):
519 for src, abs, rel, exact in walk(repo, pats, opts):
520 if exact:
520 if exact:
521 if ui.verbose:
521 if ui.verbose:
522 ui.status(_('adding %s\n') % rel)
522 ui.status(_('adding %s\n') % rel)
523 names.append(abs)
523 names.append(abs)
524 elif repo.dirstate.state(abs) == '?':
524 elif repo.dirstate.state(abs) == '?':
525 ui.status(_('adding %s\n') % rel)
525 ui.status(_('adding %s\n') % rel)
526 names.append(abs)
526 names.append(abs)
527 repo.add(names)
527 repo.add(names)
528
528
529 def addremove(ui, repo, *pats, **opts):
529 def addremove(ui, repo, *pats, **opts):
530 """add all new files, delete all missing files
530 """add all new files, delete all missing files
531
531
532 Add all new files and remove all missing files from the repository.
532 Add all new files and remove all missing files from the repository.
533
533
534 New files are ignored if they match any of the patterns in .hgignore. As
534 New files are ignored if they match any of the patterns in .hgignore. As
535 with add, these changes take effect at the next commit.
535 with add, these changes take effect at the next commit.
536 """
536 """
537 return addremove_lock(ui, repo, pats, opts)
537 return addremove_lock(ui, repo, pats, opts)
538
538
539 def addremove_lock(ui, repo, pats, opts, wlock=None):
539 def addremove_lock(ui, repo, pats, opts, wlock=None):
540 add, remove = [], []
540 add, remove = [], []
541 for src, abs, rel, exact in walk(repo, pats, opts):
541 for src, abs, rel, exact in walk(repo, pats, opts):
542 if src == 'f' and repo.dirstate.state(abs) == '?':
542 if src == 'f' and repo.dirstate.state(abs) == '?':
543 add.append(abs)
543 add.append(abs)
544 if ui.verbose or not exact:
544 if ui.verbose or not exact:
545 ui.status(_('adding %s\n') % ((pats and rel) or abs))
545 ui.status(_('adding %s\n') % ((pats and rel) or abs))
546 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
546 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
547 remove.append(abs)
547 remove.append(abs)
548 if ui.verbose or not exact:
548 if ui.verbose or not exact:
549 ui.status(_('removing %s\n') % ((pats and rel) or abs))
549 ui.status(_('removing %s\n') % ((pats and rel) or abs))
550 repo.add(add, wlock=wlock)
550 repo.add(add, wlock=wlock)
551 repo.remove(remove, wlock=wlock)
551 repo.remove(remove, wlock=wlock)
552
552
553 def annotate(ui, repo, *pats, **opts):
553 def annotate(ui, repo, *pats, **opts):
554 """show changeset information per file line
554 """show changeset information per file line
555
555
556 List changes in files, showing the revision id responsible for each line
556 List changes in files, showing the revision id responsible for each line
557
557
558 This command is useful to discover who did a change or when a change took
558 This command is useful to discover who did a change or when a change took
559 place.
559 place.
560
560
561 Without the -a option, annotate will avoid processing files it
561 Without the -a option, annotate will avoid processing files it
562 detects as binary. With -a, annotate will generate an annotation
562 detects as binary. With -a, annotate will generate an annotation
563 anyway, probably with undesirable results.
563 anyway, probably with undesirable results.
564 """
564 """
565 def getnode(rev):
565 def getnode(rev):
566 return short(repo.changelog.node(rev))
566 return short(repo.changelog.node(rev))
567
567
568 ucache = {}
568 ucache = {}
569 def getname(rev):
569 def getname(rev):
570 cl = repo.changelog.read(repo.changelog.node(rev))
570 cl = repo.changelog.read(repo.changelog.node(rev))
571 return trimuser(ui, cl[1], rev, ucache)
571 return trimuser(ui, cl[1], rev, ucache)
572
572
573 dcache = {}
573 dcache = {}
574 def getdate(rev):
574 def getdate(rev):
575 datestr = dcache.get(rev)
575 datestr = dcache.get(rev)
576 if datestr is None:
576 if datestr is None:
577 cl = repo.changelog.read(repo.changelog.node(rev))
577 cl = repo.changelog.read(repo.changelog.node(rev))
578 datestr = dcache[rev] = util.datestr(cl[2])
578 datestr = dcache[rev] = util.datestr(cl[2])
579 return datestr
579 return datestr
580
580
581 if not pats:
581 if not pats:
582 raise util.Abort(_('at least one file name or pattern required'))
582 raise util.Abort(_('at least one file name or pattern required'))
583
583
584 opmap = [['user', getname], ['number', str], ['changeset', getnode],
584 opmap = [['user', getname], ['number', str], ['changeset', getnode],
585 ['date', getdate]]
585 ['date', getdate]]
586 if not opts['user'] and not opts['changeset'] and not opts['date']:
586 if not opts['user'] and not opts['changeset'] and not opts['date']:
587 opts['number'] = 1
587 opts['number'] = 1
588
588
589 if opts['rev']:
589 if opts['rev']:
590 node = repo.changelog.lookup(opts['rev'])
590 node = repo.changelog.lookup(opts['rev'])
591 else:
591 else:
592 node = repo.dirstate.parents()[0]
592 node = repo.dirstate.parents()[0]
593 change = repo.changelog.read(node)
593 change = repo.changelog.read(node)
594 mmap = repo.manifest.read(change[0])
594 mmap = repo.manifest.read(change[0])
595
595
596 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
596 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
597 f = repo.file(abs)
597 f = repo.file(abs)
598 if not opts['text'] and util.binary(f.read(mmap[abs])):
598 if not opts['text'] and util.binary(f.read(mmap[abs])):
599 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
599 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
600 continue
600 continue
601
601
602 lines = f.annotate(mmap[abs])
602 lines = f.annotate(mmap[abs])
603 pieces = []
603 pieces = []
604
604
605 for o, f in opmap:
605 for o, f in opmap:
606 if opts[o]:
606 if opts[o]:
607 l = [f(n) for n, dummy in lines]
607 l = [f(n) for n, dummy in lines]
608 if l:
608 if l:
609 m = max(map(len, l))
609 m = max(map(len, l))
610 pieces.append(["%*s" % (m, x) for x in l])
610 pieces.append(["%*s" % (m, x) for x in l])
611
611
612 if pieces:
612 if pieces:
613 for p, l in zip(zip(*pieces), lines):
613 for p, l in zip(zip(*pieces), lines):
614 ui.write("%s: %s" % (" ".join(p), l[1]))
614 ui.write("%s: %s" % (" ".join(p), l[1]))
615
615
616 def bundle(ui, repo, fname, dest="default-push", **opts):
616 def bundle(ui, repo, fname, dest="default-push", **opts):
617 """create a changegroup file
617 """create a changegroup file
618
618
619 Generate a compressed changegroup file collecting all changesets
619 Generate a compressed changegroup file collecting all changesets
620 not found in the other repository.
620 not found in the other repository.
621
621
622 This file can then be transferred using conventional means and
622 This file can then be transferred using conventional means and
623 applied to another repository with the unbundle command. This is
623 applied to another repository with the unbundle command. This is
624 useful when native push and pull are not available or when
624 useful when native push and pull are not available or when
625 exporting an entire repository is undesirable. The standard file
625 exporting an entire repository is undesirable. The standard file
626 extension is ".hg".
626 extension is ".hg".
627
627
628 Unlike import/export, this exactly preserves all changeset
628 Unlike import/export, this exactly preserves all changeset
629 contents including permissions, rename data, and revision history.
629 contents including permissions, rename data, and revision history.
630 """
630 """
631 f = open(fname, "wb")
631 f = open(fname, "wb")
632 dest = ui.expandpath(dest, repo.root)
632 dest = ui.expandpath(dest)
633 other = hg.repository(ui, dest)
633 other = hg.repository(ui, dest)
634 o = repo.findoutgoing(other)
634 o = repo.findoutgoing(other)
635 cg = repo.changegroup(o, 'bundle')
635 cg = repo.changegroup(o, 'bundle')
636
636
637 try:
637 try:
638 f.write("HG10")
638 f.write("HG10")
639 z = bz2.BZ2Compressor(9)
639 z = bz2.BZ2Compressor(9)
640 while 1:
640 while 1:
641 chunk = cg.read(4096)
641 chunk = cg.read(4096)
642 if not chunk:
642 if not chunk:
643 break
643 break
644 f.write(z.compress(chunk))
644 f.write(z.compress(chunk))
645 f.write(z.flush())
645 f.write(z.flush())
646 except:
646 except:
647 os.unlink(fname)
647 os.unlink(fname)
648 raise
648 raise
649
649
650 def cat(ui, repo, file1, *pats, **opts):
650 def cat(ui, repo, file1, *pats, **opts):
651 """output the latest or given revisions of files
651 """output the latest or given revisions of files
652
652
653 Print the specified files as they were at the given revision.
653 Print the specified files as they were at the given revision.
654 If no revision is given then the tip is used.
654 If no revision is given then the tip is used.
655
655
656 Output may be to a file, in which case the name of the file is
656 Output may be to a file, in which case the name of the file is
657 given using a format string. The formatting rules are the same as
657 given using a format string. The formatting rules are the same as
658 for the export command, with the following additions:
658 for the export command, with the following additions:
659
659
660 %s basename of file being printed
660 %s basename of file being printed
661 %d dirname of file being printed, or '.' if in repo root
661 %d dirname of file being printed, or '.' if in repo root
662 %p root-relative path name of file being printed
662 %p root-relative path name of file being printed
663 """
663 """
664 mf = {}
664 mf = {}
665 rev = opts['rev']
665 rev = opts['rev']
666 if rev:
666 if rev:
667 node = repo.lookup(rev)
667 node = repo.lookup(rev)
668 else:
668 else:
669 node = repo.changelog.tip()
669 node = repo.changelog.tip()
670 change = repo.changelog.read(node)
670 change = repo.changelog.read(node)
671 mf = repo.manifest.read(change[0])
671 mf = repo.manifest.read(change[0])
672 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
672 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
673 r = repo.file(abs)
673 r = repo.file(abs)
674 n = mf[abs]
674 n = mf[abs]
675 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
675 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
676 fp.write(r.read(n))
676 fp.write(r.read(n))
677
677
678 def clone(ui, source, dest=None, **opts):
678 def clone(ui, source, dest=None, **opts):
679 """make a copy of an existing repository
679 """make a copy of an existing repository
680
680
681 Create a copy of an existing repository in a new directory.
681 Create a copy of an existing repository in a new directory.
682
682
683 If no destination directory name is specified, it defaults to the
683 If no destination directory name is specified, it defaults to the
684 basename of the source.
684 basename of the source.
685
685
686 The location of the source is added to the new repository's
686 The location of the source is added to the new repository's
687 .hg/hgrc file, as the default to be used for future pulls.
687 .hg/hgrc file, as the default to be used for future pulls.
688
688
689 For efficiency, hardlinks are used for cloning whenever the source
689 For efficiency, hardlinks are used for cloning whenever the source
690 and destination are on the same filesystem. Some filesystems,
690 and destination are on the same filesystem. Some filesystems,
691 such as AFS, implement hardlinking incorrectly, but do not report
691 such as AFS, implement hardlinking incorrectly, but do not report
692 errors. In these cases, use the --pull option to avoid
692 errors. In these cases, use the --pull option to avoid
693 hardlinking.
693 hardlinking.
694
694
695 See pull for valid source format details.
695 See pull for valid source format details.
696 """
696 """
697 if dest is None:
697 if dest is None:
698 dest = os.path.basename(os.path.normpath(source))
698 dest = os.path.basename(os.path.normpath(source))
699
699
700 if os.path.exists(dest):
700 if os.path.exists(dest):
701 raise util.Abort(_("destination '%s' already exists"), dest)
701 raise util.Abort(_("destination '%s' already exists"), dest)
702
702
703 dest = os.path.realpath(dest)
703 dest = os.path.realpath(dest)
704
704
705 class Dircleanup(object):
705 class Dircleanup(object):
706 def __init__(self, dir_):
706 def __init__(self, dir_):
707 self.rmtree = shutil.rmtree
707 self.rmtree = shutil.rmtree
708 self.dir_ = dir_
708 self.dir_ = dir_
709 os.mkdir(dir_)
709 os.mkdir(dir_)
710 def close(self):
710 def close(self):
711 self.dir_ = None
711 self.dir_ = None
712 def __del__(self):
712 def __del__(self):
713 if self.dir_:
713 if self.dir_:
714 self.rmtree(self.dir_, True)
714 self.rmtree(self.dir_, True)
715
715
716 if opts['ssh']:
716 if opts['ssh']:
717 ui.setconfig("ui", "ssh", opts['ssh'])
717 ui.setconfig("ui", "ssh", opts['ssh'])
718 if opts['remotecmd']:
718 if opts['remotecmd']:
719 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
719 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
720
720
721 source = ui.expandpath(source)
721 source = ui.expandpath(source)
722
722
723 d = Dircleanup(dest)
723 d = Dircleanup(dest)
724 abspath = source
724 abspath = source
725 other = hg.repository(ui, source)
725 other = hg.repository(ui, source)
726
726
727 copy = False
727 copy = False
728 if other.dev() != -1:
728 if other.dev() != -1:
729 abspath = os.path.abspath(source)
729 abspath = os.path.abspath(source)
730 if not opts['pull'] and not opts['rev']:
730 if not opts['pull'] and not opts['rev']:
731 copy = True
731 copy = True
732
732
733 if copy:
733 if copy:
734 try:
734 try:
735 # we use a lock here because if we race with commit, we
735 # we use a lock here because if we race with commit, we
736 # can end up with extra data in the cloned revlogs that's
736 # can end up with extra data in the cloned revlogs that's
737 # not pointed to by changesets, thus causing verify to
737 # not pointed to by changesets, thus causing verify to
738 # fail
738 # fail
739 l1 = other.lock()
739 l1 = other.lock()
740 except lock.LockException:
740 except lock.LockException:
741 copy = False
741 copy = False
742
742
743 if copy:
743 if copy:
744 # we lock here to avoid premature writing to the target
744 # we lock here to avoid premature writing to the target
745 os.mkdir(os.path.join(dest, ".hg"))
745 os.mkdir(os.path.join(dest, ".hg"))
746 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
746 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
747
747
748 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
748 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
749 for f in files.split():
749 for f in files.split():
750 src = os.path.join(source, ".hg", f)
750 src = os.path.join(source, ".hg", f)
751 dst = os.path.join(dest, ".hg", f)
751 dst = os.path.join(dest, ".hg", f)
752 try:
752 try:
753 util.copyfiles(src, dst)
753 util.copyfiles(src, dst)
754 except OSError, inst:
754 except OSError, inst:
755 if inst.errno != errno.ENOENT:
755 if inst.errno != errno.ENOENT:
756 raise
756 raise
757
757
758 repo = hg.repository(ui, dest)
758 repo = hg.repository(ui, dest)
759
759
760 else:
760 else:
761 revs = None
761 revs = None
762 if opts['rev']:
762 if opts['rev']:
763 if not other.local():
763 if not other.local():
764 error = _("clone -r not supported yet for remote repositories.")
764 error = _("clone -r not supported yet for remote repositories.")
765 raise util.Abort(error)
765 raise util.Abort(error)
766 else:
766 else:
767 revs = [other.lookup(rev) for rev in opts['rev']]
767 revs = [other.lookup(rev) for rev in opts['rev']]
768 repo = hg.repository(ui, dest, create=1)
768 repo = hg.repository(ui, dest, create=1)
769 repo.pull(other, heads = revs)
769 repo.pull(other, heads = revs)
770
770
771 f = repo.opener("hgrc", "w", text=True)
771 f = repo.opener("hgrc", "w", text=True)
772 f.write("[paths]\n")
772 f.write("[paths]\n")
773 f.write("default = %s\n" % abspath)
773 f.write("default = %s\n" % abspath)
774 f.close()
774 f.close()
775
775
776 if not opts['noupdate']:
776 if not opts['noupdate']:
777 update(repo.ui, repo)
777 update(repo.ui, repo)
778
778
779 d.close()
779 d.close()
780
780
781 def commit(ui, repo, *pats, **opts):
781 def commit(ui, repo, *pats, **opts):
782 """commit the specified files or all outstanding changes
782 """commit the specified files or all outstanding changes
783
783
784 Commit changes to the given files into the repository.
784 Commit changes to the given files into the repository.
785
785
786 If a list of files is omitted, all changes reported by "hg status"
786 If a list of files is omitted, all changes reported by "hg status"
787 will be commited.
787 will be commited.
788
788
789 The HGEDITOR or EDITOR environment variables are used to start an
789 The HGEDITOR or EDITOR environment variables are used to start an
790 editor to add a commit comment.
790 editor to add a commit comment.
791 """
791 """
792 message = opts['message']
792 message = opts['message']
793 logfile = opts['logfile']
793 logfile = opts['logfile']
794
794
795 if message and logfile:
795 if message and logfile:
796 raise util.Abort(_('options --message and --logfile are mutually '
796 raise util.Abort(_('options --message and --logfile are mutually '
797 'exclusive'))
797 'exclusive'))
798 if not message and logfile:
798 if not message and logfile:
799 try:
799 try:
800 if logfile == '-':
800 if logfile == '-':
801 message = sys.stdin.read()
801 message = sys.stdin.read()
802 else:
802 else:
803 message = open(logfile).read()
803 message = open(logfile).read()
804 except IOError, inst:
804 except IOError, inst:
805 raise util.Abort(_("can't read commit message '%s': %s") %
805 raise util.Abort(_("can't read commit message '%s': %s") %
806 (logfile, inst.strerror))
806 (logfile, inst.strerror))
807
807
808 if opts['addremove']:
808 if opts['addremove']:
809 addremove(ui, repo, *pats, **opts)
809 addremove(ui, repo, *pats, **opts)
810 fns, match, anypats = matchpats(repo, pats, opts)
810 fns, match, anypats = matchpats(repo, pats, opts)
811 if pats:
811 if pats:
812 modified, added, removed, deleted, unknown = (
812 modified, added, removed, deleted, unknown = (
813 repo.changes(files=fns, match=match))
813 repo.changes(files=fns, match=match))
814 files = modified + added + removed
814 files = modified + added + removed
815 else:
815 else:
816 files = []
816 files = []
817 try:
817 try:
818 repo.commit(files, message, opts['user'], opts['date'], match)
818 repo.commit(files, message, opts['user'], opts['date'], match)
819 except ValueError, inst:
819 except ValueError, inst:
820 raise util.Abort(str(inst))
820 raise util.Abort(str(inst))
821
821
822 def docopy(ui, repo, pats, opts, wlock):
822 def docopy(ui, repo, pats, opts, wlock):
823 # called with the repo lock held
823 # called with the repo lock held
824 cwd = repo.getcwd()
824 cwd = repo.getcwd()
825 errors = 0
825 errors = 0
826 copied = []
826 copied = []
827 targets = {}
827 targets = {}
828
828
829 def okaytocopy(abs, rel, exact):
829 def okaytocopy(abs, rel, exact):
830 reasons = {'?': _('is not managed'),
830 reasons = {'?': _('is not managed'),
831 'a': _('has been marked for add'),
831 'a': _('has been marked for add'),
832 'r': _('has been marked for remove')}
832 'r': _('has been marked for remove')}
833 state = repo.dirstate.state(abs)
833 state = repo.dirstate.state(abs)
834 reason = reasons.get(state)
834 reason = reasons.get(state)
835 if reason:
835 if reason:
836 if state == 'a':
836 if state == 'a':
837 origsrc = repo.dirstate.copied(abs)
837 origsrc = repo.dirstate.copied(abs)
838 if origsrc is not None:
838 if origsrc is not None:
839 return origsrc
839 return origsrc
840 if exact:
840 if exact:
841 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
841 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
842 else:
842 else:
843 return abs
843 return abs
844
844
845 def copy(origsrc, abssrc, relsrc, target, exact):
845 def copy(origsrc, abssrc, relsrc, target, exact):
846 abstarget = util.canonpath(repo.root, cwd, target)
846 abstarget = util.canonpath(repo.root, cwd, target)
847 reltarget = util.pathto(cwd, abstarget)
847 reltarget = util.pathto(cwd, abstarget)
848 prevsrc = targets.get(abstarget)
848 prevsrc = targets.get(abstarget)
849 if prevsrc is not None:
849 if prevsrc is not None:
850 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
850 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
851 (reltarget, abssrc, prevsrc))
851 (reltarget, abssrc, prevsrc))
852 return
852 return
853 if (not opts['after'] and os.path.exists(reltarget) or
853 if (not opts['after'] and os.path.exists(reltarget) or
854 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
854 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
855 if not opts['force']:
855 if not opts['force']:
856 ui.warn(_('%s: not overwriting - file exists\n') %
856 ui.warn(_('%s: not overwriting - file exists\n') %
857 reltarget)
857 reltarget)
858 return
858 return
859 if not opts['after']:
859 if not opts['after']:
860 os.unlink(reltarget)
860 os.unlink(reltarget)
861 if opts['after']:
861 if opts['after']:
862 if not os.path.exists(reltarget):
862 if not os.path.exists(reltarget):
863 return
863 return
864 else:
864 else:
865 targetdir = os.path.dirname(reltarget) or '.'
865 targetdir = os.path.dirname(reltarget) or '.'
866 if not os.path.isdir(targetdir):
866 if not os.path.isdir(targetdir):
867 os.makedirs(targetdir)
867 os.makedirs(targetdir)
868 try:
868 try:
869 restore = repo.dirstate.state(abstarget) == 'r'
869 restore = repo.dirstate.state(abstarget) == 'r'
870 if restore:
870 if restore:
871 repo.undelete([abstarget], wlock)
871 repo.undelete([abstarget], wlock)
872 try:
872 try:
873 shutil.copyfile(relsrc, reltarget)
873 shutil.copyfile(relsrc, reltarget)
874 shutil.copymode(relsrc, reltarget)
874 shutil.copymode(relsrc, reltarget)
875 restore = False
875 restore = False
876 finally:
876 finally:
877 if restore:
877 if restore:
878 repo.remove([abstarget], wlock)
878 repo.remove([abstarget], wlock)
879 except shutil.Error, inst:
879 except shutil.Error, inst:
880 raise util.Abort(str(inst))
880 raise util.Abort(str(inst))
881 except IOError, inst:
881 except IOError, inst:
882 if inst.errno == errno.ENOENT:
882 if inst.errno == errno.ENOENT:
883 ui.warn(_('%s: deleted in working copy\n') % relsrc)
883 ui.warn(_('%s: deleted in working copy\n') % relsrc)
884 else:
884 else:
885 ui.warn(_('%s: cannot copy - %s\n') %
885 ui.warn(_('%s: cannot copy - %s\n') %
886 (relsrc, inst.strerror))
886 (relsrc, inst.strerror))
887 errors += 1
887 errors += 1
888 return
888 return
889 if ui.verbose or not exact:
889 if ui.verbose or not exact:
890 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
890 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
891 targets[abstarget] = abssrc
891 targets[abstarget] = abssrc
892 if abstarget != origsrc:
892 if abstarget != origsrc:
893 repo.copy(origsrc, abstarget, wlock)
893 repo.copy(origsrc, abstarget, wlock)
894 copied.append((abssrc, relsrc, exact))
894 copied.append((abssrc, relsrc, exact))
895
895
896 def targetpathfn(pat, dest, srcs):
896 def targetpathfn(pat, dest, srcs):
897 if os.path.isdir(pat):
897 if os.path.isdir(pat):
898 abspfx = util.canonpath(repo.root, cwd, pat)
898 abspfx = util.canonpath(repo.root, cwd, pat)
899 if destdirexists:
899 if destdirexists:
900 striplen = len(os.path.split(abspfx)[0])
900 striplen = len(os.path.split(abspfx)[0])
901 else:
901 else:
902 striplen = len(abspfx)
902 striplen = len(abspfx)
903 if striplen:
903 if striplen:
904 striplen += len(os.sep)
904 striplen += len(os.sep)
905 res = lambda p: os.path.join(dest, p[striplen:])
905 res = lambda p: os.path.join(dest, p[striplen:])
906 elif destdirexists:
906 elif destdirexists:
907 res = lambda p: os.path.join(dest, os.path.basename(p))
907 res = lambda p: os.path.join(dest, os.path.basename(p))
908 else:
908 else:
909 res = lambda p: dest
909 res = lambda p: dest
910 return res
910 return res
911
911
912 def targetpathafterfn(pat, dest, srcs):
912 def targetpathafterfn(pat, dest, srcs):
913 if util.patkind(pat, None)[0]:
913 if util.patkind(pat, None)[0]:
914 # a mercurial pattern
914 # a mercurial pattern
915 res = lambda p: os.path.join(dest, os.path.basename(p))
915 res = lambda p: os.path.join(dest, os.path.basename(p))
916 else:
916 else:
917 abspfx = util.canonpath(repo.root, cwd, pat)
917 abspfx = util.canonpath(repo.root, cwd, pat)
918 if len(abspfx) < len(srcs[0][0]):
918 if len(abspfx) < len(srcs[0][0]):
919 # A directory. Either the target path contains the last
919 # A directory. Either the target path contains the last
920 # component of the source path or it does not.
920 # component of the source path or it does not.
921 def evalpath(striplen):
921 def evalpath(striplen):
922 score = 0
922 score = 0
923 for s in srcs:
923 for s in srcs:
924 t = os.path.join(dest, s[0][striplen:])
924 t = os.path.join(dest, s[0][striplen:])
925 if os.path.exists(t):
925 if os.path.exists(t):
926 score += 1
926 score += 1
927 return score
927 return score
928
928
929 striplen = len(abspfx)
929 striplen = len(abspfx)
930 if striplen:
930 if striplen:
931 striplen += len(os.sep)
931 striplen += len(os.sep)
932 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
932 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
933 score = evalpath(striplen)
933 score = evalpath(striplen)
934 striplen1 = len(os.path.split(abspfx)[0])
934 striplen1 = len(os.path.split(abspfx)[0])
935 if striplen1:
935 if striplen1:
936 striplen1 += len(os.sep)
936 striplen1 += len(os.sep)
937 if evalpath(striplen1) > score:
937 if evalpath(striplen1) > score:
938 striplen = striplen1
938 striplen = striplen1
939 res = lambda p: os.path.join(dest, p[striplen:])
939 res = lambda p: os.path.join(dest, p[striplen:])
940 else:
940 else:
941 # a file
941 # a file
942 if destdirexists:
942 if destdirexists:
943 res = lambda p: os.path.join(dest, os.path.basename(p))
943 res = lambda p: os.path.join(dest, os.path.basename(p))
944 else:
944 else:
945 res = lambda p: dest
945 res = lambda p: dest
946 return res
946 return res
947
947
948
948
949 pats = list(pats)
949 pats = list(pats)
950 if not pats:
950 if not pats:
951 raise util.Abort(_('no source or destination specified'))
951 raise util.Abort(_('no source or destination specified'))
952 if len(pats) == 1:
952 if len(pats) == 1:
953 raise util.Abort(_('no destination specified'))
953 raise util.Abort(_('no destination specified'))
954 dest = pats.pop()
954 dest = pats.pop()
955 destdirexists = os.path.isdir(dest)
955 destdirexists = os.path.isdir(dest)
956 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
956 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
957 raise util.Abort(_('with multiple sources, destination must be an '
957 raise util.Abort(_('with multiple sources, destination must be an '
958 'existing directory'))
958 'existing directory'))
959 if opts['after']:
959 if opts['after']:
960 tfn = targetpathafterfn
960 tfn = targetpathafterfn
961 else:
961 else:
962 tfn = targetpathfn
962 tfn = targetpathfn
963 copylist = []
963 copylist = []
964 for pat in pats:
964 for pat in pats:
965 srcs = []
965 srcs = []
966 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
966 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
967 origsrc = okaytocopy(abssrc, relsrc, exact)
967 origsrc = okaytocopy(abssrc, relsrc, exact)
968 if origsrc:
968 if origsrc:
969 srcs.append((origsrc, abssrc, relsrc, exact))
969 srcs.append((origsrc, abssrc, relsrc, exact))
970 if not srcs:
970 if not srcs:
971 continue
971 continue
972 copylist.append((tfn(pat, dest, srcs), srcs))
972 copylist.append((tfn(pat, dest, srcs), srcs))
973 if not copylist:
973 if not copylist:
974 raise util.Abort(_('no files to copy'))
974 raise util.Abort(_('no files to copy'))
975
975
976 for targetpath, srcs in copylist:
976 for targetpath, srcs in copylist:
977 for origsrc, abssrc, relsrc, exact in srcs:
977 for origsrc, abssrc, relsrc, exact in srcs:
978 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
978 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
979
979
980 if errors:
980 if errors:
981 ui.warn(_('(consider using --after)\n'))
981 ui.warn(_('(consider using --after)\n'))
982 return errors, copied
982 return errors, copied
983
983
984 def copy(ui, repo, *pats, **opts):
984 def copy(ui, repo, *pats, **opts):
985 """mark files as copied for the next commit
985 """mark files as copied for the next commit
986
986
987 Mark dest as having copies of source files. If dest is a
987 Mark dest as having copies of source files. If dest is a
988 directory, copies are put in that directory. If dest is a file,
988 directory, copies are put in that directory. If dest is a file,
989 there can only be one source.
989 there can only be one source.
990
990
991 By default, this command copies the contents of files as they
991 By default, this command copies the contents of files as they
992 stand in the working directory. If invoked with --after, the
992 stand in the working directory. If invoked with --after, the
993 operation is recorded, but no copying is performed.
993 operation is recorded, but no copying is performed.
994
994
995 This command takes effect in the next commit.
995 This command takes effect in the next commit.
996
996
997 NOTE: This command should be treated as experimental. While it
997 NOTE: This command should be treated as experimental. While it
998 should properly record copied files, this information is not yet
998 should properly record copied files, this information is not yet
999 fully used by merge, nor fully reported by log.
999 fully used by merge, nor fully reported by log.
1000 """
1000 """
1001 try:
1001 try:
1002 wlock = repo.wlock(0)
1002 wlock = repo.wlock(0)
1003 errs, copied = docopy(ui, repo, pats, opts, wlock)
1003 errs, copied = docopy(ui, repo, pats, opts, wlock)
1004 except lock.LockHeld, inst:
1004 except lock.LockHeld, inst:
1005 ui.warn(_("repository lock held by %s\n") % inst.args[0])
1005 ui.warn(_("repository lock held by %s\n") % inst.args[0])
1006 errs = 1
1006 errs = 1
1007 return errs
1007 return errs
1008
1008
1009 def debugancestor(ui, index, rev1, rev2):
1009 def debugancestor(ui, index, rev1, rev2):
1010 """find the ancestor revision of two revisions in a given index"""
1010 """find the ancestor revision of two revisions in a given index"""
1011 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "")
1011 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "")
1012 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1012 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1013 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1013 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1014
1014
1015 def debugcomplete(ui, cmd):
1015 def debugcomplete(ui, cmd):
1016 """returns the completion list associated with the given command"""
1016 """returns the completion list associated with the given command"""
1017 clist = findpossible(cmd).keys()
1017 clist = findpossible(cmd).keys()
1018 clist.sort()
1018 clist.sort()
1019 ui.write("%s\n" % " ".join(clist))
1019 ui.write("%s\n" % " ".join(clist))
1020
1020
1021 def debugrebuildstate(ui, repo, rev=None):
1021 def debugrebuildstate(ui, repo, rev=None):
1022 """rebuild the dirstate as it would look like for the given revision"""
1022 """rebuild the dirstate as it would look like for the given revision"""
1023 if not rev:
1023 if not rev:
1024 rev = repo.changelog.tip()
1024 rev = repo.changelog.tip()
1025 else:
1025 else:
1026 rev = repo.lookup(rev)
1026 rev = repo.lookup(rev)
1027 change = repo.changelog.read(rev)
1027 change = repo.changelog.read(rev)
1028 n = change[0]
1028 n = change[0]
1029 files = repo.manifest.readflags(n)
1029 files = repo.manifest.readflags(n)
1030 wlock = repo.wlock()
1030 wlock = repo.wlock()
1031 repo.dirstate.rebuild(rev, files.iteritems())
1031 repo.dirstate.rebuild(rev, files.iteritems())
1032
1032
1033 def debugcheckstate(ui, repo):
1033 def debugcheckstate(ui, repo):
1034 """validate the correctness of the current dirstate"""
1034 """validate the correctness of the current dirstate"""
1035 parent1, parent2 = repo.dirstate.parents()
1035 parent1, parent2 = repo.dirstate.parents()
1036 repo.dirstate.read()
1036 repo.dirstate.read()
1037 dc = repo.dirstate.map
1037 dc = repo.dirstate.map
1038 keys = dc.keys()
1038 keys = dc.keys()
1039 keys.sort()
1039 keys.sort()
1040 m1n = repo.changelog.read(parent1)[0]
1040 m1n = repo.changelog.read(parent1)[0]
1041 m2n = repo.changelog.read(parent2)[0]
1041 m2n = repo.changelog.read(parent2)[0]
1042 m1 = repo.manifest.read(m1n)
1042 m1 = repo.manifest.read(m1n)
1043 m2 = repo.manifest.read(m2n)
1043 m2 = repo.manifest.read(m2n)
1044 errors = 0
1044 errors = 0
1045 for f in dc:
1045 for f in dc:
1046 state = repo.dirstate.state(f)
1046 state = repo.dirstate.state(f)
1047 if state in "nr" and f not in m1:
1047 if state in "nr" and f not in m1:
1048 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1048 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1049 errors += 1
1049 errors += 1
1050 if state in "a" and f in m1:
1050 if state in "a" and f in m1:
1051 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1051 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1052 errors += 1
1052 errors += 1
1053 if state in "m" and f not in m1 and f not in m2:
1053 if state in "m" and f not in m1 and f not in m2:
1054 ui.warn(_("%s in state %s, but not in either manifest\n") %
1054 ui.warn(_("%s in state %s, but not in either manifest\n") %
1055 (f, state))
1055 (f, state))
1056 errors += 1
1056 errors += 1
1057 for f in m1:
1057 for f in m1:
1058 state = repo.dirstate.state(f)
1058 state = repo.dirstate.state(f)
1059 if state not in "nrm":
1059 if state not in "nrm":
1060 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1060 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1061 errors += 1
1061 errors += 1
1062 if errors:
1062 if errors:
1063 error = _(".hg/dirstate inconsistent with current parent's manifest")
1063 error = _(".hg/dirstate inconsistent with current parent's manifest")
1064 raise util.Abort(error)
1064 raise util.Abort(error)
1065
1065
1066 def debugconfig(ui, repo):
1066 def debugconfig(ui, repo):
1067 """show combined config settings from all hgrc files"""
1067 """show combined config settings from all hgrc files"""
1068 for section, name, value in ui.walkconfig():
1068 for section, name, value in ui.walkconfig():
1069 ui.write('%s.%s=%s\n' % (section, name, value))
1069 ui.write('%s.%s=%s\n' % (section, name, value))
1070
1070
1071 def debugsetparents(ui, repo, rev1, rev2=None):
1071 def debugsetparents(ui, repo, rev1, rev2=None):
1072 """manually set the parents of the current working directory
1072 """manually set the parents of the current working directory
1073
1073
1074 This is useful for writing repository conversion tools, but should
1074 This is useful for writing repository conversion tools, but should
1075 be used with care.
1075 be used with care.
1076 """
1076 """
1077
1077
1078 if not rev2:
1078 if not rev2:
1079 rev2 = hex(nullid)
1079 rev2 = hex(nullid)
1080
1080
1081 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1081 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1082
1082
1083 def debugstate(ui, repo):
1083 def debugstate(ui, repo):
1084 """show the contents of the current dirstate"""
1084 """show the contents of the current dirstate"""
1085 repo.dirstate.read()
1085 repo.dirstate.read()
1086 dc = repo.dirstate.map
1086 dc = repo.dirstate.map
1087 keys = dc.keys()
1087 keys = dc.keys()
1088 keys.sort()
1088 keys.sort()
1089 for file_ in keys:
1089 for file_ in keys:
1090 ui.write("%c %3o %10d %s %s\n"
1090 ui.write("%c %3o %10d %s %s\n"
1091 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1091 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1092 time.strftime("%x %X",
1092 time.strftime("%x %X",
1093 time.localtime(dc[file_][3])), file_))
1093 time.localtime(dc[file_][3])), file_))
1094 for f in repo.dirstate.copies:
1094 for f in repo.dirstate.copies:
1095 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1095 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1096
1096
1097 def debugdata(ui, file_, rev):
1097 def debugdata(ui, file_, rev):
1098 """dump the contents of an data file revision"""
1098 """dump the contents of an data file revision"""
1099 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1099 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1100 file_[:-2] + ".i", file_)
1100 file_[:-2] + ".i", file_)
1101 try:
1101 try:
1102 ui.write(r.revision(r.lookup(rev)))
1102 ui.write(r.revision(r.lookup(rev)))
1103 except KeyError:
1103 except KeyError:
1104 raise util.Abort(_('invalid revision identifier %s'), rev)
1104 raise util.Abort(_('invalid revision identifier %s'), rev)
1105
1105
1106 def debugindex(ui, file_):
1106 def debugindex(ui, file_):
1107 """dump the contents of an index file"""
1107 """dump the contents of an index file"""
1108 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1108 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1109 ui.write(" rev offset length base linkrev" +
1109 ui.write(" rev offset length base linkrev" +
1110 " nodeid p1 p2\n")
1110 " nodeid p1 p2\n")
1111 for i in range(r.count()):
1111 for i in range(r.count()):
1112 e = r.index[i]
1112 e = r.index[i]
1113 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1113 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1114 i, e[0], e[1], e[2], e[3],
1114 i, e[0], e[1], e[2], e[3],
1115 short(e[6]), short(e[4]), short(e[5])))
1115 short(e[6]), short(e[4]), short(e[5])))
1116
1116
1117 def debugindexdot(ui, file_):
1117 def debugindexdot(ui, file_):
1118 """dump an index DAG as a .dot file"""
1118 """dump an index DAG as a .dot file"""
1119 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1119 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1120 ui.write("digraph G {\n")
1120 ui.write("digraph G {\n")
1121 for i in range(r.count()):
1121 for i in range(r.count()):
1122 e = r.index[i]
1122 e = r.index[i]
1123 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1123 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1124 if e[5] != nullid:
1124 if e[5] != nullid:
1125 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1125 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1126 ui.write("}\n")
1126 ui.write("}\n")
1127
1127
1128 def debugrename(ui, repo, file, rev=None):
1128 def debugrename(ui, repo, file, rev=None):
1129 """dump rename information"""
1129 """dump rename information"""
1130 r = repo.file(relpath(repo, [file])[0])
1130 r = repo.file(relpath(repo, [file])[0])
1131 if rev:
1131 if rev:
1132 try:
1132 try:
1133 # assume all revision numbers are for changesets
1133 # assume all revision numbers are for changesets
1134 n = repo.lookup(rev)
1134 n = repo.lookup(rev)
1135 change = repo.changelog.read(n)
1135 change = repo.changelog.read(n)
1136 m = repo.manifest.read(change[0])
1136 m = repo.manifest.read(change[0])
1137 n = m[relpath(repo, [file])[0]]
1137 n = m[relpath(repo, [file])[0]]
1138 except (hg.RepoError, KeyError):
1138 except (hg.RepoError, KeyError):
1139 n = r.lookup(rev)
1139 n = r.lookup(rev)
1140 else:
1140 else:
1141 n = r.tip()
1141 n = r.tip()
1142 m = r.renamed(n)
1142 m = r.renamed(n)
1143 if m:
1143 if m:
1144 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1144 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1145 else:
1145 else:
1146 ui.write(_("not renamed\n"))
1146 ui.write(_("not renamed\n"))
1147
1147
1148 def debugwalk(ui, repo, *pats, **opts):
1148 def debugwalk(ui, repo, *pats, **opts):
1149 """show how files match on given patterns"""
1149 """show how files match on given patterns"""
1150 items = list(walk(repo, pats, opts))
1150 items = list(walk(repo, pats, opts))
1151 if not items:
1151 if not items:
1152 return
1152 return
1153 fmt = '%%s %%-%ds %%-%ds %%s' % (
1153 fmt = '%%s %%-%ds %%-%ds %%s' % (
1154 max([len(abs) for (src, abs, rel, exact) in items]),
1154 max([len(abs) for (src, abs, rel, exact) in items]),
1155 max([len(rel) for (src, abs, rel, exact) in items]))
1155 max([len(rel) for (src, abs, rel, exact) in items]))
1156 for src, abs, rel, exact in items:
1156 for src, abs, rel, exact in items:
1157 line = fmt % (src, abs, rel, exact and 'exact' or '')
1157 line = fmt % (src, abs, rel, exact and 'exact' or '')
1158 ui.write("%s\n" % line.rstrip())
1158 ui.write("%s\n" % line.rstrip())
1159
1159
1160 def diff(ui, repo, *pats, **opts):
1160 def diff(ui, repo, *pats, **opts):
1161 """diff repository (or selected files)
1161 """diff repository (or selected files)
1162
1162
1163 Show differences between revisions for the specified files.
1163 Show differences between revisions for the specified files.
1164
1164
1165 Differences between files are shown using the unified diff format.
1165 Differences between files are shown using the unified diff format.
1166
1166
1167 When two revision arguments are given, then changes are shown
1167 When two revision arguments are given, then changes are shown
1168 between those revisions. If only one revision is specified then
1168 between those revisions. If only one revision is specified then
1169 that revision is compared to the working directory, and, when no
1169 that revision is compared to the working directory, and, when no
1170 revisions are specified, the working directory files are compared
1170 revisions are specified, the working directory files are compared
1171 to its parent.
1171 to its parent.
1172
1172
1173 Without the -a option, diff will avoid generating diffs of files
1173 Without the -a option, diff will avoid generating diffs of files
1174 it detects as binary. With -a, diff will generate a diff anyway,
1174 it detects as binary. With -a, diff will generate a diff anyway,
1175 probably with undesirable results.
1175 probably with undesirable results.
1176 """
1176 """
1177 node1, node2 = None, None
1177 node1, node2 = None, None
1178 revs = [repo.lookup(x) for x in opts['rev']]
1178 revs = [repo.lookup(x) for x in opts['rev']]
1179
1179
1180 if len(revs) > 0:
1180 if len(revs) > 0:
1181 node1 = revs[0]
1181 node1 = revs[0]
1182 if len(revs) > 1:
1182 if len(revs) > 1:
1183 node2 = revs[1]
1183 node2 = revs[1]
1184 if len(revs) > 2:
1184 if len(revs) > 2:
1185 raise util.Abort(_("too many revisions to diff"))
1185 raise util.Abort(_("too many revisions to diff"))
1186
1186
1187 fns, matchfn, anypats = matchpats(repo, pats, opts)
1187 fns, matchfn, anypats = matchpats(repo, pats, opts)
1188
1188
1189 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1189 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1190 text=opts['text'], opts=opts)
1190 text=opts['text'], opts=opts)
1191
1191
1192 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1192 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1193 node = repo.lookup(changeset)
1193 node = repo.lookup(changeset)
1194 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1194 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1195 if opts['switch_parent']:
1195 if opts['switch_parent']:
1196 parents.reverse()
1196 parents.reverse()
1197 prev = (parents and parents[0]) or nullid
1197 prev = (parents and parents[0]) or nullid
1198 change = repo.changelog.read(node)
1198 change = repo.changelog.read(node)
1199
1199
1200 fp = make_file(repo, repo.changelog, opts['output'],
1200 fp = make_file(repo, repo.changelog, opts['output'],
1201 node=node, total=total, seqno=seqno,
1201 node=node, total=total, seqno=seqno,
1202 revwidth=revwidth)
1202 revwidth=revwidth)
1203 if fp != sys.stdout:
1203 if fp != sys.stdout:
1204 ui.note("%s\n" % fp.name)
1204 ui.note("%s\n" % fp.name)
1205
1205
1206 fp.write("# HG changeset patch\n")
1206 fp.write("# HG changeset patch\n")
1207 fp.write("# User %s\n" % change[1])
1207 fp.write("# User %s\n" % change[1])
1208 fp.write("# Node ID %s\n" % hex(node))
1208 fp.write("# Node ID %s\n" % hex(node))
1209 fp.write("# Parent %s\n" % hex(prev))
1209 fp.write("# Parent %s\n" % hex(prev))
1210 if len(parents) > 1:
1210 if len(parents) > 1:
1211 fp.write("# Parent %s\n" % hex(parents[1]))
1211 fp.write("# Parent %s\n" % hex(parents[1]))
1212 fp.write(change[4].rstrip())
1212 fp.write(change[4].rstrip())
1213 fp.write("\n\n")
1213 fp.write("\n\n")
1214
1214
1215 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1215 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1216 if fp != sys.stdout:
1216 if fp != sys.stdout:
1217 fp.close()
1217 fp.close()
1218
1218
1219 def export(ui, repo, *changesets, **opts):
1219 def export(ui, repo, *changesets, **opts):
1220 """dump the header and diffs for one or more changesets
1220 """dump the header and diffs for one or more changesets
1221
1221
1222 Print the changeset header and diffs for one or more revisions.
1222 Print the changeset header and diffs for one or more revisions.
1223
1223
1224 The information shown in the changeset header is: author,
1224 The information shown in the changeset header is: author,
1225 changeset hash, parent and commit comment.
1225 changeset hash, parent and commit comment.
1226
1226
1227 Output may be to a file, in which case the name of the file is
1227 Output may be to a file, in which case the name of the file is
1228 given using a format string. The formatting rules are as follows:
1228 given using a format string. The formatting rules are as follows:
1229
1229
1230 %% literal "%" character
1230 %% literal "%" character
1231 %H changeset hash (40 bytes of hexadecimal)
1231 %H changeset hash (40 bytes of hexadecimal)
1232 %N number of patches being generated
1232 %N number of patches being generated
1233 %R changeset revision number
1233 %R changeset revision number
1234 %b basename of the exporting repository
1234 %b basename of the exporting repository
1235 %h short-form changeset hash (12 bytes of hexadecimal)
1235 %h short-form changeset hash (12 bytes of hexadecimal)
1236 %n zero-padded sequence number, starting at 1
1236 %n zero-padded sequence number, starting at 1
1237 %r zero-padded changeset revision number
1237 %r zero-padded changeset revision number
1238
1238
1239 Without the -a option, export will avoid generating diffs of files
1239 Without the -a option, export will avoid generating diffs of files
1240 it detects as binary. With -a, export will generate a diff anyway,
1240 it detects as binary. With -a, export will generate a diff anyway,
1241 probably with undesirable results.
1241 probably with undesirable results.
1242
1242
1243 With the --switch-parent option, the diff will be against the second
1243 With the --switch-parent option, the diff will be against the second
1244 parent. It can be useful to review a merge.
1244 parent. It can be useful to review a merge.
1245 """
1245 """
1246 if not changesets:
1246 if not changesets:
1247 raise util.Abort(_("export requires at least one changeset"))
1247 raise util.Abort(_("export requires at least one changeset"))
1248 seqno = 0
1248 seqno = 0
1249 revs = list(revrange(ui, repo, changesets))
1249 revs = list(revrange(ui, repo, changesets))
1250 total = len(revs)
1250 total = len(revs)
1251 revwidth = max(map(len, revs))
1251 revwidth = max(map(len, revs))
1252 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1252 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1253 ui.note(msg)
1253 ui.note(msg)
1254 for cset in revs:
1254 for cset in revs:
1255 seqno += 1
1255 seqno += 1
1256 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1256 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1257
1257
1258 def forget(ui, repo, *pats, **opts):
1258 def forget(ui, repo, *pats, **opts):
1259 """don't add the specified files on the next commit
1259 """don't add the specified files on the next commit
1260
1260
1261 Undo an 'hg add' scheduled for the next commit.
1261 Undo an 'hg add' scheduled for the next commit.
1262 """
1262 """
1263 forget = []
1263 forget = []
1264 for src, abs, rel, exact in walk(repo, pats, opts):
1264 for src, abs, rel, exact in walk(repo, pats, opts):
1265 if repo.dirstate.state(abs) == 'a':
1265 if repo.dirstate.state(abs) == 'a':
1266 forget.append(abs)
1266 forget.append(abs)
1267 if ui.verbose or not exact:
1267 if ui.verbose or not exact:
1268 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1268 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1269 repo.forget(forget)
1269 repo.forget(forget)
1270
1270
1271 def grep(ui, repo, pattern, *pats, **opts):
1271 def grep(ui, repo, pattern, *pats, **opts):
1272 """search for a pattern in specified files and revisions
1272 """search for a pattern in specified files and revisions
1273
1273
1274 Search revisions of files for a regular expression.
1274 Search revisions of files for a regular expression.
1275
1275
1276 This command behaves differently than Unix grep. It only accepts
1276 This command behaves differently than Unix grep. It only accepts
1277 Python/Perl regexps. It searches repository history, not the
1277 Python/Perl regexps. It searches repository history, not the
1278 working directory. It always prints the revision number in which
1278 working directory. It always prints the revision number in which
1279 a match appears.
1279 a match appears.
1280
1280
1281 By default, grep only prints output for the first revision of a
1281 By default, grep only prints output for the first revision of a
1282 file in which it finds a match. To get it to print every revision
1282 file in which it finds a match. To get it to print every revision
1283 that contains a change in match status ("-" for a match that
1283 that contains a change in match status ("-" for a match that
1284 becomes a non-match, or "+" for a non-match that becomes a match),
1284 becomes a non-match, or "+" for a non-match that becomes a match),
1285 use the --all flag.
1285 use the --all flag.
1286 """
1286 """
1287 reflags = 0
1287 reflags = 0
1288 if opts['ignore_case']:
1288 if opts['ignore_case']:
1289 reflags |= re.I
1289 reflags |= re.I
1290 regexp = re.compile(pattern, reflags)
1290 regexp = re.compile(pattern, reflags)
1291 sep, eol = ':', '\n'
1291 sep, eol = ':', '\n'
1292 if opts['print0']:
1292 if opts['print0']:
1293 sep = eol = '\0'
1293 sep = eol = '\0'
1294
1294
1295 fcache = {}
1295 fcache = {}
1296 def getfile(fn):
1296 def getfile(fn):
1297 if fn not in fcache:
1297 if fn not in fcache:
1298 fcache[fn] = repo.file(fn)
1298 fcache[fn] = repo.file(fn)
1299 return fcache[fn]
1299 return fcache[fn]
1300
1300
1301 def matchlines(body):
1301 def matchlines(body):
1302 begin = 0
1302 begin = 0
1303 linenum = 0
1303 linenum = 0
1304 while True:
1304 while True:
1305 match = regexp.search(body, begin)
1305 match = regexp.search(body, begin)
1306 if not match:
1306 if not match:
1307 break
1307 break
1308 mstart, mend = match.span()
1308 mstart, mend = match.span()
1309 linenum += body.count('\n', begin, mstart) + 1
1309 linenum += body.count('\n', begin, mstart) + 1
1310 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1310 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1311 lend = body.find('\n', mend)
1311 lend = body.find('\n', mend)
1312 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1312 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1313 begin = lend + 1
1313 begin = lend + 1
1314
1314
1315 class linestate(object):
1315 class linestate(object):
1316 def __init__(self, line, linenum, colstart, colend):
1316 def __init__(self, line, linenum, colstart, colend):
1317 self.line = line
1317 self.line = line
1318 self.linenum = linenum
1318 self.linenum = linenum
1319 self.colstart = colstart
1319 self.colstart = colstart
1320 self.colend = colend
1320 self.colend = colend
1321 def __eq__(self, other):
1321 def __eq__(self, other):
1322 return self.line == other.line
1322 return self.line == other.line
1323 def __hash__(self):
1323 def __hash__(self):
1324 return hash(self.line)
1324 return hash(self.line)
1325
1325
1326 matches = {}
1326 matches = {}
1327 def grepbody(fn, rev, body):
1327 def grepbody(fn, rev, body):
1328 matches[rev].setdefault(fn, {})
1328 matches[rev].setdefault(fn, {})
1329 m = matches[rev][fn]
1329 m = matches[rev][fn]
1330 for lnum, cstart, cend, line in matchlines(body):
1330 for lnum, cstart, cend, line in matchlines(body):
1331 s = linestate(line, lnum, cstart, cend)
1331 s = linestate(line, lnum, cstart, cend)
1332 m[s] = s
1332 m[s] = s
1333
1333
1334 # FIXME: prev isn't used, why ?
1334 # FIXME: prev isn't used, why ?
1335 prev = {}
1335 prev = {}
1336 ucache = {}
1336 ucache = {}
1337 def display(fn, rev, states, prevstates):
1337 def display(fn, rev, states, prevstates):
1338 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1338 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1339 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1339 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1340 counts = {'-': 0, '+': 0}
1340 counts = {'-': 0, '+': 0}
1341 filerevmatches = {}
1341 filerevmatches = {}
1342 for l in diff:
1342 for l in diff:
1343 if incrementing or not opts['all']:
1343 if incrementing or not opts['all']:
1344 change = ((l in prevstates) and '-') or '+'
1344 change = ((l in prevstates) and '-') or '+'
1345 r = rev
1345 r = rev
1346 else:
1346 else:
1347 change = ((l in states) and '-') or '+'
1347 change = ((l in states) and '-') or '+'
1348 r = prev[fn]
1348 r = prev[fn]
1349 cols = [fn, str(rev)]
1349 cols = [fn, str(rev)]
1350 if opts['line_number']:
1350 if opts['line_number']:
1351 cols.append(str(l.linenum))
1351 cols.append(str(l.linenum))
1352 if opts['all']:
1352 if opts['all']:
1353 cols.append(change)
1353 cols.append(change)
1354 if opts['user']:
1354 if opts['user']:
1355 cols.append(trimuser(ui, getchange(rev)[1], rev,
1355 cols.append(trimuser(ui, getchange(rev)[1], rev,
1356 ucache))
1356 ucache))
1357 if opts['files_with_matches']:
1357 if opts['files_with_matches']:
1358 c = (fn, rev)
1358 c = (fn, rev)
1359 if c in filerevmatches:
1359 if c in filerevmatches:
1360 continue
1360 continue
1361 filerevmatches[c] = 1
1361 filerevmatches[c] = 1
1362 else:
1362 else:
1363 cols.append(l.line)
1363 cols.append(l.line)
1364 ui.write(sep.join(cols), eol)
1364 ui.write(sep.join(cols), eol)
1365 counts[change] += 1
1365 counts[change] += 1
1366 return counts['+'], counts['-']
1366 return counts['+'], counts['-']
1367
1367
1368 fstate = {}
1368 fstate = {}
1369 skip = {}
1369 skip = {}
1370 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1370 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1371 count = 0
1371 count = 0
1372 incrementing = False
1372 incrementing = False
1373 for st, rev, fns in changeiter:
1373 for st, rev, fns in changeiter:
1374 if st == 'window':
1374 if st == 'window':
1375 incrementing = rev
1375 incrementing = rev
1376 matches.clear()
1376 matches.clear()
1377 elif st == 'add':
1377 elif st == 'add':
1378 change = repo.changelog.read(repo.lookup(str(rev)))
1378 change = repo.changelog.read(repo.lookup(str(rev)))
1379 mf = repo.manifest.read(change[0])
1379 mf = repo.manifest.read(change[0])
1380 matches[rev] = {}
1380 matches[rev] = {}
1381 for fn in fns:
1381 for fn in fns:
1382 if fn in skip:
1382 if fn in skip:
1383 continue
1383 continue
1384 fstate.setdefault(fn, {})
1384 fstate.setdefault(fn, {})
1385 try:
1385 try:
1386 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1386 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1387 except KeyError:
1387 except KeyError:
1388 pass
1388 pass
1389 elif st == 'iter':
1389 elif st == 'iter':
1390 states = matches[rev].items()
1390 states = matches[rev].items()
1391 states.sort()
1391 states.sort()
1392 for fn, m in states:
1392 for fn, m in states:
1393 if fn in skip:
1393 if fn in skip:
1394 continue
1394 continue
1395 if incrementing or not opts['all'] or fstate[fn]:
1395 if incrementing or not opts['all'] or fstate[fn]:
1396 pos, neg = display(fn, rev, m, fstate[fn])
1396 pos, neg = display(fn, rev, m, fstate[fn])
1397 count += pos + neg
1397 count += pos + neg
1398 if pos and not opts['all']:
1398 if pos and not opts['all']:
1399 skip[fn] = True
1399 skip[fn] = True
1400 fstate[fn] = m
1400 fstate[fn] = m
1401 prev[fn] = rev
1401 prev[fn] = rev
1402
1402
1403 if not incrementing:
1403 if not incrementing:
1404 fstate = fstate.items()
1404 fstate = fstate.items()
1405 fstate.sort()
1405 fstate.sort()
1406 for fn, state in fstate:
1406 for fn, state in fstate:
1407 if fn in skip:
1407 if fn in skip:
1408 continue
1408 continue
1409 display(fn, rev, {}, state)
1409 display(fn, rev, {}, state)
1410 return (count == 0 and 1) or 0
1410 return (count == 0 and 1) or 0
1411
1411
1412 def heads(ui, repo, **opts):
1412 def heads(ui, repo, **opts):
1413 """show current repository heads
1413 """show current repository heads
1414
1414
1415 Show all repository head changesets.
1415 Show all repository head changesets.
1416
1416
1417 Repository "heads" are changesets that don't have children
1417 Repository "heads" are changesets that don't have children
1418 changesets. They are where development generally takes place and
1418 changesets. They are where development generally takes place and
1419 are the usual targets for update and merge operations.
1419 are the usual targets for update and merge operations.
1420 """
1420 """
1421 if opts['rev']:
1421 if opts['rev']:
1422 heads = repo.heads(repo.lookup(opts['rev']))
1422 heads = repo.heads(repo.lookup(opts['rev']))
1423 else:
1423 else:
1424 heads = repo.heads()
1424 heads = repo.heads()
1425 br = None
1425 br = None
1426 if opts['branches']:
1426 if opts['branches']:
1427 br = repo.branchlookup(heads)
1427 br = repo.branchlookup(heads)
1428 for n in heads:
1428 for n in heads:
1429 show_changeset(ui, repo, changenode=n, brinfo=br)
1429 show_changeset(ui, repo, changenode=n, brinfo=br)
1430
1430
1431 def identify(ui, repo):
1431 def identify(ui, repo):
1432 """print information about the working copy
1432 """print information about the working copy
1433
1433
1434 Print a short summary of the current state of the repo.
1434 Print a short summary of the current state of the repo.
1435
1435
1436 This summary identifies the repository state using one or two parent
1436 This summary identifies the repository state using one or two parent
1437 hash identifiers, followed by a "+" if there are uncommitted changes
1437 hash identifiers, followed by a "+" if there are uncommitted changes
1438 in the working directory, followed by a list of tags for this revision.
1438 in the working directory, followed by a list of tags for this revision.
1439 """
1439 """
1440 parents = [p for p in repo.dirstate.parents() if p != nullid]
1440 parents = [p for p in repo.dirstate.parents() if p != nullid]
1441 if not parents:
1441 if not parents:
1442 ui.write(_("unknown\n"))
1442 ui.write(_("unknown\n"))
1443 return
1443 return
1444
1444
1445 hexfunc = ui.verbose and hex or short
1445 hexfunc = ui.verbose and hex or short
1446 modified, added, removed, deleted, unknown = repo.changes()
1446 modified, added, removed, deleted, unknown = repo.changes()
1447 output = ["%s%s" %
1447 output = ["%s%s" %
1448 ('+'.join([hexfunc(parent) for parent in parents]),
1448 ('+'.join([hexfunc(parent) for parent in parents]),
1449 (modified or added or removed or deleted) and "+" or "")]
1449 (modified or added or removed or deleted) and "+" or "")]
1450
1450
1451 if not ui.quiet:
1451 if not ui.quiet:
1452 # multiple tags for a single parent separated by '/'
1452 # multiple tags for a single parent separated by '/'
1453 parenttags = ['/'.join(tags)
1453 parenttags = ['/'.join(tags)
1454 for tags in map(repo.nodetags, parents) if tags]
1454 for tags in map(repo.nodetags, parents) if tags]
1455 # tags for multiple parents separated by ' + '
1455 # tags for multiple parents separated by ' + '
1456 if parenttags:
1456 if parenttags:
1457 output.append(' + '.join(parenttags))
1457 output.append(' + '.join(parenttags))
1458
1458
1459 ui.write("%s\n" % ' '.join(output))
1459 ui.write("%s\n" % ' '.join(output))
1460
1460
1461 def import_(ui, repo, patch1, *patches, **opts):
1461 def import_(ui, repo, patch1, *patches, **opts):
1462 """import an ordered set of patches
1462 """import an ordered set of patches
1463
1463
1464 Import a list of patches and commit them individually.
1464 Import a list of patches and commit them individually.
1465
1465
1466 If there are outstanding changes in the working directory, import
1466 If there are outstanding changes in the working directory, import
1467 will abort unless given the -f flag.
1467 will abort unless given the -f flag.
1468
1468
1469 If a patch looks like a mail message (its first line starts with
1469 If a patch looks like a mail message (its first line starts with
1470 "From " or looks like an RFC822 header), it will not be applied
1470 "From " or looks like an RFC822 header), it will not be applied
1471 unless the -f option is used. The importer neither parses nor
1471 unless the -f option is used. The importer neither parses nor
1472 discards mail headers, so use -f only to override the "mailness"
1472 discards mail headers, so use -f only to override the "mailness"
1473 safety check, not to import a real mail message.
1473 safety check, not to import a real mail message.
1474 """
1474 """
1475 patches = (patch1,) + patches
1475 patches = (patch1,) + patches
1476
1476
1477 if not opts['force']:
1477 if not opts['force']:
1478 modified, added, removed, deleted, unknown = repo.changes()
1478 modified, added, removed, deleted, unknown = repo.changes()
1479 if modified or added or removed or deleted:
1479 if modified or added or removed or deleted:
1480 raise util.Abort(_("outstanding uncommitted changes"))
1480 raise util.Abort(_("outstanding uncommitted changes"))
1481
1481
1482 d = opts["base"]
1482 d = opts["base"]
1483 strip = opts["strip"]
1483 strip = opts["strip"]
1484
1484
1485 mailre = re.compile(r'(?:From |[\w-]+:)')
1485 mailre = re.compile(r'(?:From |[\w-]+:)')
1486
1486
1487 # attempt to detect the start of a patch
1487 # attempt to detect the start of a patch
1488 # (this heuristic is borrowed from quilt)
1488 # (this heuristic is borrowed from quilt)
1489 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1489 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1490 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1490 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1491 '(---|\*\*\*)[ \t])')
1491 '(---|\*\*\*)[ \t])')
1492
1492
1493 for patch in patches:
1493 for patch in patches:
1494 ui.status(_("applying %s\n") % patch)
1494 ui.status(_("applying %s\n") % patch)
1495 pf = os.path.join(d, patch)
1495 pf = os.path.join(d, patch)
1496
1496
1497 message = []
1497 message = []
1498 user = None
1498 user = None
1499 hgpatch = False
1499 hgpatch = False
1500 for line in file(pf):
1500 for line in file(pf):
1501 line = line.rstrip()
1501 line = line.rstrip()
1502 if (not message and not hgpatch and
1502 if (not message and not hgpatch and
1503 mailre.match(line) and not opts['force']):
1503 mailre.match(line) and not opts['force']):
1504 if len(line) > 35:
1504 if len(line) > 35:
1505 line = line[:32] + '...'
1505 line = line[:32] + '...'
1506 raise util.Abort(_('first line looks like a '
1506 raise util.Abort(_('first line looks like a '
1507 'mail header: ') + line)
1507 'mail header: ') + line)
1508 if diffre.match(line):
1508 if diffre.match(line):
1509 break
1509 break
1510 elif hgpatch:
1510 elif hgpatch:
1511 # parse values when importing the result of an hg export
1511 # parse values when importing the result of an hg export
1512 if line.startswith("# User "):
1512 if line.startswith("# User "):
1513 user = line[7:]
1513 user = line[7:]
1514 ui.debug(_('User: %s\n') % user)
1514 ui.debug(_('User: %s\n') % user)
1515 elif not line.startswith("# ") and line:
1515 elif not line.startswith("# ") and line:
1516 message.append(line)
1516 message.append(line)
1517 hgpatch = False
1517 hgpatch = False
1518 elif line == '# HG changeset patch':
1518 elif line == '# HG changeset patch':
1519 hgpatch = True
1519 hgpatch = True
1520 message = [] # We may have collected garbage
1520 message = [] # We may have collected garbage
1521 else:
1521 else:
1522 message.append(line)
1522 message.append(line)
1523
1523
1524 # make sure message isn't empty
1524 # make sure message isn't empty
1525 if not message:
1525 if not message:
1526 message = _("imported patch %s\n") % patch
1526 message = _("imported patch %s\n") % patch
1527 else:
1527 else:
1528 message = "%s\n" % '\n'.join(message)
1528 message = "%s\n" % '\n'.join(message)
1529 ui.debug(_('message:\n%s\n') % message)
1529 ui.debug(_('message:\n%s\n') % message)
1530
1530
1531 files = util.patch(strip, pf, ui)
1531 files = util.patch(strip, pf, ui)
1532
1532
1533 if len(files) > 0:
1533 if len(files) > 0:
1534 addremove(ui, repo, *files)
1534 addremove(ui, repo, *files)
1535 repo.commit(files, message, user)
1535 repo.commit(files, message, user)
1536
1536
1537 def incoming(ui, repo, source="default", **opts):
1537 def incoming(ui, repo, source="default", **opts):
1538 """show new changesets found in source
1538 """show new changesets found in source
1539
1539
1540 Show new changesets found in the specified repo or the default
1540 Show new changesets found in the specified repo or the default
1541 pull repo. These are the changesets that would be pulled if a pull
1541 pull repo. These are the changesets that would be pulled if a pull
1542 was requested.
1542 was requested.
1543
1543
1544 Currently only local repositories are supported.
1544 Currently only local repositories are supported.
1545 """
1545 """
1546 source = ui.expandpath(source, repo.root)
1546 source = ui.expandpath(source)
1547 other = hg.repository(ui, source)
1547 other = hg.repository(ui, source)
1548 if not other.local():
1548 if not other.local():
1549 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1549 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1550 o = repo.findincoming(other)
1550 o = repo.findincoming(other)
1551 if not o:
1551 if not o:
1552 return
1552 return
1553 o = other.changelog.nodesbetween(o)[0]
1553 o = other.changelog.nodesbetween(o)[0]
1554 if opts['newest_first']:
1554 if opts['newest_first']:
1555 o.reverse()
1555 o.reverse()
1556 for n in o:
1556 for n in o:
1557 parents = [p for p in other.changelog.parents(n) if p != nullid]
1557 parents = [p for p in other.changelog.parents(n) if p != nullid]
1558 if opts['no_merges'] and len(parents) == 2:
1558 if opts['no_merges'] and len(parents) == 2:
1559 continue
1559 continue
1560 show_changeset(ui, other, changenode=n)
1560 show_changeset(ui, other, changenode=n)
1561 if opts['patch']:
1561 if opts['patch']:
1562 prev = (parents and parents[0]) or nullid
1562 prev = (parents and parents[0]) or nullid
1563 dodiff(ui, ui, other, prev, n)
1563 dodiff(ui, ui, other, prev, n)
1564 ui.write("\n")
1564 ui.write("\n")
1565
1565
1566 def init(ui, dest="."):
1566 def init(ui, dest="."):
1567 """create a new repository in the given directory
1567 """create a new repository in the given directory
1568
1568
1569 Initialize a new repository in the given directory. If the given
1569 Initialize a new repository in the given directory. If the given
1570 directory does not exist, it is created.
1570 directory does not exist, it is created.
1571
1571
1572 If no directory is given, the current directory is used.
1572 If no directory is given, the current directory is used.
1573 """
1573 """
1574 if not os.path.exists(dest):
1574 if not os.path.exists(dest):
1575 os.mkdir(dest)
1575 os.mkdir(dest)
1576 hg.repository(ui, dest, create=1)
1576 hg.repository(ui, dest, create=1)
1577
1577
1578 def locate(ui, repo, *pats, **opts):
1578 def locate(ui, repo, *pats, **opts):
1579 """locate files matching specific patterns
1579 """locate files matching specific patterns
1580
1580
1581 Print all files under Mercurial control whose names match the
1581 Print all files under Mercurial control whose names match the
1582 given patterns.
1582 given patterns.
1583
1583
1584 This command searches the current directory and its
1584 This command searches the current directory and its
1585 subdirectories. To search an entire repository, move to the root
1585 subdirectories. To search an entire repository, move to the root
1586 of the repository.
1586 of the repository.
1587
1587
1588 If no patterns are given to match, this command prints all file
1588 If no patterns are given to match, this command prints all file
1589 names.
1589 names.
1590
1590
1591 If you want to feed the output of this command into the "xargs"
1591 If you want to feed the output of this command into the "xargs"
1592 command, use the "-0" option to both this command and "xargs".
1592 command, use the "-0" option to both this command and "xargs".
1593 This will avoid the problem of "xargs" treating single filenames
1593 This will avoid the problem of "xargs" treating single filenames
1594 that contain white space as multiple filenames.
1594 that contain white space as multiple filenames.
1595 """
1595 """
1596 end = opts['print0'] and '\0' or '\n'
1596 end = opts['print0'] and '\0' or '\n'
1597 rev = opts['rev']
1597 rev = opts['rev']
1598 if rev:
1598 if rev:
1599 node = repo.lookup(rev)
1599 node = repo.lookup(rev)
1600 else:
1600 else:
1601 node = None
1601 node = None
1602
1602
1603 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1603 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1604 head='(?:.*/|)'):
1604 head='(?:.*/|)'):
1605 if not node and repo.dirstate.state(abs) == '?':
1605 if not node and repo.dirstate.state(abs) == '?':
1606 continue
1606 continue
1607 if opts['fullpath']:
1607 if opts['fullpath']:
1608 ui.write(os.path.join(repo.root, abs), end)
1608 ui.write(os.path.join(repo.root, abs), end)
1609 else:
1609 else:
1610 ui.write(((pats and rel) or abs), end)
1610 ui.write(((pats and rel) or abs), end)
1611
1611
1612 def log(ui, repo, *pats, **opts):
1612 def log(ui, repo, *pats, **opts):
1613 """show revision history of entire repository or files
1613 """show revision history of entire repository or files
1614
1614
1615 Print the revision history of the specified files or the entire project.
1615 Print the revision history of the specified files or the entire project.
1616
1616
1617 By default this command outputs: changeset id and hash, tags,
1617 By default this command outputs: changeset id and hash, tags,
1618 non-trivial parents, user, date and time, and a summary for each
1618 non-trivial parents, user, date and time, and a summary for each
1619 commit. When the -v/--verbose switch is used, the list of changed
1619 commit. When the -v/--verbose switch is used, the list of changed
1620 files and full commit message is shown.
1620 files and full commit message is shown.
1621 """
1621 """
1622 class dui(object):
1622 class dui(object):
1623 # Implement and delegate some ui protocol. Save hunks of
1623 # Implement and delegate some ui protocol. Save hunks of
1624 # output for later display in the desired order.
1624 # output for later display in the desired order.
1625 def __init__(self, ui):
1625 def __init__(self, ui):
1626 self.ui = ui
1626 self.ui = ui
1627 self.hunk = {}
1627 self.hunk = {}
1628 def bump(self, rev):
1628 def bump(self, rev):
1629 self.rev = rev
1629 self.rev = rev
1630 self.hunk[rev] = []
1630 self.hunk[rev] = []
1631 def note(self, *args):
1631 def note(self, *args):
1632 if self.verbose:
1632 if self.verbose:
1633 self.write(*args)
1633 self.write(*args)
1634 def status(self, *args):
1634 def status(self, *args):
1635 if not self.quiet:
1635 if not self.quiet:
1636 self.write(*args)
1636 self.write(*args)
1637 def write(self, *args):
1637 def write(self, *args):
1638 self.hunk[self.rev].append(args)
1638 self.hunk[self.rev].append(args)
1639 def debug(self, *args):
1639 def debug(self, *args):
1640 if self.debugflag:
1640 if self.debugflag:
1641 self.write(*args)
1641 self.write(*args)
1642 def __getattr__(self, key):
1642 def __getattr__(self, key):
1643 return getattr(self.ui, key)
1643 return getattr(self.ui, key)
1644
1644
1645 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1645 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1646
1646
1647 if opts['limit']:
1647 if opts['limit']:
1648 try:
1648 try:
1649 limit = int(opts['limit'])
1649 limit = int(opts['limit'])
1650 except ValueError:
1650 except ValueError:
1651 raise util.Abort(_('limit must be a positive integer'))
1651 raise util.Abort(_('limit must be a positive integer'))
1652 if limit <= 0: raise util.Abort(_('limit must be positive'))
1652 if limit <= 0: raise util.Abort(_('limit must be positive'))
1653 else:
1653 else:
1654 limit = sys.maxint
1654 limit = sys.maxint
1655 count = 0
1655 count = 0
1656
1656
1657 for st, rev, fns in changeiter:
1657 for st, rev, fns in changeiter:
1658 if st == 'window':
1658 if st == 'window':
1659 du = dui(ui)
1659 du = dui(ui)
1660 elif st == 'add':
1660 elif st == 'add':
1661 du.bump(rev)
1661 du.bump(rev)
1662 changenode = repo.changelog.node(rev)
1662 changenode = repo.changelog.node(rev)
1663 parents = [p for p in repo.changelog.parents(changenode)
1663 parents = [p for p in repo.changelog.parents(changenode)
1664 if p != nullid]
1664 if p != nullid]
1665 if opts['no_merges'] and len(parents) == 2:
1665 if opts['no_merges'] and len(parents) == 2:
1666 continue
1666 continue
1667 if opts['only_merges'] and len(parents) != 2:
1667 if opts['only_merges'] and len(parents) != 2:
1668 continue
1668 continue
1669
1669
1670 if opts['keyword']:
1670 if opts['keyword']:
1671 changes = getchange(rev)
1671 changes = getchange(rev)
1672 miss = 0
1672 miss = 0
1673 for k in [kw.lower() for kw in opts['keyword']]:
1673 for k in [kw.lower() for kw in opts['keyword']]:
1674 if not (k in changes[1].lower() or
1674 if not (k in changes[1].lower() or
1675 k in changes[4].lower() or
1675 k in changes[4].lower() or
1676 k in " ".join(changes[3][:20]).lower()):
1676 k in " ".join(changes[3][:20]).lower()):
1677 miss = 1
1677 miss = 1
1678 break
1678 break
1679 if miss:
1679 if miss:
1680 continue
1680 continue
1681
1681
1682 br = None
1682 br = None
1683 if opts['branches']:
1683 if opts['branches']:
1684 br = repo.branchlookup([repo.changelog.node(rev)])
1684 br = repo.branchlookup([repo.changelog.node(rev)])
1685
1685
1686 show_changeset(du, repo, rev, brinfo=br)
1686 show_changeset(du, repo, rev, brinfo=br)
1687 if opts['patch']:
1687 if opts['patch']:
1688 prev = (parents and parents[0]) or nullid
1688 prev = (parents and parents[0]) or nullid
1689 dodiff(du, du, repo, prev, changenode, match=matchfn)
1689 dodiff(du, du, repo, prev, changenode, match=matchfn)
1690 du.write("\n\n")
1690 du.write("\n\n")
1691 elif st == 'iter':
1691 elif st == 'iter':
1692 if count == limit: break
1692 if count == limit: break
1693 if du.hunk[rev]:
1693 if du.hunk[rev]:
1694 count += 1
1694 count += 1
1695 for args in du.hunk[rev]:
1695 for args in du.hunk[rev]:
1696 ui.write(*args)
1696 ui.write(*args)
1697
1697
1698 def manifest(ui, repo, rev=None):
1698 def manifest(ui, repo, rev=None):
1699 """output the latest or given revision of the project manifest
1699 """output the latest or given revision of the project manifest
1700
1700
1701 Print a list of version controlled files for the given revision.
1701 Print a list of version controlled files for the given revision.
1702
1702
1703 The manifest is the list of files being version controlled. If no revision
1703 The manifest is the list of files being version controlled. If no revision
1704 is given then the tip is used.
1704 is given then the tip is used.
1705 """
1705 """
1706 if rev:
1706 if rev:
1707 try:
1707 try:
1708 # assume all revision numbers are for changesets
1708 # assume all revision numbers are for changesets
1709 n = repo.lookup(rev)
1709 n = repo.lookup(rev)
1710 change = repo.changelog.read(n)
1710 change = repo.changelog.read(n)
1711 n = change[0]
1711 n = change[0]
1712 except hg.RepoError:
1712 except hg.RepoError:
1713 n = repo.manifest.lookup(rev)
1713 n = repo.manifest.lookup(rev)
1714 else:
1714 else:
1715 n = repo.manifest.tip()
1715 n = repo.manifest.tip()
1716 m = repo.manifest.read(n)
1716 m = repo.manifest.read(n)
1717 mf = repo.manifest.readflags(n)
1717 mf = repo.manifest.readflags(n)
1718 files = m.keys()
1718 files = m.keys()
1719 files.sort()
1719 files.sort()
1720
1720
1721 for f in files:
1721 for f in files:
1722 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1722 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1723
1723
1724 def outgoing(ui, repo, dest="default-push", **opts):
1724 def outgoing(ui, repo, dest="default-push", **opts):
1725 """show changesets not found in destination
1725 """show changesets not found in destination
1726
1726
1727 Show changesets not found in the specified destination repo or the
1727 Show changesets not found in the specified destination repo or the
1728 default push repo. These are the changesets that would be pushed
1728 default push repo. These are the changesets that would be pushed
1729 if a push was requested.
1729 if a push was requested.
1730
1730
1731 See pull for valid source format details.
1731 See pull for valid source format details.
1732 """
1732 """
1733 dest = ui.expandpath(dest, repo.root)
1733 dest = ui.expandpath(dest)
1734 other = hg.repository(ui, dest)
1734 other = hg.repository(ui, dest)
1735 o = repo.findoutgoing(other)
1735 o = repo.findoutgoing(other)
1736 o = repo.changelog.nodesbetween(o)[0]
1736 o = repo.changelog.nodesbetween(o)[0]
1737 if opts['newest_first']:
1737 if opts['newest_first']:
1738 o.reverse()
1738 o.reverse()
1739 for n in o:
1739 for n in o:
1740 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1740 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1741 if opts['no_merges'] and len(parents) == 2:
1741 if opts['no_merges'] and len(parents) == 2:
1742 continue
1742 continue
1743 show_changeset(ui, repo, changenode=n)
1743 show_changeset(ui, repo, changenode=n)
1744 if opts['patch']:
1744 if opts['patch']:
1745 prev = (parents and parents[0]) or nullid
1745 prev = (parents and parents[0]) or nullid
1746 dodiff(ui, ui, repo, prev, n)
1746 dodiff(ui, ui, repo, prev, n)
1747 ui.write("\n")
1747 ui.write("\n")
1748
1748
1749 def parents(ui, repo, rev=None, branches=None):
1749 def parents(ui, repo, rev=None, branches=None):
1750 """show the parents of the working dir or revision
1750 """show the parents of the working dir or revision
1751
1751
1752 Print the working directory's parent revisions.
1752 Print the working directory's parent revisions.
1753 """
1753 """
1754 if rev:
1754 if rev:
1755 p = repo.changelog.parents(repo.lookup(rev))
1755 p = repo.changelog.parents(repo.lookup(rev))
1756 else:
1756 else:
1757 p = repo.dirstate.parents()
1757 p = repo.dirstate.parents()
1758
1758
1759 br = None
1759 br = None
1760 if branches is not None:
1760 if branches is not None:
1761 br = repo.branchlookup(p)
1761 br = repo.branchlookup(p)
1762 for n in p:
1762 for n in p:
1763 if n != nullid:
1763 if n != nullid:
1764 show_changeset(ui, repo, changenode=n, brinfo=br)
1764 show_changeset(ui, repo, changenode=n, brinfo=br)
1765
1765
1766 def paths(ui, repo, search=None):
1766 def paths(ui, repo, search=None):
1767 """show definition of symbolic path names
1767 """show definition of symbolic path names
1768
1768
1769 Show definition of symbolic path name NAME. If no name is given, show
1769 Show definition of symbolic path name NAME. If no name is given, show
1770 definition of available names.
1770 definition of available names.
1771
1771
1772 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1772 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1773 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1773 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1774 """
1774 """
1775 if search:
1775 if search:
1776 for name, path in ui.configitems("paths"):
1776 for name, path in ui.configitems("paths"):
1777 if name == search:
1777 if name == search:
1778 ui.write("%s\n" % path)
1778 ui.write("%s\n" % path)
1779 return
1779 return
1780 ui.warn(_("not found!\n"))
1780 ui.warn(_("not found!\n"))
1781 return 1
1781 return 1
1782 else:
1782 else:
1783 for name, path in ui.configitems("paths"):
1783 for name, path in ui.configitems("paths"):
1784 ui.write("%s = %s\n" % (name, path))
1784 ui.write("%s = %s\n" % (name, path))
1785
1785
1786 def pull(ui, repo, source="default", **opts):
1786 def pull(ui, repo, source="default", **opts):
1787 """pull changes from the specified source
1787 """pull changes from the specified source
1788
1788
1789 Pull changes from a remote repository to a local one.
1789 Pull changes from a remote repository to a local one.
1790
1790
1791 This finds all changes from the repository at the specified path
1791 This finds all changes from the repository at the specified path
1792 or URL and adds them to the local repository. By default, this
1792 or URL and adds them to the local repository. By default, this
1793 does not update the copy of the project in the working directory.
1793 does not update the copy of the project in the working directory.
1794
1794
1795 Valid URLs are of the form:
1795 Valid URLs are of the form:
1796
1796
1797 local/filesystem/path
1797 local/filesystem/path
1798 http://[user@]host[:port][/path]
1798 http://[user@]host[:port][/path]
1799 https://[user@]host[:port][/path]
1799 https://[user@]host[:port][/path]
1800 ssh://[user@]host[:port][/path]
1800 ssh://[user@]host[:port][/path]
1801
1801
1802 SSH requires an accessible shell account on the destination machine
1802 SSH requires an accessible shell account on the destination machine
1803 and a copy of hg in the remote path. With SSH, paths are relative
1803 and a copy of hg in the remote path. With SSH, paths are relative
1804 to the remote user's home directory by default; use two slashes at
1804 to the remote user's home directory by default; use two slashes at
1805 the start of a path to specify it as relative to the filesystem root.
1805 the start of a path to specify it as relative to the filesystem root.
1806 """
1806 """
1807 source = ui.expandpath(source, repo.root)
1807 source = ui.expandpath(source)
1808 ui.status(_('pulling from %s\n') % (source))
1808 ui.status(_('pulling from %s\n') % (source))
1809
1809
1810 if opts['ssh']:
1810 if opts['ssh']:
1811 ui.setconfig("ui", "ssh", opts['ssh'])
1811 ui.setconfig("ui", "ssh", opts['ssh'])
1812 if opts['remotecmd']:
1812 if opts['remotecmd']:
1813 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1813 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1814
1814
1815 other = hg.repository(ui, source)
1815 other = hg.repository(ui, source)
1816 revs = None
1816 revs = None
1817 if opts['rev'] and not other.local():
1817 if opts['rev'] and not other.local():
1818 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
1818 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
1819 elif opts['rev']:
1819 elif opts['rev']:
1820 revs = [other.lookup(rev) for rev in opts['rev']]
1820 revs = [other.lookup(rev) for rev in opts['rev']]
1821 r = repo.pull(other, heads=revs)
1821 r = repo.pull(other, heads=revs)
1822 if not r:
1822 if not r:
1823 if opts['update']:
1823 if opts['update']:
1824 return update(ui, repo)
1824 return update(ui, repo)
1825 else:
1825 else:
1826 ui.status(_("(run 'hg update' to get a working copy)\n"))
1826 ui.status(_("(run 'hg update' to get a working copy)\n"))
1827
1827
1828 return r
1828 return r
1829
1829
1830 def push(ui, repo, dest="default-push", **opts):
1830 def push(ui, repo, dest="default-push", **opts):
1831 """push changes to the specified destination
1831 """push changes to the specified destination
1832
1832
1833 Push changes from the local repository to the given destination.
1833 Push changes from the local repository to the given destination.
1834
1834
1835 This is the symmetrical operation for pull. It helps to move
1835 This is the symmetrical operation for pull. It helps to move
1836 changes from the current repository to a different one. If the
1836 changes from the current repository to a different one. If the
1837 destination is local this is identical to a pull in that directory
1837 destination is local this is identical to a pull in that directory
1838 from the current one.
1838 from the current one.
1839
1839
1840 By default, push will refuse to run if it detects the result would
1840 By default, push will refuse to run if it detects the result would
1841 increase the number of remote heads. This generally indicates the
1841 increase the number of remote heads. This generally indicates the
1842 the client has forgotten to sync and merge before pushing.
1842 the client has forgotten to sync and merge before pushing.
1843
1843
1844 Valid URLs are of the form:
1844 Valid URLs are of the form:
1845
1845
1846 local/filesystem/path
1846 local/filesystem/path
1847 ssh://[user@]host[:port][/path]
1847 ssh://[user@]host[:port][/path]
1848
1848
1849 SSH requires an accessible shell account on the destination
1849 SSH requires an accessible shell account on the destination
1850 machine and a copy of hg in the remote path.
1850 machine and a copy of hg in the remote path.
1851 """
1851 """
1852 dest = ui.expandpath(dest, repo.root)
1852 dest = ui.expandpath(dest)
1853 ui.status('pushing to %s\n' % (dest))
1853 ui.status('pushing to %s\n' % (dest))
1854
1854
1855 if opts['ssh']:
1855 if opts['ssh']:
1856 ui.setconfig("ui", "ssh", opts['ssh'])
1856 ui.setconfig("ui", "ssh", opts['ssh'])
1857 if opts['remotecmd']:
1857 if opts['remotecmd']:
1858 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1858 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1859
1859
1860 other = hg.repository(ui, dest)
1860 other = hg.repository(ui, dest)
1861 revs = None
1861 revs = None
1862 if opts['rev']:
1862 if opts['rev']:
1863 revs = [repo.lookup(rev) for rev in opts['rev']]
1863 revs = [repo.lookup(rev) for rev in opts['rev']]
1864 r = repo.push(other, opts['force'], revs=revs)
1864 r = repo.push(other, opts['force'], revs=revs)
1865 return r
1865 return r
1866
1866
1867 def rawcommit(ui, repo, *flist, **rc):
1867 def rawcommit(ui, repo, *flist, **rc):
1868 """raw commit interface (DEPRECATED)
1868 """raw commit interface (DEPRECATED)
1869
1869
1870 (DEPRECATED)
1870 (DEPRECATED)
1871 Lowlevel commit, for use in helper scripts.
1871 Lowlevel commit, for use in helper scripts.
1872
1872
1873 This command is not intended to be used by normal users, as it is
1873 This command is not intended to be used by normal users, as it is
1874 primarily useful for importing from other SCMs.
1874 primarily useful for importing from other SCMs.
1875
1875
1876 This command is now deprecated and will be removed in a future
1876 This command is now deprecated and will be removed in a future
1877 release, please use debugsetparents and commit instead.
1877 release, please use debugsetparents and commit instead.
1878 """
1878 """
1879
1879
1880 ui.warn(_("(the rawcommit command is deprecated)\n"))
1880 ui.warn(_("(the rawcommit command is deprecated)\n"))
1881
1881
1882 message = rc['message']
1882 message = rc['message']
1883 if not message and rc['logfile']:
1883 if not message and rc['logfile']:
1884 try:
1884 try:
1885 message = open(rc['logfile']).read()
1885 message = open(rc['logfile']).read()
1886 except IOError:
1886 except IOError:
1887 pass
1887 pass
1888 if not message and not rc['logfile']:
1888 if not message and not rc['logfile']:
1889 raise util.Abort(_("missing commit message"))
1889 raise util.Abort(_("missing commit message"))
1890
1890
1891 files = relpath(repo, list(flist))
1891 files = relpath(repo, list(flist))
1892 if rc['files']:
1892 if rc['files']:
1893 files += open(rc['files']).read().splitlines()
1893 files += open(rc['files']).read().splitlines()
1894
1894
1895 rc['parent'] = map(repo.lookup, rc['parent'])
1895 rc['parent'] = map(repo.lookup, rc['parent'])
1896
1896
1897 try:
1897 try:
1898 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1898 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1899 except ValueError, inst:
1899 except ValueError, inst:
1900 raise util.Abort(str(inst))
1900 raise util.Abort(str(inst))
1901
1901
1902 def recover(ui, repo):
1902 def recover(ui, repo):
1903 """roll back an interrupted transaction
1903 """roll back an interrupted transaction
1904
1904
1905 Recover from an interrupted commit or pull.
1905 Recover from an interrupted commit or pull.
1906
1906
1907 This command tries to fix the repository status after an interrupted
1907 This command tries to fix the repository status after an interrupted
1908 operation. It should only be necessary when Mercurial suggests it.
1908 operation. It should only be necessary when Mercurial suggests it.
1909 """
1909 """
1910 if repo.recover():
1910 if repo.recover():
1911 return repo.verify()
1911 return repo.verify()
1912 return False
1912 return False
1913
1913
1914 def remove(ui, repo, pat, *pats, **opts):
1914 def remove(ui, repo, pat, *pats, **opts):
1915 """remove the specified files on the next commit
1915 """remove the specified files on the next commit
1916
1916
1917 Schedule the indicated files for removal from the repository.
1917 Schedule the indicated files for removal from the repository.
1918
1918
1919 This command schedules the files to be removed at the next commit.
1919 This command schedules the files to be removed at the next commit.
1920 This only removes files from the current branch, not from the
1920 This only removes files from the current branch, not from the
1921 entire project history. If the files still exist in the working
1921 entire project history. If the files still exist in the working
1922 directory, they will be deleted from it.
1922 directory, they will be deleted from it.
1923 """
1923 """
1924 names = []
1924 names = []
1925 def okaytoremove(abs, rel, exact):
1925 def okaytoremove(abs, rel, exact):
1926 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
1926 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
1927 reason = None
1927 reason = None
1928 if modified and not opts['force']:
1928 if modified and not opts['force']:
1929 reason = _('is modified')
1929 reason = _('is modified')
1930 elif added:
1930 elif added:
1931 reason = _('has been marked for add')
1931 reason = _('has been marked for add')
1932 elif unknown:
1932 elif unknown:
1933 reason = _('is not managed')
1933 reason = _('is not managed')
1934 if reason:
1934 if reason:
1935 if exact:
1935 if exact:
1936 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1936 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1937 else:
1937 else:
1938 return True
1938 return True
1939 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1939 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1940 if okaytoremove(abs, rel, exact):
1940 if okaytoremove(abs, rel, exact):
1941 if ui.verbose or not exact:
1941 if ui.verbose or not exact:
1942 ui.status(_('removing %s\n') % rel)
1942 ui.status(_('removing %s\n') % rel)
1943 names.append(abs)
1943 names.append(abs)
1944 repo.remove(names, unlink=True)
1944 repo.remove(names, unlink=True)
1945
1945
1946 def rename(ui, repo, *pats, **opts):
1946 def rename(ui, repo, *pats, **opts):
1947 """rename files; equivalent of copy + remove
1947 """rename files; equivalent of copy + remove
1948
1948
1949 Mark dest as copies of sources; mark sources for deletion. If
1949 Mark dest as copies of sources; mark sources for deletion. If
1950 dest is a directory, copies are put in that directory. If dest is
1950 dest is a directory, copies are put in that directory. If dest is
1951 a file, there can only be one source.
1951 a file, there can only be one source.
1952
1952
1953 By default, this command copies the contents of files as they
1953 By default, this command copies the contents of files as they
1954 stand in the working directory. If invoked with --after, the
1954 stand in the working directory. If invoked with --after, the
1955 operation is recorded, but no copying is performed.
1955 operation is recorded, but no copying is performed.
1956
1956
1957 This command takes effect in the next commit.
1957 This command takes effect in the next commit.
1958
1958
1959 NOTE: This command should be treated as experimental. While it
1959 NOTE: This command should be treated as experimental. While it
1960 should properly record rename files, this information is not yet
1960 should properly record rename files, this information is not yet
1961 fully used by merge, nor fully reported by log.
1961 fully used by merge, nor fully reported by log.
1962 """
1962 """
1963 try:
1963 try:
1964 wlock = repo.wlock(0)
1964 wlock = repo.wlock(0)
1965 errs, copied = docopy(ui, repo, pats, opts, wlock)
1965 errs, copied = docopy(ui, repo, pats, opts, wlock)
1966 names = []
1966 names = []
1967 for abs, rel, exact in copied:
1967 for abs, rel, exact in copied:
1968 if ui.verbose or not exact:
1968 if ui.verbose or not exact:
1969 ui.status(_('removing %s\n') % rel)
1969 ui.status(_('removing %s\n') % rel)
1970 names.append(abs)
1970 names.append(abs)
1971 repo.remove(names, True, wlock)
1971 repo.remove(names, True, wlock)
1972 except lock.LockHeld, inst:
1972 except lock.LockHeld, inst:
1973 ui.warn(_("repository lock held by %s\n") % inst.args[0])
1973 ui.warn(_("repository lock held by %s\n") % inst.args[0])
1974 errs = 1
1974 errs = 1
1975 return errs
1975 return errs
1976
1976
1977 def revert(ui, repo, *pats, **opts):
1977 def revert(ui, repo, *pats, **opts):
1978 """revert modified files or dirs back to their unmodified states
1978 """revert modified files or dirs back to their unmodified states
1979
1979
1980 In its default mode, it reverts any uncommitted modifications made
1980 In its default mode, it reverts any uncommitted modifications made
1981 to the named files or directories. This restores the contents of
1981 to the named files or directories. This restores the contents of
1982 the affected files to an unmodified state.
1982 the affected files to an unmodified state.
1983
1983
1984 Using the -r option, it reverts the given files or directories to
1984 Using the -r option, it reverts the given files or directories to
1985 their state as of an earlier revision. This can be helpful to "roll
1985 their state as of an earlier revision. This can be helpful to "roll
1986 back" some or all of a change that should not have been committed.
1986 back" some or all of a change that should not have been committed.
1987
1987
1988 Revert modifies the working directory. It does not commit any
1988 Revert modifies the working directory. It does not commit any
1989 changes, or change the parent of the current working directory.
1989 changes, or change the parent of the current working directory.
1990
1990
1991 If a file has been deleted, it is recreated. If the executable
1991 If a file has been deleted, it is recreated. If the executable
1992 mode of a file was changed, it is reset.
1992 mode of a file was changed, it is reset.
1993
1993
1994 If names are given, all files matching the names are reverted.
1994 If names are given, all files matching the names are reverted.
1995
1995
1996 If no arguments are given, all files in the repository are reverted.
1996 If no arguments are given, all files in the repository are reverted.
1997 """
1997 """
1998 node = opts['rev'] and repo.lookup(opts['rev']) or \
1998 node = opts['rev'] and repo.lookup(opts['rev']) or \
1999 repo.dirstate.parents()[0]
1999 repo.dirstate.parents()[0]
2000
2000
2001 files, choose, anypats = matchpats(repo, pats, opts)
2001 files, choose, anypats = matchpats(repo, pats, opts)
2002 modified, added, removed, deleted, unknown = repo.changes(match=choose)
2002 modified, added, removed, deleted, unknown = repo.changes(match=choose)
2003 repo.forget(added)
2003 repo.forget(added)
2004 repo.undelete(removed)
2004 repo.undelete(removed)
2005
2005
2006 return repo.update(node, False, True, choose, False)
2006 return repo.update(node, False, True, choose, False)
2007
2007
2008 def root(ui, repo):
2008 def root(ui, repo):
2009 """print the root (top) of the current working dir
2009 """print the root (top) of the current working dir
2010
2010
2011 Print the root directory of the current repository.
2011 Print the root directory of the current repository.
2012 """
2012 """
2013 ui.write(repo.root + "\n")
2013 ui.write(repo.root + "\n")
2014
2014
2015 def serve(ui, repo, **opts):
2015 def serve(ui, repo, **opts):
2016 """export the repository via HTTP
2016 """export the repository via HTTP
2017
2017
2018 Start a local HTTP repository browser and pull server.
2018 Start a local HTTP repository browser and pull server.
2019
2019
2020 By default, the server logs accesses to stdout and errors to
2020 By default, the server logs accesses to stdout and errors to
2021 stderr. Use the "-A" and "-E" options to log to files.
2021 stderr. Use the "-A" and "-E" options to log to files.
2022 """
2022 """
2023
2023
2024 if opts["stdio"]:
2024 if opts["stdio"]:
2025 fin, fout = sys.stdin, sys.stdout
2025 fin, fout = sys.stdin, sys.stdout
2026 sys.stdout = sys.stderr
2026 sys.stdout = sys.stderr
2027
2027
2028 # Prevent insertion/deletion of CRs
2028 # Prevent insertion/deletion of CRs
2029 util.set_binary(fin)
2029 util.set_binary(fin)
2030 util.set_binary(fout)
2030 util.set_binary(fout)
2031
2031
2032 def getarg():
2032 def getarg():
2033 argline = fin.readline()[:-1]
2033 argline = fin.readline()[:-1]
2034 arg, l = argline.split()
2034 arg, l = argline.split()
2035 val = fin.read(int(l))
2035 val = fin.read(int(l))
2036 return arg, val
2036 return arg, val
2037 def respond(v):
2037 def respond(v):
2038 fout.write("%d\n" % len(v))
2038 fout.write("%d\n" % len(v))
2039 fout.write(v)
2039 fout.write(v)
2040 fout.flush()
2040 fout.flush()
2041
2041
2042 lock = None
2042 lock = None
2043
2043
2044 while 1:
2044 while 1:
2045 cmd = fin.readline()[:-1]
2045 cmd = fin.readline()[:-1]
2046 if cmd == '':
2046 if cmd == '':
2047 return
2047 return
2048 if cmd == "heads":
2048 if cmd == "heads":
2049 h = repo.heads()
2049 h = repo.heads()
2050 respond(" ".join(map(hex, h)) + "\n")
2050 respond(" ".join(map(hex, h)) + "\n")
2051 if cmd == "lock":
2051 if cmd == "lock":
2052 lock = repo.lock()
2052 lock = repo.lock()
2053 respond("")
2053 respond("")
2054 if cmd == "unlock":
2054 if cmd == "unlock":
2055 if lock:
2055 if lock:
2056 lock.release()
2056 lock.release()
2057 lock = None
2057 lock = None
2058 respond("")
2058 respond("")
2059 elif cmd == "branches":
2059 elif cmd == "branches":
2060 arg, nodes = getarg()
2060 arg, nodes = getarg()
2061 nodes = map(bin, nodes.split(" "))
2061 nodes = map(bin, nodes.split(" "))
2062 r = []
2062 r = []
2063 for b in repo.branches(nodes):
2063 for b in repo.branches(nodes):
2064 r.append(" ".join(map(hex, b)) + "\n")
2064 r.append(" ".join(map(hex, b)) + "\n")
2065 respond("".join(r))
2065 respond("".join(r))
2066 elif cmd == "between":
2066 elif cmd == "between":
2067 arg, pairs = getarg()
2067 arg, pairs = getarg()
2068 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2068 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2069 r = []
2069 r = []
2070 for b in repo.between(pairs):
2070 for b in repo.between(pairs):
2071 r.append(" ".join(map(hex, b)) + "\n")
2071 r.append(" ".join(map(hex, b)) + "\n")
2072 respond("".join(r))
2072 respond("".join(r))
2073 elif cmd == "changegroup":
2073 elif cmd == "changegroup":
2074 nodes = []
2074 nodes = []
2075 arg, roots = getarg()
2075 arg, roots = getarg()
2076 nodes = map(bin, roots.split(" "))
2076 nodes = map(bin, roots.split(" "))
2077
2077
2078 cg = repo.changegroup(nodes, 'serve')
2078 cg = repo.changegroup(nodes, 'serve')
2079 while 1:
2079 while 1:
2080 d = cg.read(4096)
2080 d = cg.read(4096)
2081 if not d:
2081 if not d:
2082 break
2082 break
2083 fout.write(d)
2083 fout.write(d)
2084
2084
2085 fout.flush()
2085 fout.flush()
2086
2086
2087 elif cmd == "addchangegroup":
2087 elif cmd == "addchangegroup":
2088 if not lock:
2088 if not lock:
2089 respond("not locked")
2089 respond("not locked")
2090 continue
2090 continue
2091 respond("")
2091 respond("")
2092
2092
2093 r = repo.addchangegroup(fin)
2093 r = repo.addchangegroup(fin)
2094 respond("")
2094 respond("")
2095
2095
2096 optlist = "name templates style address port ipv6 accesslog errorlog"
2096 optlist = "name templates style address port ipv6 accesslog errorlog"
2097 for o in optlist.split():
2097 for o in optlist.split():
2098 if opts[o]:
2098 if opts[o]:
2099 ui.setconfig("web", o, opts[o])
2099 ui.setconfig("web", o, opts[o])
2100
2100
2101 if opts['daemon'] and not opts['daemon_pipefds']:
2101 if opts['daemon'] and not opts['daemon_pipefds']:
2102 rfd, wfd = os.pipe()
2102 rfd, wfd = os.pipe()
2103 args = sys.argv[:]
2103 args = sys.argv[:]
2104 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2104 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2105 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2105 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2106 args[0], args)
2106 args[0], args)
2107 os.close(wfd)
2107 os.close(wfd)
2108 os.read(rfd, 1)
2108 os.read(rfd, 1)
2109 os._exit(0)
2109 os._exit(0)
2110
2110
2111 try:
2111 try:
2112 httpd = hgweb.create_server(repo)
2112 httpd = hgweb.create_server(repo)
2113 except socket.error, inst:
2113 except socket.error, inst:
2114 raise util.Abort(_('cannot start server: ') + inst.args[1])
2114 raise util.Abort(_('cannot start server: ') + inst.args[1])
2115
2115
2116 if ui.verbose:
2116 if ui.verbose:
2117 addr, port = httpd.socket.getsockname()
2117 addr, port = httpd.socket.getsockname()
2118 if addr == '0.0.0.0':
2118 if addr == '0.0.0.0':
2119 addr = socket.gethostname()
2119 addr = socket.gethostname()
2120 else:
2120 else:
2121 try:
2121 try:
2122 addr = socket.gethostbyaddr(addr)[0]
2122 addr = socket.gethostbyaddr(addr)[0]
2123 except socket.error:
2123 except socket.error:
2124 pass
2124 pass
2125 if port != 80:
2125 if port != 80:
2126 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2126 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2127 else:
2127 else:
2128 ui.status(_('listening at http://%s/\n') % addr)
2128 ui.status(_('listening at http://%s/\n') % addr)
2129
2129
2130 if opts['pid_file']:
2130 if opts['pid_file']:
2131 fp = open(opts['pid_file'], 'w')
2131 fp = open(opts['pid_file'], 'w')
2132 fp.write(str(os.getpid()))
2132 fp.write(str(os.getpid()))
2133 fp.close()
2133 fp.close()
2134
2134
2135 if opts['daemon_pipefds']:
2135 if opts['daemon_pipefds']:
2136 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2136 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2137 os.close(rfd)
2137 os.close(rfd)
2138 os.write(wfd, 'y')
2138 os.write(wfd, 'y')
2139 os.close(wfd)
2139 os.close(wfd)
2140 sys.stdout.flush()
2140 sys.stdout.flush()
2141 sys.stderr.flush()
2141 sys.stderr.flush()
2142 fd = os.open(util.nulldev, os.O_RDWR)
2142 fd = os.open(util.nulldev, os.O_RDWR)
2143 if fd != 0: os.dup2(fd, 0)
2143 if fd != 0: os.dup2(fd, 0)
2144 if fd != 1: os.dup2(fd, 1)
2144 if fd != 1: os.dup2(fd, 1)
2145 if fd != 2: os.dup2(fd, 2)
2145 if fd != 2: os.dup2(fd, 2)
2146 if fd not in (0, 1, 2): os.close(fd)
2146 if fd not in (0, 1, 2): os.close(fd)
2147
2147
2148 httpd.serve_forever()
2148 httpd.serve_forever()
2149
2149
2150 def status(ui, repo, *pats, **opts):
2150 def status(ui, repo, *pats, **opts):
2151 """show changed files in the working directory
2151 """show changed files in the working directory
2152
2152
2153 Show changed files in the repository. If names are
2153 Show changed files in the repository. If names are
2154 given, only files that match are shown.
2154 given, only files that match are shown.
2155
2155
2156 The codes used to show the status of files are:
2156 The codes used to show the status of files are:
2157 M = modified
2157 M = modified
2158 A = added
2158 A = added
2159 R = removed
2159 R = removed
2160 ! = deleted, but still tracked
2160 ! = deleted, but still tracked
2161 ? = not tracked
2161 ? = not tracked
2162 """
2162 """
2163
2163
2164 files, matchfn, anypats = matchpats(repo, pats, opts)
2164 files, matchfn, anypats = matchpats(repo, pats, opts)
2165 cwd = (pats and repo.getcwd()) or ''
2165 cwd = (pats and repo.getcwd()) or ''
2166 modified, added, removed, deleted, unknown = [
2166 modified, added, removed, deleted, unknown = [
2167 [util.pathto(cwd, x) for x in n]
2167 [util.pathto(cwd, x) for x in n]
2168 for n in repo.changes(files=files, match=matchfn)]
2168 for n in repo.changes(files=files, match=matchfn)]
2169
2169
2170 changetypes = [(_('modified'), 'M', modified),
2170 changetypes = [(_('modified'), 'M', modified),
2171 (_('added'), 'A', added),
2171 (_('added'), 'A', added),
2172 (_('removed'), 'R', removed),
2172 (_('removed'), 'R', removed),
2173 (_('deleted'), '!', deleted),
2173 (_('deleted'), '!', deleted),
2174 (_('unknown'), '?', unknown)]
2174 (_('unknown'), '?', unknown)]
2175
2175
2176 end = opts['print0'] and '\0' or '\n'
2176 end = opts['print0'] and '\0' or '\n'
2177
2177
2178 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2178 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2179 or changetypes):
2179 or changetypes):
2180 if opts['no_status']:
2180 if opts['no_status']:
2181 format = "%%s%s" % end
2181 format = "%%s%s" % end
2182 else:
2182 else:
2183 format = "%s %%s%s" % (char, end);
2183 format = "%s %%s%s" % (char, end);
2184
2184
2185 for f in changes:
2185 for f in changes:
2186 ui.write(format % f)
2186 ui.write(format % f)
2187
2187
2188 def tag(ui, repo, name, rev_=None, **opts):
2188 def tag(ui, repo, name, rev_=None, **opts):
2189 """add a tag for the current tip or a given revision
2189 """add a tag for the current tip or a given revision
2190
2190
2191 Name a particular revision using <name>.
2191 Name a particular revision using <name>.
2192
2192
2193 Tags are used to name particular revisions of the repository and are
2193 Tags are used to name particular revisions of the repository and are
2194 very useful to compare different revision, to go back to significant
2194 very useful to compare different revision, to go back to significant
2195 earlier versions or to mark branch points as releases, etc.
2195 earlier versions or to mark branch points as releases, etc.
2196
2196
2197 If no revision is given, the tip is used.
2197 If no revision is given, the tip is used.
2198
2198
2199 To facilitate version control, distribution, and merging of tags,
2199 To facilitate version control, distribution, and merging of tags,
2200 they are stored as a file named ".hgtags" which is managed
2200 they are stored as a file named ".hgtags" which is managed
2201 similarly to other project files and can be hand-edited if
2201 similarly to other project files and can be hand-edited if
2202 necessary. The file '.hg/localtags' is used for local tags (not
2202 necessary. The file '.hg/localtags' is used for local tags (not
2203 shared among repositories).
2203 shared among repositories).
2204 """
2204 """
2205 if name == "tip":
2205 if name == "tip":
2206 raise util.Abort(_("the name 'tip' is reserved"))
2206 raise util.Abort(_("the name 'tip' is reserved"))
2207 if rev_ is not None:
2207 if rev_ is not None:
2208 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2208 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2209 "please use 'hg tag [-r REV] NAME' instead\n"))
2209 "please use 'hg tag [-r REV] NAME' instead\n"))
2210 if opts['rev']:
2210 if opts['rev']:
2211 raise util.Abort(_("use only one form to specify the revision"))
2211 raise util.Abort(_("use only one form to specify the revision"))
2212 if opts['rev']:
2212 if opts['rev']:
2213 rev_ = opts['rev']
2213 rev_ = opts['rev']
2214 if rev_:
2214 if rev_:
2215 r = hex(repo.lookup(rev_))
2215 r = hex(repo.lookup(rev_))
2216 else:
2216 else:
2217 r = hex(repo.changelog.tip())
2217 r = hex(repo.changelog.tip())
2218
2218
2219 disallowed = (revrangesep, '\r', '\n')
2219 disallowed = (revrangesep, '\r', '\n')
2220 for c in disallowed:
2220 for c in disallowed:
2221 if name.find(c) >= 0:
2221 if name.find(c) >= 0:
2222 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2222 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2223
2223
2224 repo.hook('pretag', throw=True, node=r, tag=name,
2224 repo.hook('pretag', throw=True, node=r, tag=name,
2225 local=int(not not opts['local']))
2225 local=int(not not opts['local']))
2226
2226
2227 if opts['local']:
2227 if opts['local']:
2228 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2228 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2229 repo.hook('tag', node=r, tag=name, local=1)
2229 repo.hook('tag', node=r, tag=name, local=1)
2230 return
2230 return
2231
2231
2232 for x in repo.changes():
2232 for x in repo.changes():
2233 if ".hgtags" in x:
2233 if ".hgtags" in x:
2234 raise util.Abort(_("working copy of .hgtags is changed "
2234 raise util.Abort(_("working copy of .hgtags is changed "
2235 "(please commit .hgtags manually)"))
2235 "(please commit .hgtags manually)"))
2236
2236
2237 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2237 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2238 if repo.dirstate.state(".hgtags") == '?':
2238 if repo.dirstate.state(".hgtags") == '?':
2239 repo.add([".hgtags"])
2239 repo.add([".hgtags"])
2240
2240
2241 message = (opts['message'] or
2241 message = (opts['message'] or
2242 _("Added tag %s for changeset %s") % (name, r))
2242 _("Added tag %s for changeset %s") % (name, r))
2243 try:
2243 try:
2244 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2244 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2245 repo.hook('tag', node=r, tag=name, local=0)
2245 repo.hook('tag', node=r, tag=name, local=0)
2246 except ValueError, inst:
2246 except ValueError, inst:
2247 raise util.Abort(str(inst))
2247 raise util.Abort(str(inst))
2248
2248
2249 def tags(ui, repo):
2249 def tags(ui, repo):
2250 """list repository tags
2250 """list repository tags
2251
2251
2252 List the repository tags.
2252 List the repository tags.
2253
2253
2254 This lists both regular and local tags.
2254 This lists both regular and local tags.
2255 """
2255 """
2256
2256
2257 l = repo.tagslist()
2257 l = repo.tagslist()
2258 l.reverse()
2258 l.reverse()
2259 for t, n in l:
2259 for t, n in l:
2260 try:
2260 try:
2261 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2261 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2262 except KeyError:
2262 except KeyError:
2263 r = " ?:?"
2263 r = " ?:?"
2264 ui.write("%-30s %s\n" % (t, r))
2264 ui.write("%-30s %s\n" % (t, r))
2265
2265
2266 def tip(ui, repo, **opts):
2266 def tip(ui, repo, **opts):
2267 """show the tip revision
2267 """show the tip revision
2268
2268
2269 Show the tip revision.
2269 Show the tip revision.
2270 """
2270 """
2271 n = repo.changelog.tip()
2271 n = repo.changelog.tip()
2272 br = None
2272 br = None
2273 if opts['branches']:
2273 if opts['branches']:
2274 br = repo.branchlookup([n])
2274 br = repo.branchlookup([n])
2275 show_changeset(ui, repo, changenode=n, brinfo=br)
2275 show_changeset(ui, repo, changenode=n, brinfo=br)
2276 if opts['patch']:
2276 if opts['patch']:
2277 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2277 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2278
2278
2279 def unbundle(ui, repo, fname, **opts):
2279 def unbundle(ui, repo, fname, **opts):
2280 """apply a changegroup file
2280 """apply a changegroup file
2281
2281
2282 Apply a compressed changegroup file generated by the bundle
2282 Apply a compressed changegroup file generated by the bundle
2283 command.
2283 command.
2284 """
2284 """
2285 f = urllib.urlopen(fname)
2285 f = urllib.urlopen(fname)
2286
2286
2287 if f.read(4) != "HG10":
2287 if f.read(4) != "HG10":
2288 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2288 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2289
2289
2290 def bzgenerator(f):
2290 def bzgenerator(f):
2291 zd = bz2.BZ2Decompressor()
2291 zd = bz2.BZ2Decompressor()
2292 for chunk in f:
2292 for chunk in f:
2293 yield zd.decompress(chunk)
2293 yield zd.decompress(chunk)
2294
2294
2295 bzgen = bzgenerator(util.filechunkiter(f, 4096))
2295 bzgen = bzgenerator(util.filechunkiter(f, 4096))
2296 if repo.addchangegroup(util.chunkbuffer(bzgen)):
2296 if repo.addchangegroup(util.chunkbuffer(bzgen)):
2297 return 1
2297 return 1
2298
2298
2299 if opts['update']:
2299 if opts['update']:
2300 return update(ui, repo)
2300 return update(ui, repo)
2301 else:
2301 else:
2302 ui.status(_("(run 'hg update' to get a working copy)\n"))
2302 ui.status(_("(run 'hg update' to get a working copy)\n"))
2303
2303
2304 def undo(ui, repo):
2304 def undo(ui, repo):
2305 """undo the last commit or pull
2305 """undo the last commit or pull
2306
2306
2307 Roll back the last pull or commit transaction on the
2307 Roll back the last pull or commit transaction on the
2308 repository, restoring the project to its earlier state.
2308 repository, restoring the project to its earlier state.
2309
2309
2310 This command should be used with care. There is only one level of
2310 This command should be used with care. There is only one level of
2311 undo and there is no redo.
2311 undo and there is no redo.
2312
2312
2313 This command is not intended for use on public repositories. Once
2313 This command is not intended for use on public repositories. Once
2314 a change is visible for pull by other users, undoing it locally is
2314 a change is visible for pull by other users, undoing it locally is
2315 ineffective.
2315 ineffective.
2316 """
2316 """
2317 repo.undo()
2317 repo.undo()
2318
2318
2319 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2319 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2320 branch=None):
2320 branch=None):
2321 """update or merge working directory
2321 """update or merge working directory
2322
2322
2323 Update the working directory to the specified revision.
2323 Update the working directory to the specified revision.
2324
2324
2325 If there are no outstanding changes in the working directory and
2325 If there are no outstanding changes in the working directory and
2326 there is a linear relationship between the current version and the
2326 there is a linear relationship between the current version and the
2327 requested version, the result is the requested version.
2327 requested version, the result is the requested version.
2328
2328
2329 Otherwise the result is a merge between the contents of the
2329 Otherwise the result is a merge between the contents of the
2330 current working directory and the requested version. Files that
2330 current working directory and the requested version. Files that
2331 changed between either parent are marked as changed for the next
2331 changed between either parent are marked as changed for the next
2332 commit and a commit must be performed before any further updates
2332 commit and a commit must be performed before any further updates
2333 are allowed.
2333 are allowed.
2334
2334
2335 By default, update will refuse to run if doing so would require
2335 By default, update will refuse to run if doing so would require
2336 merging or discarding local changes.
2336 merging or discarding local changes.
2337 """
2337 """
2338 if branch:
2338 if branch:
2339 br = repo.branchlookup(branch=branch)
2339 br = repo.branchlookup(branch=branch)
2340 found = []
2340 found = []
2341 for x in br:
2341 for x in br:
2342 if branch in br[x]:
2342 if branch in br[x]:
2343 found.append(x)
2343 found.append(x)
2344 if len(found) > 1:
2344 if len(found) > 1:
2345 ui.warn(_("Found multiple heads for %s\n") % branch)
2345 ui.warn(_("Found multiple heads for %s\n") % branch)
2346 for x in found:
2346 for x in found:
2347 show_changeset(ui, repo, changenode=x, brinfo=br)
2347 show_changeset(ui, repo, changenode=x, brinfo=br)
2348 return 1
2348 return 1
2349 if len(found) == 1:
2349 if len(found) == 1:
2350 node = found[0]
2350 node = found[0]
2351 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2351 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2352 else:
2352 else:
2353 ui.warn(_("branch %s not found\n") % (branch))
2353 ui.warn(_("branch %s not found\n") % (branch))
2354 return 1
2354 return 1
2355 else:
2355 else:
2356 node = node and repo.lookup(node) or repo.changelog.tip()
2356 node = node and repo.lookup(node) or repo.changelog.tip()
2357 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2357 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2358
2358
2359 def verify(ui, repo):
2359 def verify(ui, repo):
2360 """verify the integrity of the repository
2360 """verify the integrity of the repository
2361
2361
2362 Verify the integrity of the current repository.
2362 Verify the integrity of the current repository.
2363
2363
2364 This will perform an extensive check of the repository's
2364 This will perform an extensive check of the repository's
2365 integrity, validating the hashes and checksums of each entry in
2365 integrity, validating the hashes and checksums of each entry in
2366 the changelog, manifest, and tracked files, as well as the
2366 the changelog, manifest, and tracked files, as well as the
2367 integrity of their crosslinks and indices.
2367 integrity of their crosslinks and indices.
2368 """
2368 """
2369 return repo.verify()
2369 return repo.verify()
2370
2370
2371 # Command options and aliases are listed here, alphabetically
2371 # Command options and aliases are listed here, alphabetically
2372
2372
2373 table = {
2373 table = {
2374 "^add":
2374 "^add":
2375 (add,
2375 (add,
2376 [('I', 'include', [], _('include names matching the given patterns')),
2376 [('I', 'include', [], _('include names matching the given patterns')),
2377 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2377 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2378 _('hg add [OPTION]... [FILE]...')),
2378 _('hg add [OPTION]... [FILE]...')),
2379 "addremove":
2379 "addremove":
2380 (addremove,
2380 (addremove,
2381 [('I', 'include', [], _('include names matching the given patterns')),
2381 [('I', 'include', [], _('include names matching the given patterns')),
2382 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2382 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2383 _('hg addremove [OPTION]... [FILE]...')),
2383 _('hg addremove [OPTION]... [FILE]...')),
2384 "^annotate":
2384 "^annotate":
2385 (annotate,
2385 (annotate,
2386 [('r', 'rev', '', _('annotate the specified revision')),
2386 [('r', 'rev', '', _('annotate the specified revision')),
2387 ('a', 'text', None, _('treat all files as text')),
2387 ('a', 'text', None, _('treat all files as text')),
2388 ('u', 'user', None, _('list the author')),
2388 ('u', 'user', None, _('list the author')),
2389 ('d', 'date', None, _('list the date')),
2389 ('d', 'date', None, _('list the date')),
2390 ('n', 'number', None, _('list the revision number (default)')),
2390 ('n', 'number', None, _('list the revision number (default)')),
2391 ('c', 'changeset', None, _('list the changeset')),
2391 ('c', 'changeset', None, _('list the changeset')),
2392 ('I', 'include', [], _('include names matching the given patterns')),
2392 ('I', 'include', [], _('include names matching the given patterns')),
2393 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2393 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2394 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2394 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2395 "bundle":
2395 "bundle":
2396 (bundle,
2396 (bundle,
2397 [],
2397 [],
2398 _('hg bundle FILE DEST')),
2398 _('hg bundle FILE DEST')),
2399 "cat":
2399 "cat":
2400 (cat,
2400 (cat,
2401 [('o', 'output', '', _('print output to file with formatted name')),
2401 [('o', 'output', '', _('print output to file with formatted name')),
2402 ('r', 'rev', '', _('print the given revision')),
2402 ('r', 'rev', '', _('print the given revision')),
2403 ('I', 'include', [], _('include names matching the given patterns')),
2403 ('I', 'include', [], _('include names matching the given patterns')),
2404 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2404 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2405 _('hg cat [OPTION]... FILE...')),
2405 _('hg cat [OPTION]... FILE...')),
2406 "^clone":
2406 "^clone":
2407 (clone,
2407 (clone,
2408 [('U', 'noupdate', None, _('do not update the new working directory')),
2408 [('U', 'noupdate', None, _('do not update the new working directory')),
2409 ('r', 'rev', [],
2409 ('r', 'rev', [],
2410 _('a changeset you would like to have after cloning')),
2410 _('a changeset you would like to have after cloning')),
2411 ('', 'pull', None, _('use pull protocol to copy metadata')),
2411 ('', 'pull', None, _('use pull protocol to copy metadata')),
2412 ('e', 'ssh', '', _('specify ssh command to use')),
2412 ('e', 'ssh', '', _('specify ssh command to use')),
2413 ('', 'remotecmd', '',
2413 ('', 'remotecmd', '',
2414 _('specify hg command to run on the remote side'))],
2414 _('specify hg command to run on the remote side'))],
2415 _('hg clone [OPTION]... SOURCE [DEST]')),
2415 _('hg clone [OPTION]... SOURCE [DEST]')),
2416 "^commit|ci":
2416 "^commit|ci":
2417 (commit,
2417 (commit,
2418 [('A', 'addremove', None, _('run addremove during commit')),
2418 [('A', 'addremove', None, _('run addremove during commit')),
2419 ('m', 'message', '', _('use <text> as commit message')),
2419 ('m', 'message', '', _('use <text> as commit message')),
2420 ('l', 'logfile', '', _('read the commit message from <file>')),
2420 ('l', 'logfile', '', _('read the commit message from <file>')),
2421 ('d', 'date', '', _('record datecode as commit date')),
2421 ('d', 'date', '', _('record datecode as commit date')),
2422 ('u', 'user', '', _('record user as commiter')),
2422 ('u', 'user', '', _('record user as commiter')),
2423 ('I', 'include', [], _('include names matching the given patterns')),
2423 ('I', 'include', [], _('include names matching the given patterns')),
2424 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2424 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2425 _('hg commit [OPTION]... [FILE]...')),
2425 _('hg commit [OPTION]... [FILE]...')),
2426 "copy|cp":
2426 "copy|cp":
2427 (copy,
2427 (copy,
2428 [('A', 'after', None, _('record a copy that has already occurred')),
2428 [('A', 'after', None, _('record a copy that has already occurred')),
2429 ('f', 'force', None,
2429 ('f', 'force', None,
2430 _('forcibly copy over an existing managed file')),
2430 _('forcibly copy over an existing managed file')),
2431 ('I', 'include', [], _('include names matching the given patterns')),
2431 ('I', 'include', [], _('include names matching the given patterns')),
2432 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2432 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2433 _('hg copy [OPTION]... [SOURCE]... DEST')),
2433 _('hg copy [OPTION]... [SOURCE]... DEST')),
2434 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2434 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2435 "debugcomplete": (debugcomplete, [], _('debugcomplete CMD')),
2435 "debugcomplete": (debugcomplete, [], _('debugcomplete CMD')),
2436 "debugrebuildstate":
2436 "debugrebuildstate":
2437 (debugrebuildstate,
2437 (debugrebuildstate,
2438 [('r', 'rev', '', _('revision to rebuild to'))],
2438 [('r', 'rev', '', _('revision to rebuild to'))],
2439 _('debugrebuildstate [-r REV] [REV]')),
2439 _('debugrebuildstate [-r REV] [REV]')),
2440 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2440 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2441 "debugconfig": (debugconfig, [], _('debugconfig')),
2441 "debugconfig": (debugconfig, [], _('debugconfig')),
2442 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2442 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2443 "debugstate": (debugstate, [], _('debugstate')),
2443 "debugstate": (debugstate, [], _('debugstate')),
2444 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2444 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2445 "debugindex": (debugindex, [], _('debugindex FILE')),
2445 "debugindex": (debugindex, [], _('debugindex FILE')),
2446 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2446 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2447 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2447 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2448 "debugwalk":
2448 "debugwalk":
2449 (debugwalk,
2449 (debugwalk,
2450 [('I', 'include', [], _('include names matching the given patterns')),
2450 [('I', 'include', [], _('include names matching the given patterns')),
2451 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2451 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2452 _('debugwalk [OPTION]... [FILE]...')),
2452 _('debugwalk [OPTION]... [FILE]...')),
2453 "^diff":
2453 "^diff":
2454 (diff,
2454 (diff,
2455 [('r', 'rev', [], _('revision')),
2455 [('r', 'rev', [], _('revision')),
2456 ('a', 'text', None, _('treat all files as text')),
2456 ('a', 'text', None, _('treat all files as text')),
2457 ('p', 'show-function', None,
2457 ('p', 'show-function', None,
2458 _('show which function each change is in')),
2458 _('show which function each change is in')),
2459 ('w', 'ignore-all-space', None,
2459 ('w', 'ignore-all-space', None,
2460 _('ignore white space when comparing lines')),
2460 _('ignore white space when comparing lines')),
2461 ('I', 'include', [], _('include names matching the given patterns')),
2461 ('I', 'include', [], _('include names matching the given patterns')),
2462 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2462 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2463 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2463 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2464 "^export":
2464 "^export":
2465 (export,
2465 (export,
2466 [('o', 'output', '', _('print output to file with formatted name')),
2466 [('o', 'output', '', _('print output to file with formatted name')),
2467 ('a', 'text', None, _('treat all files as text')),
2467 ('a', 'text', None, _('treat all files as text')),
2468 ('', 'switch-parent', None, _('diff against the second parent'))],
2468 ('', 'switch-parent', None, _('diff against the second parent'))],
2469 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2469 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2470 "forget":
2470 "forget":
2471 (forget,
2471 (forget,
2472 [('I', 'include', [], _('include names matching the given patterns')),
2472 [('I', 'include', [], _('include names matching the given patterns')),
2473 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2473 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2474 _('hg forget [OPTION]... FILE...')),
2474 _('hg forget [OPTION]... FILE...')),
2475 "grep":
2475 "grep":
2476 (grep,
2476 (grep,
2477 [('0', 'print0', None, _('end fields with NUL')),
2477 [('0', 'print0', None, _('end fields with NUL')),
2478 ('', 'all', None, _('print all revisions that match')),
2478 ('', 'all', None, _('print all revisions that match')),
2479 ('i', 'ignore-case', None, _('ignore case when matching')),
2479 ('i', 'ignore-case', None, _('ignore case when matching')),
2480 ('l', 'files-with-matches', None,
2480 ('l', 'files-with-matches', None,
2481 _('print only filenames and revs that match')),
2481 _('print only filenames and revs that match')),
2482 ('n', 'line-number', None, _('print matching line numbers')),
2482 ('n', 'line-number', None, _('print matching line numbers')),
2483 ('r', 'rev', [], _('search in given revision range')),
2483 ('r', 'rev', [], _('search in given revision range')),
2484 ('u', 'user', None, _('print user who committed change')),
2484 ('u', 'user', None, _('print user who committed change')),
2485 ('I', 'include', [], _('include names matching the given patterns')),
2485 ('I', 'include', [], _('include names matching the given patterns')),
2486 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2486 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2487 _('hg grep [OPTION]... PATTERN [FILE]...')),
2487 _('hg grep [OPTION]... PATTERN [FILE]...')),
2488 "heads":
2488 "heads":
2489 (heads,
2489 (heads,
2490 [('b', 'branches', None, _('show branches')),
2490 [('b', 'branches', None, _('show branches')),
2491 ('r', 'rev', '', _('show only heads which are descendants of rev'))],
2491 ('r', 'rev', '', _('show only heads which are descendants of rev'))],
2492 _('hg heads [-b] [-r <rev>]')),
2492 _('hg heads [-b] [-r <rev>]')),
2493 "help": (help_, [], _('hg help [COMMAND]')),
2493 "help": (help_, [], _('hg help [COMMAND]')),
2494 "identify|id": (identify, [], _('hg identify')),
2494 "identify|id": (identify, [], _('hg identify')),
2495 "import|patch":
2495 "import|patch":
2496 (import_,
2496 (import_,
2497 [('p', 'strip', 1,
2497 [('p', 'strip', 1,
2498 _('directory strip option for patch. This has the same\n') +
2498 _('directory strip option for patch. This has the same\n') +
2499 _('meaning as the corresponding patch option')),
2499 _('meaning as the corresponding patch option')),
2500 ('b', 'base', '', _('base path')),
2500 ('b', 'base', '', _('base path')),
2501 ('f', 'force', None,
2501 ('f', 'force', None,
2502 _('skip check for outstanding uncommitted changes'))],
2502 _('skip check for outstanding uncommitted changes'))],
2503 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2503 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2504 "incoming|in": (incoming,
2504 "incoming|in": (incoming,
2505 [('M', 'no-merges', None, _('do not show merges')),
2505 [('M', 'no-merges', None, _('do not show merges')),
2506 ('p', 'patch', None, _('show patch')),
2506 ('p', 'patch', None, _('show patch')),
2507 ('n', 'newest-first', None, _('show newest record first'))],
2507 ('n', 'newest-first', None, _('show newest record first'))],
2508 _('hg incoming [-p] [-n] [-M] [SOURCE]')),
2508 _('hg incoming [-p] [-n] [-M] [SOURCE]')),
2509 "^init": (init, [], _('hg init [DEST]')),
2509 "^init": (init, [], _('hg init [DEST]')),
2510 "locate":
2510 "locate":
2511 (locate,
2511 (locate,
2512 [('r', 'rev', '', _('search the repository as it stood at rev')),
2512 [('r', 'rev', '', _('search the repository as it stood at rev')),
2513 ('0', 'print0', None,
2513 ('0', 'print0', None,
2514 _('end filenames with NUL, for use with xargs')),
2514 _('end filenames with NUL, for use with xargs')),
2515 ('f', 'fullpath', None,
2515 ('f', 'fullpath', None,
2516 _('print complete paths from the filesystem root')),
2516 _('print complete paths from the filesystem root')),
2517 ('I', 'include', [], _('include names matching the given patterns')),
2517 ('I', 'include', [], _('include names matching the given patterns')),
2518 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2518 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2519 _('hg locate [OPTION]... [PATTERN]...')),
2519 _('hg locate [OPTION]... [PATTERN]...')),
2520 "^log|history":
2520 "^log|history":
2521 (log,
2521 (log,
2522 [('b', 'branches', None, _('show branches')),
2522 [('b', 'branches', None, _('show branches')),
2523 ('k', 'keyword', [], _('search for a keyword')),
2523 ('k', 'keyword', [], _('search for a keyword')),
2524 ('l', 'limit', '', _('limit number of changes displayed')),
2524 ('l', 'limit', '', _('limit number of changes displayed')),
2525 ('r', 'rev', [], _('show the specified revision or range')),
2525 ('r', 'rev', [], _('show the specified revision or range')),
2526 ('M', 'no-merges', None, _('do not show merges')),
2526 ('M', 'no-merges', None, _('do not show merges')),
2527 ('m', 'only-merges', None, _('show only merges')),
2527 ('m', 'only-merges', None, _('show only merges')),
2528 ('p', 'patch', None, _('show patch')),
2528 ('p', 'patch', None, _('show patch')),
2529 ('I', 'include', [], _('include names matching the given patterns')),
2529 ('I', 'include', [], _('include names matching the given patterns')),
2530 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2530 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2531 _('hg log [OPTION]... [FILE]')),
2531 _('hg log [OPTION]... [FILE]')),
2532 "manifest": (manifest, [], _('hg manifest [REV]')),
2532 "manifest": (manifest, [], _('hg manifest [REV]')),
2533 "outgoing|out": (outgoing,
2533 "outgoing|out": (outgoing,
2534 [('M', 'no-merges', None, _('do not show merges')),
2534 [('M', 'no-merges', None, _('do not show merges')),
2535 ('p', 'patch', None, _('show patch')),
2535 ('p', 'patch', None, _('show patch')),
2536 ('n', 'newest-first', None, _('show newest record first'))],
2536 ('n', 'newest-first', None, _('show newest record first'))],
2537 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2537 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2538 "^parents":
2538 "^parents":
2539 (parents,
2539 (parents,
2540 [('b', 'branches', None, _('show branches'))],
2540 [('b', 'branches', None, _('show branches'))],
2541 _('hg parents [-b] [REV]')),
2541 _('hg parents [-b] [REV]')),
2542 "paths": (paths, [], _('hg paths [NAME]')),
2542 "paths": (paths, [], _('hg paths [NAME]')),
2543 "^pull":
2543 "^pull":
2544 (pull,
2544 (pull,
2545 [('u', 'update', None,
2545 [('u', 'update', None,
2546 _('update the working directory to tip after pull')),
2546 _('update the working directory to tip after pull')),
2547 ('e', 'ssh', '', _('specify ssh command to use')),
2547 ('e', 'ssh', '', _('specify ssh command to use')),
2548 ('r', 'rev', [], _('a specific revision you would like to pull')),
2548 ('r', 'rev', [], _('a specific revision you would like to pull')),
2549 ('', 'remotecmd', '',
2549 ('', 'remotecmd', '',
2550 _('specify hg command to run on the remote side'))],
2550 _('specify hg command to run on the remote side'))],
2551 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
2551 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
2552 "^push":
2552 "^push":
2553 (push,
2553 (push,
2554 [('f', 'force', None, _('force push')),
2554 [('f', 'force', None, _('force push')),
2555 ('e', 'ssh', '', _('specify ssh command to use')),
2555 ('e', 'ssh', '', _('specify ssh command to use')),
2556 ('r', 'rev', [], _('a specific revision you would like to push')),
2556 ('r', 'rev', [], _('a specific revision you would like to push')),
2557 ('', 'remotecmd', '',
2557 ('', 'remotecmd', '',
2558 _('specify hg command to run on the remote side'))],
2558 _('specify hg command to run on the remote side'))],
2559 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
2559 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
2560 "debugrawcommit|rawcommit":
2560 "debugrawcommit|rawcommit":
2561 (rawcommit,
2561 (rawcommit,
2562 [('p', 'parent', [], _('parent')),
2562 [('p', 'parent', [], _('parent')),
2563 ('d', 'date', '', _('date code')),
2563 ('d', 'date', '', _('date code')),
2564 ('u', 'user', '', _('user')),
2564 ('u', 'user', '', _('user')),
2565 ('F', 'files', '', _('file list')),
2565 ('F', 'files', '', _('file list')),
2566 ('m', 'message', '', _('commit message')),
2566 ('m', 'message', '', _('commit message')),
2567 ('l', 'logfile', '', _('commit message file'))],
2567 ('l', 'logfile', '', _('commit message file'))],
2568 _('hg debugrawcommit [OPTION]... [FILE]...')),
2568 _('hg debugrawcommit [OPTION]... [FILE]...')),
2569 "recover": (recover, [], _('hg recover')),
2569 "recover": (recover, [], _('hg recover')),
2570 "^remove|rm":
2570 "^remove|rm":
2571 (remove,
2571 (remove,
2572 [('f', 'force', None, _('remove file even if modified')),
2572 [('f', 'force', None, _('remove file even if modified')),
2573 ('I', 'include', [], _('include names matching the given patterns')),
2573 ('I', 'include', [], _('include names matching the given patterns')),
2574 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2574 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2575 _('hg remove [OPTION]... FILE...')),
2575 _('hg remove [OPTION]... FILE...')),
2576 "rename|mv":
2576 "rename|mv":
2577 (rename,
2577 (rename,
2578 [('A', 'after', None, _('record a rename that has already occurred')),
2578 [('A', 'after', None, _('record a rename that has already occurred')),
2579 ('f', 'force', None,
2579 ('f', 'force', None,
2580 _('forcibly copy over an existing managed file')),
2580 _('forcibly copy over an existing managed file')),
2581 ('I', 'include', [], _('include names matching the given patterns')),
2581 ('I', 'include', [], _('include names matching the given patterns')),
2582 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2582 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2583 _('hg rename [OPTION]... SOURCE... DEST')),
2583 _('hg rename [OPTION]... SOURCE... DEST')),
2584 "^revert":
2584 "^revert":
2585 (revert,
2585 (revert,
2586 [('r', 'rev', '', _('revision to revert to')),
2586 [('r', 'rev', '', _('revision to revert to')),
2587 ('I', 'include', [], _('include names matching the given patterns')),
2587 ('I', 'include', [], _('include names matching the given patterns')),
2588 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2588 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2589 _('hg revert [-r REV] [NAME]...')),
2589 _('hg revert [-r REV] [NAME]...')),
2590 "root": (root, [], _('hg root')),
2590 "root": (root, [], _('hg root')),
2591 "^serve":
2591 "^serve":
2592 (serve,
2592 (serve,
2593 [('A', 'accesslog', '', _('name of access log file to write to')),
2593 [('A', 'accesslog', '', _('name of access log file to write to')),
2594 ('d', 'daemon', None, _('run server in background')),
2594 ('d', 'daemon', None, _('run server in background')),
2595 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2595 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2596 ('E', 'errorlog', '', _('name of error log file to write to')),
2596 ('E', 'errorlog', '', _('name of error log file to write to')),
2597 ('p', 'port', 0, _('port to use (default: 8000)')),
2597 ('p', 'port', 0, _('port to use (default: 8000)')),
2598 ('a', 'address', '', _('address to use')),
2598 ('a', 'address', '', _('address to use')),
2599 ('n', 'name', '',
2599 ('n', 'name', '',
2600 _('name to show in web pages (default: working dir)')),
2600 _('name to show in web pages (default: working dir)')),
2601 ('', 'pid-file', '', _('name of file to write process ID to')),
2601 ('', 'pid-file', '', _('name of file to write process ID to')),
2602 ('', 'stdio', None, _('for remote clients')),
2602 ('', 'stdio', None, _('for remote clients')),
2603 ('t', 'templates', '', _('web templates to use')),
2603 ('t', 'templates', '', _('web templates to use')),
2604 ('', 'style', '', _('template style to use')),
2604 ('', 'style', '', _('template style to use')),
2605 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2605 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2606 _('hg serve [OPTION]...')),
2606 _('hg serve [OPTION]...')),
2607 "^status|st":
2607 "^status|st":
2608 (status,
2608 (status,
2609 [('m', 'modified', None, _('show only modified files')),
2609 [('m', 'modified', None, _('show only modified files')),
2610 ('a', 'added', None, _('show only added files')),
2610 ('a', 'added', None, _('show only added files')),
2611 ('r', 'removed', None, _('show only removed files')),
2611 ('r', 'removed', None, _('show only removed files')),
2612 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2612 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2613 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2613 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2614 ('n', 'no-status', None, _('hide status prefix')),
2614 ('n', 'no-status', None, _('hide status prefix')),
2615 ('0', 'print0', None,
2615 ('0', 'print0', None,
2616 _('end filenames with NUL, for use with xargs')),
2616 _('end filenames with NUL, for use with xargs')),
2617 ('I', 'include', [], _('include names matching the given patterns')),
2617 ('I', 'include', [], _('include names matching the given patterns')),
2618 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2618 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2619 _('hg status [OPTION]... [FILE]...')),
2619 _('hg status [OPTION]... [FILE]...')),
2620 "tag":
2620 "tag":
2621 (tag,
2621 (tag,
2622 [('l', 'local', None, _('make the tag local')),
2622 [('l', 'local', None, _('make the tag local')),
2623 ('m', 'message', '', _('message for tag commit log entry')),
2623 ('m', 'message', '', _('message for tag commit log entry')),
2624 ('d', 'date', '', _('record datecode as commit date')),
2624 ('d', 'date', '', _('record datecode as commit date')),
2625 ('u', 'user', '', _('record user as commiter')),
2625 ('u', 'user', '', _('record user as commiter')),
2626 ('r', 'rev', '', _('revision to tag'))],
2626 ('r', 'rev', '', _('revision to tag'))],
2627 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2627 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2628 "tags": (tags, [], _('hg tags')),
2628 "tags": (tags, [], _('hg tags')),
2629 "tip":
2629 "tip":
2630 (tip,
2630 (tip,
2631 [('b', 'branches', None, _('show branches')),
2631 [('b', 'branches', None, _('show branches')),
2632 ('p', 'patch', None, _('show patch'))],
2632 ('p', 'patch', None, _('show patch'))],
2633 _('hg tip [-b] [-p]')),
2633 _('hg tip [-b] [-p]')),
2634 "unbundle":
2634 "unbundle":
2635 (unbundle,
2635 (unbundle,
2636 [('u', 'update', None,
2636 [('u', 'update', None,
2637 _('update the working directory to tip after unbundle'))],
2637 _('update the working directory to tip after unbundle'))],
2638 _('hg unbundle [-u] FILE')),
2638 _('hg unbundle [-u] FILE')),
2639 "undo": (undo, [], _('hg undo')),
2639 "undo": (undo, [], _('hg undo')),
2640 "^update|up|checkout|co":
2640 "^update|up|checkout|co":
2641 (update,
2641 (update,
2642 [('b', 'branch', '', _('checkout the head of a specific branch')),
2642 [('b', 'branch', '', _('checkout the head of a specific branch')),
2643 ('m', 'merge', None, _('allow merging of branches')),
2643 ('m', 'merge', None, _('allow merging of branches')),
2644 ('C', 'clean', None, _('overwrite locally modified files')),
2644 ('C', 'clean', None, _('overwrite locally modified files')),
2645 ('f', 'force', None, _('force a merge with outstanding changes'))],
2645 ('f', 'force', None, _('force a merge with outstanding changes'))],
2646 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
2646 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
2647 "verify": (verify, [], _('hg verify')),
2647 "verify": (verify, [], _('hg verify')),
2648 "version": (show_version, [], _('hg version')),
2648 "version": (show_version, [], _('hg version')),
2649 }
2649 }
2650
2650
2651 globalopts = [
2651 globalopts = [
2652 ('R', 'repository', '', _('repository root directory')),
2652 ('R', 'repository', '', _('repository root directory')),
2653 ('', 'cwd', '', _('change working directory')),
2653 ('', 'cwd', '', _('change working directory')),
2654 ('y', 'noninteractive', None,
2654 ('y', 'noninteractive', None,
2655 _('do not prompt, assume \'yes\' for any required answers')),
2655 _('do not prompt, assume \'yes\' for any required answers')),
2656 ('q', 'quiet', None, _('suppress output')),
2656 ('q', 'quiet', None, _('suppress output')),
2657 ('v', 'verbose', None, _('enable additional output')),
2657 ('v', 'verbose', None, _('enable additional output')),
2658 ('', 'debug', None, _('enable debugging output')),
2658 ('', 'debug', None, _('enable debugging output')),
2659 ('', 'debugger', None, _('start debugger')),
2659 ('', 'debugger', None, _('start debugger')),
2660 ('', 'traceback', None, _('print traceback on exception')),
2660 ('', 'traceback', None, _('print traceback on exception')),
2661 ('', 'time', None, _('time how long the command takes')),
2661 ('', 'time', None, _('time how long the command takes')),
2662 ('', 'profile', None, _('print command execution profile')),
2662 ('', 'profile', None, _('print command execution profile')),
2663 ('', 'version', None, _('output version information and exit')),
2663 ('', 'version', None, _('output version information and exit')),
2664 ('h', 'help', None, _('display help and exit')),
2664 ('h', 'help', None, _('display help and exit')),
2665 ]
2665 ]
2666
2666
2667 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2667 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2668 " debugindex debugindexdot")
2668 " debugindex debugindexdot")
2669 optionalrepo = ("paths debugconfig")
2669 optionalrepo = ("paths debugconfig")
2670
2670
2671 def findpossible(cmd):
2671 def findpossible(cmd):
2672 """
2672 """
2673 Return cmd -> (aliases, command table entry)
2673 Return cmd -> (aliases, command table entry)
2674 for each matching command
2674 for each matching command
2675 """
2675 """
2676 choice = {}
2676 choice = {}
2677 debugchoice = {}
2677 debugchoice = {}
2678 for e in table.keys():
2678 for e in table.keys():
2679 aliases = e.lstrip("^").split("|")
2679 aliases = e.lstrip("^").split("|")
2680 if cmd in aliases:
2680 if cmd in aliases:
2681 choice[cmd] = (aliases, table[e])
2681 choice[cmd] = (aliases, table[e])
2682 continue
2682 continue
2683 for a in aliases:
2683 for a in aliases:
2684 if a.startswith(cmd):
2684 if a.startswith(cmd):
2685 if aliases[0].startswith("debug"):
2685 if aliases[0].startswith("debug"):
2686 debugchoice[a] = (aliases, table[e])
2686 debugchoice[a] = (aliases, table[e])
2687 else:
2687 else:
2688 choice[a] = (aliases, table[e])
2688 choice[a] = (aliases, table[e])
2689 break
2689 break
2690
2690
2691 if not choice and debugchoice:
2691 if not choice and debugchoice:
2692 choice = debugchoice
2692 choice = debugchoice
2693
2693
2694 return choice
2694 return choice
2695
2695
2696 def find(cmd):
2696 def find(cmd):
2697 """Return (aliases, command table entry) for command string."""
2697 """Return (aliases, command table entry) for command string."""
2698 choice = findpossible(cmd)
2698 choice = findpossible(cmd)
2699
2699
2700 if choice.has_key(cmd):
2700 if choice.has_key(cmd):
2701 return choice[cmd]
2701 return choice[cmd]
2702
2702
2703 if len(choice) > 1:
2703 if len(choice) > 1:
2704 clist = choice.keys()
2704 clist = choice.keys()
2705 clist.sort()
2705 clist.sort()
2706 raise AmbiguousCommand(cmd, clist)
2706 raise AmbiguousCommand(cmd, clist)
2707
2707
2708 if choice:
2708 if choice:
2709 return choice.values()[0]
2709 return choice.values()[0]
2710
2710
2711 raise UnknownCommand(cmd)
2711 raise UnknownCommand(cmd)
2712
2712
2713 class SignalInterrupt(Exception):
2713 class SignalInterrupt(Exception):
2714 """Exception raised on SIGTERM and SIGHUP."""
2714 """Exception raised on SIGTERM and SIGHUP."""
2715
2715
2716 def catchterm(*args):
2716 def catchterm(*args):
2717 raise SignalInterrupt
2717 raise SignalInterrupt
2718
2718
2719 def run():
2719 def run():
2720 sys.exit(dispatch(sys.argv[1:]))
2720 sys.exit(dispatch(sys.argv[1:]))
2721
2721
2722 class ParseError(Exception):
2722 class ParseError(Exception):
2723 """Exception raised on errors in parsing the command line."""
2723 """Exception raised on errors in parsing the command line."""
2724
2724
2725 def parse(ui, args):
2725 def parse(ui, args):
2726 options = {}
2726 options = {}
2727 cmdoptions = {}
2727 cmdoptions = {}
2728
2728
2729 try:
2729 try:
2730 args = fancyopts.fancyopts(args, globalopts, options)
2730 args = fancyopts.fancyopts(args, globalopts, options)
2731 except fancyopts.getopt.GetoptError, inst:
2731 except fancyopts.getopt.GetoptError, inst:
2732 raise ParseError(None, inst)
2732 raise ParseError(None, inst)
2733
2733
2734 if args:
2734 if args:
2735 cmd, args = args[0], args[1:]
2735 cmd, args = args[0], args[1:]
2736 aliases, i = find(cmd)
2736 aliases, i = find(cmd)
2737 cmd = aliases[0]
2737 cmd = aliases[0]
2738 defaults = ui.config("defaults", cmd)
2738 defaults = ui.config("defaults", cmd)
2739 if defaults:
2739 if defaults:
2740 args = defaults.split() + args
2740 args = defaults.split() + args
2741 c = list(i[1])
2741 c = list(i[1])
2742 else:
2742 else:
2743 cmd = None
2743 cmd = None
2744 c = []
2744 c = []
2745
2745
2746 # combine global options into local
2746 # combine global options into local
2747 for o in globalopts:
2747 for o in globalopts:
2748 c.append((o[0], o[1], options[o[1]], o[3]))
2748 c.append((o[0], o[1], options[o[1]], o[3]))
2749
2749
2750 try:
2750 try:
2751 args = fancyopts.fancyopts(args, c, cmdoptions)
2751 args = fancyopts.fancyopts(args, c, cmdoptions)
2752 except fancyopts.getopt.GetoptError, inst:
2752 except fancyopts.getopt.GetoptError, inst:
2753 raise ParseError(cmd, inst)
2753 raise ParseError(cmd, inst)
2754
2754
2755 # separate global options back out
2755 # separate global options back out
2756 for o in globalopts:
2756 for o in globalopts:
2757 n = o[1]
2757 n = o[1]
2758 options[n] = cmdoptions[n]
2758 options[n] = cmdoptions[n]
2759 del cmdoptions[n]
2759 del cmdoptions[n]
2760
2760
2761 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2761 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2762
2762
2763 def dispatch(args):
2763 def dispatch(args):
2764 signal.signal(signal.SIGTERM, catchterm)
2764 signal.signal(signal.SIGTERM, catchterm)
2765 try:
2765 try:
2766 signal.signal(signal.SIGHUP, catchterm)
2766 signal.signal(signal.SIGHUP, catchterm)
2767 except AttributeError:
2767 except AttributeError:
2768 pass
2768 pass
2769
2769
2770 try:
2770 try:
2771 u = ui.ui()
2771 u = ui.ui()
2772 except util.Abort, inst:
2772 except util.Abort, inst:
2773 sys.stderr.write(_("abort: %s\n") % inst)
2773 sys.stderr.write(_("abort: %s\n") % inst)
2774 sys.exit(1)
2774 sys.exit(1)
2775
2775
2776 external = []
2776 external = []
2777 for x in u.extensions():
2777 for x in u.extensions():
2778 def on_exception(exc, inst):
2778 def on_exception(exc, inst):
2779 u.warn(_("*** failed to import extension %s\n") % x[1])
2779 u.warn(_("*** failed to import extension %s\n") % x[1])
2780 u.warn("%s\n" % inst)
2780 u.warn("%s\n" % inst)
2781 if "--traceback" in sys.argv[1:]:
2781 if "--traceback" in sys.argv[1:]:
2782 traceback.print_exc()
2782 traceback.print_exc()
2783 if x[1]:
2783 if x[1]:
2784 try:
2784 try:
2785 mod = imp.load_source(x[0], x[1])
2785 mod = imp.load_source(x[0], x[1])
2786 except Exception, inst:
2786 except Exception, inst:
2787 on_exception(Exception, inst)
2787 on_exception(Exception, inst)
2788 continue
2788 continue
2789 else:
2789 else:
2790 def importh(name):
2790 def importh(name):
2791 mod = __import__(name)
2791 mod = __import__(name)
2792 components = name.split('.')
2792 components = name.split('.')
2793 for comp in components[1:]:
2793 for comp in components[1:]:
2794 mod = getattr(mod, comp)
2794 mod = getattr(mod, comp)
2795 return mod
2795 return mod
2796 try:
2796 try:
2797 try:
2797 try:
2798 mod = importh("hgext." + x[0])
2798 mod = importh("hgext." + x[0])
2799 except ImportError:
2799 except ImportError:
2800 mod = importh(x[0])
2800 mod = importh(x[0])
2801 except Exception, inst:
2801 except Exception, inst:
2802 on_exception(Exception, inst)
2802 on_exception(Exception, inst)
2803 continue
2803 continue
2804
2804
2805 external.append(mod)
2805 external.append(mod)
2806 for x in external:
2806 for x in external:
2807 cmdtable = getattr(x, 'cmdtable', {})
2807 cmdtable = getattr(x, 'cmdtable', {})
2808 for t in cmdtable:
2808 for t in cmdtable:
2809 if t in table:
2809 if t in table:
2810 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
2810 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
2811 table.update(cmdtable)
2811 table.update(cmdtable)
2812
2812
2813 try:
2813 try:
2814 cmd, func, args, options, cmdoptions = parse(u, args)
2814 cmd, func, args, options, cmdoptions = parse(u, args)
2815 if options["time"]:
2815 if options["time"]:
2816 def get_times():
2816 def get_times():
2817 t = os.times()
2817 t = os.times()
2818 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2818 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2819 t = (t[0], t[1], t[2], t[3], time.clock())
2819 t = (t[0], t[1], t[2], t[3], time.clock())
2820 return t
2820 return t
2821 s = get_times()
2821 s = get_times()
2822 def print_time():
2822 def print_time():
2823 t = get_times()
2823 t = get_times()
2824 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2824 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2825 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2825 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2826 atexit.register(print_time)
2826 atexit.register(print_time)
2827
2827
2828 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2828 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2829 not options["noninteractive"])
2829 not options["noninteractive"])
2830
2830
2831 # enter the debugger before command execution
2831 # enter the debugger before command execution
2832 if options['debugger']:
2832 if options['debugger']:
2833 pdb.set_trace()
2833 pdb.set_trace()
2834
2834
2835 try:
2835 try:
2836 if options['cwd']:
2836 if options['cwd']:
2837 try:
2837 try:
2838 os.chdir(options['cwd'])
2838 os.chdir(options['cwd'])
2839 except OSError, inst:
2839 except OSError, inst:
2840 raise util.Abort('%s: %s' %
2840 raise util.Abort('%s: %s' %
2841 (options['cwd'], inst.strerror))
2841 (options['cwd'], inst.strerror))
2842
2842
2843 path = options["repository"] or ""
2843 path = options["repository"] or ""
2844 repo = path and hg.repository(u, path=path) or None
2844 repo = path and hg.repository(u, path=path) or None
2845
2845
2846 if options['help']:
2846 if options['help']:
2847 help_(u, cmd, options['version'])
2847 help_(u, cmd, options['version'])
2848 sys.exit(0)
2848 sys.exit(0)
2849 elif options['version']:
2849 elif options['version']:
2850 show_version(u)
2850 show_version(u)
2851 sys.exit(0)
2851 sys.exit(0)
2852 elif not cmd:
2852 elif not cmd:
2853 help_(u, 'shortlist')
2853 help_(u, 'shortlist')
2854 sys.exit(0)
2854 sys.exit(0)
2855
2855
2856 if cmd not in norepo.split():
2856 if cmd not in norepo.split():
2857 try:
2857 try:
2858 if not repo:
2858 if not repo:
2859 repo = hg.repository(u, path=path)
2859 repo = hg.repository(u, path=path)
2860 u = repo.ui
2860 u = repo.ui
2861 for x in external:
2861 for x in external:
2862 if hasattr(x, 'reposetup'):
2862 if hasattr(x, 'reposetup'):
2863 x.reposetup(u, repo)
2863 x.reposetup(u, repo)
2864 except hg.RepoError:
2864 except hg.RepoError:
2865 if cmd not in optionalrepo.split():
2865 if cmd not in optionalrepo.split():
2866 raise
2866 raise
2867 d = lambda: func(u, repo, *args, **cmdoptions)
2867 d = lambda: func(u, repo, *args, **cmdoptions)
2868 else:
2868 else:
2869 d = lambda: func(u, *args, **cmdoptions)
2869 d = lambda: func(u, *args, **cmdoptions)
2870
2870
2871 try:
2871 try:
2872 if options['profile']:
2872 if options['profile']:
2873 import hotshot, hotshot.stats
2873 import hotshot, hotshot.stats
2874 prof = hotshot.Profile("hg.prof")
2874 prof = hotshot.Profile("hg.prof")
2875 try:
2875 try:
2876 try:
2876 try:
2877 return prof.runcall(d)
2877 return prof.runcall(d)
2878 except:
2878 except:
2879 try:
2879 try:
2880 u.warn(_('exception raised - generating '
2880 u.warn(_('exception raised - generating '
2881 'profile anyway\n'))
2881 'profile anyway\n'))
2882 except:
2882 except:
2883 pass
2883 pass
2884 raise
2884 raise
2885 finally:
2885 finally:
2886 prof.close()
2886 prof.close()
2887 stats = hotshot.stats.load("hg.prof")
2887 stats = hotshot.stats.load("hg.prof")
2888 stats.strip_dirs()
2888 stats.strip_dirs()
2889 stats.sort_stats('time', 'calls')
2889 stats.sort_stats('time', 'calls')
2890 stats.print_stats(40)
2890 stats.print_stats(40)
2891 else:
2891 else:
2892 return d()
2892 return d()
2893 finally:
2893 finally:
2894 u.flush()
2894 u.flush()
2895 except:
2895 except:
2896 # enter the debugger when we hit an exception
2896 # enter the debugger when we hit an exception
2897 if options['debugger']:
2897 if options['debugger']:
2898 pdb.post_mortem(sys.exc_info()[2])
2898 pdb.post_mortem(sys.exc_info()[2])
2899 if options['traceback']:
2899 if options['traceback']:
2900 traceback.print_exc()
2900 traceback.print_exc()
2901 raise
2901 raise
2902 except ParseError, inst:
2902 except ParseError, inst:
2903 if inst.args[0]:
2903 if inst.args[0]:
2904 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2904 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2905 help_(u, inst.args[0])
2905 help_(u, inst.args[0])
2906 else:
2906 else:
2907 u.warn(_("hg: %s\n") % inst.args[1])
2907 u.warn(_("hg: %s\n") % inst.args[1])
2908 help_(u, 'shortlist')
2908 help_(u, 'shortlist')
2909 sys.exit(-1)
2909 sys.exit(-1)
2910 except AmbiguousCommand, inst:
2910 except AmbiguousCommand, inst:
2911 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
2911 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
2912 (inst.args[0], " ".join(inst.args[1])))
2912 (inst.args[0], " ".join(inst.args[1])))
2913 sys.exit(1)
2913 sys.exit(1)
2914 except UnknownCommand, inst:
2914 except UnknownCommand, inst:
2915 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2915 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2916 help_(u, 'shortlist')
2916 help_(u, 'shortlist')
2917 sys.exit(1)
2917 sys.exit(1)
2918 except hg.RepoError, inst:
2918 except hg.RepoError, inst:
2919 u.warn(_("abort: "), inst, "!\n")
2919 u.warn(_("abort: "), inst, "!\n")
2920 except revlog.RevlogError, inst:
2920 except revlog.RevlogError, inst:
2921 u.warn(_("abort: "), inst, "!\n")
2921 u.warn(_("abort: "), inst, "!\n")
2922 except SignalInterrupt:
2922 except SignalInterrupt:
2923 u.warn(_("killed!\n"))
2923 u.warn(_("killed!\n"))
2924 except KeyboardInterrupt:
2924 except KeyboardInterrupt:
2925 try:
2925 try:
2926 u.warn(_("interrupted!\n"))
2926 u.warn(_("interrupted!\n"))
2927 except IOError, inst:
2927 except IOError, inst:
2928 if inst.errno == errno.EPIPE:
2928 if inst.errno == errno.EPIPE:
2929 if u.debugflag:
2929 if u.debugflag:
2930 u.warn(_("\nbroken pipe\n"))
2930 u.warn(_("\nbroken pipe\n"))
2931 else:
2931 else:
2932 raise
2932 raise
2933 except IOError, inst:
2933 except IOError, inst:
2934 if hasattr(inst, "code"):
2934 if hasattr(inst, "code"):
2935 u.warn(_("abort: %s\n") % inst)
2935 u.warn(_("abort: %s\n") % inst)
2936 elif hasattr(inst, "reason"):
2936 elif hasattr(inst, "reason"):
2937 u.warn(_("abort: error: %s\n") % inst.reason[1])
2937 u.warn(_("abort: error: %s\n") % inst.reason[1])
2938 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2938 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2939 if u.debugflag:
2939 if u.debugflag:
2940 u.warn(_("broken pipe\n"))
2940 u.warn(_("broken pipe\n"))
2941 elif getattr(inst, "strerror", None):
2941 elif getattr(inst, "strerror", None):
2942 if getattr(inst, "filename", None):
2942 if getattr(inst, "filename", None):
2943 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
2943 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
2944 else:
2944 else:
2945 u.warn(_("abort: %s\n") % inst.strerror)
2945 u.warn(_("abort: %s\n") % inst.strerror)
2946 else:
2946 else:
2947 raise
2947 raise
2948 except OSError, inst:
2948 except OSError, inst:
2949 if hasattr(inst, "filename"):
2949 if hasattr(inst, "filename"):
2950 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
2950 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
2951 else:
2951 else:
2952 u.warn(_("abort: %s\n") % inst.strerror)
2952 u.warn(_("abort: %s\n") % inst.strerror)
2953 except util.Abort, inst:
2953 except util.Abort, inst:
2954 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
2954 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
2955 sys.exit(1)
2955 sys.exit(1)
2956 except TypeError, inst:
2956 except TypeError, inst:
2957 # was this an argument error?
2957 # was this an argument error?
2958 tb = traceback.extract_tb(sys.exc_info()[2])
2958 tb = traceback.extract_tb(sys.exc_info()[2])
2959 if len(tb) > 2: # no
2959 if len(tb) > 2: # no
2960 raise
2960 raise
2961 u.debug(inst, "\n")
2961 u.debug(inst, "\n")
2962 u.warn(_("%s: invalid arguments\n") % cmd)
2962 u.warn(_("%s: invalid arguments\n") % cmd)
2963 help_(u, cmd)
2963 help_(u, cmd)
2964 except SystemExit:
2964 except SystemExit:
2965 # don't catch this in the catch-all below
2965 # don't catch this in the catch-all below
2966 raise
2966 raise
2967 except:
2967 except:
2968 u.warn(_("** unknown exception encountered, details follow\n"))
2968 u.warn(_("** unknown exception encountered, details follow\n"))
2969 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2969 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2970 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
2970 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
2971 % version.get_version())
2971 % version.get_version())
2972 raise
2972 raise
2973
2973
2974 sys.exit(-1)
2974 sys.exit(-1)
@@ -1,1895 +1,1895 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import struct, os, util
8 import struct, os, util
9 import filelog, manifest, changelog, dirstate, repo
9 import filelog, manifest, changelog, dirstate, repo
10 from node import *
10 from node import *
11 from i18n import gettext as _
11 from i18n import gettext as _
12 from demandload import *
12 from demandload import *
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
14
14
15 class localrepository(object):
15 class localrepository(object):
16 def __del__(self):
16 def __del__(self):
17 self.transhandle = None
17 self.transhandle = None
18 def __init__(self, parentui, path=None, create=0):
18 def __init__(self, parentui, path=None, create=0):
19 if not path:
19 if not path:
20 p = os.getcwd()
20 p = os.getcwd()
21 while not os.path.isdir(os.path.join(p, ".hg")):
21 while not os.path.isdir(os.path.join(p, ".hg")):
22 oldp = p
22 oldp = p
23 p = os.path.dirname(p)
23 p = os.path.dirname(p)
24 if p == oldp:
24 if p == oldp:
25 raise repo.RepoError(_("no repo found"))
25 raise repo.RepoError(_("no repo found"))
26 path = p
26 path = p
27 self.path = os.path.join(path, ".hg")
27 self.path = os.path.join(path, ".hg")
28
28
29 if not create and not os.path.isdir(self.path):
29 if not create and not os.path.isdir(self.path):
30 raise repo.RepoError(_("repository %s not found") % path)
30 raise repo.RepoError(_("repository %s not found") % path)
31
31
32 self.root = os.path.abspath(path)
32 self.root = os.path.abspath(path)
33 self.ui = ui.ui(parentui=parentui)
33 self.ui = ui.ui(parentui=parentui)
34 self.opener = util.opener(self.path)
34 self.opener = util.opener(self.path)
35 self.wopener = util.opener(self.root)
35 self.wopener = util.opener(self.root)
36 self.manifest = manifest.manifest(self.opener)
36 self.manifest = manifest.manifest(self.opener)
37 self.changelog = changelog.changelog(self.opener)
37 self.changelog = changelog.changelog(self.opener)
38 self.tagscache = None
38 self.tagscache = None
39 self.nodetagscache = None
39 self.nodetagscache = None
40 self.encodepats = None
40 self.encodepats = None
41 self.decodepats = None
41 self.decodepats = None
42 self.transhandle = None
42 self.transhandle = None
43
43
44 if create:
44 if create:
45 os.mkdir(self.path)
45 os.mkdir(self.path)
46 os.mkdir(self.join("data"))
46 os.mkdir(self.join("data"))
47
47
48 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
48 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
49 try:
49 try:
50 self.ui.readconfig(self.join("hgrc"))
50 self.ui.readconfig(self.join("hgrc"), self.root)
51 except IOError:
51 except IOError:
52 pass
52 pass
53
53
54 def hook(self, name, throw=False, **args):
54 def hook(self, name, throw=False, **args):
55 def runhook(name, cmd):
55 def runhook(name, cmd):
56 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
56 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
57 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
57 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
58 r = util.system(cmd, environ=env, cwd=self.root)
58 r = util.system(cmd, environ=env, cwd=self.root)
59 if r:
59 if r:
60 desc, r = util.explain_exit(r)
60 desc, r = util.explain_exit(r)
61 if throw:
61 if throw:
62 raise util.Abort(_('%s hook %s') % (name, desc))
62 raise util.Abort(_('%s hook %s') % (name, desc))
63 self.ui.warn(_('error: %s hook %s\n') % (name, desc))
63 self.ui.warn(_('error: %s hook %s\n') % (name, desc))
64 return False
64 return False
65 return True
65 return True
66
66
67 r = True
67 r = True
68 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
68 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
69 if hname.split(".", 1)[0] == name and cmd]
69 if hname.split(".", 1)[0] == name and cmd]
70 hooks.sort()
70 hooks.sort()
71 for hname, cmd in hooks:
71 for hname, cmd in hooks:
72 r = runhook(hname, cmd) and r
72 r = runhook(hname, cmd) and r
73 return r
73 return r
74
74
75 def tags(self):
75 def tags(self):
76 '''return a mapping of tag to node'''
76 '''return a mapping of tag to node'''
77 if not self.tagscache:
77 if not self.tagscache:
78 self.tagscache = {}
78 self.tagscache = {}
79 def addtag(self, k, n):
79 def addtag(self, k, n):
80 try:
80 try:
81 bin_n = bin(n)
81 bin_n = bin(n)
82 except TypeError:
82 except TypeError:
83 bin_n = ''
83 bin_n = ''
84 self.tagscache[k.strip()] = bin_n
84 self.tagscache[k.strip()] = bin_n
85
85
86 try:
86 try:
87 # read each head of the tags file, ending with the tip
87 # read each head of the tags file, ending with the tip
88 # and add each tag found to the map, with "newer" ones
88 # and add each tag found to the map, with "newer" ones
89 # taking precedence
89 # taking precedence
90 fl = self.file(".hgtags")
90 fl = self.file(".hgtags")
91 h = fl.heads()
91 h = fl.heads()
92 h.reverse()
92 h.reverse()
93 for r in h:
93 for r in h:
94 for l in fl.read(r).splitlines():
94 for l in fl.read(r).splitlines():
95 if l:
95 if l:
96 n, k = l.split(" ", 1)
96 n, k = l.split(" ", 1)
97 addtag(self, k, n)
97 addtag(self, k, n)
98 except KeyError:
98 except KeyError:
99 pass
99 pass
100
100
101 try:
101 try:
102 f = self.opener("localtags")
102 f = self.opener("localtags")
103 for l in f:
103 for l in f:
104 n, k = l.split(" ", 1)
104 n, k = l.split(" ", 1)
105 addtag(self, k, n)
105 addtag(self, k, n)
106 except IOError:
106 except IOError:
107 pass
107 pass
108
108
109 self.tagscache['tip'] = self.changelog.tip()
109 self.tagscache['tip'] = self.changelog.tip()
110
110
111 return self.tagscache
111 return self.tagscache
112
112
113 def tagslist(self):
113 def tagslist(self):
114 '''return a list of tags ordered by revision'''
114 '''return a list of tags ordered by revision'''
115 l = []
115 l = []
116 for t, n in self.tags().items():
116 for t, n in self.tags().items():
117 try:
117 try:
118 r = self.changelog.rev(n)
118 r = self.changelog.rev(n)
119 except:
119 except:
120 r = -2 # sort to the beginning of the list if unknown
120 r = -2 # sort to the beginning of the list if unknown
121 l.append((r, t, n))
121 l.append((r, t, n))
122 l.sort()
122 l.sort()
123 return [(t, n) for r, t, n in l]
123 return [(t, n) for r, t, n in l]
124
124
125 def nodetags(self, node):
125 def nodetags(self, node):
126 '''return the tags associated with a node'''
126 '''return the tags associated with a node'''
127 if not self.nodetagscache:
127 if not self.nodetagscache:
128 self.nodetagscache = {}
128 self.nodetagscache = {}
129 for t, n in self.tags().items():
129 for t, n in self.tags().items():
130 self.nodetagscache.setdefault(n, []).append(t)
130 self.nodetagscache.setdefault(n, []).append(t)
131 return self.nodetagscache.get(node, [])
131 return self.nodetagscache.get(node, [])
132
132
133 def lookup(self, key):
133 def lookup(self, key):
134 try:
134 try:
135 return self.tags()[key]
135 return self.tags()[key]
136 except KeyError:
136 except KeyError:
137 try:
137 try:
138 return self.changelog.lookup(key)
138 return self.changelog.lookup(key)
139 except:
139 except:
140 raise repo.RepoError(_("unknown revision '%s'") % key)
140 raise repo.RepoError(_("unknown revision '%s'") % key)
141
141
142 def dev(self):
142 def dev(self):
143 return os.stat(self.path).st_dev
143 return os.stat(self.path).st_dev
144
144
145 def local(self):
145 def local(self):
146 return True
146 return True
147
147
148 def join(self, f):
148 def join(self, f):
149 return os.path.join(self.path, f)
149 return os.path.join(self.path, f)
150
150
151 def wjoin(self, f):
151 def wjoin(self, f):
152 return os.path.join(self.root, f)
152 return os.path.join(self.root, f)
153
153
154 def file(self, f):
154 def file(self, f):
155 if f[0] == '/':
155 if f[0] == '/':
156 f = f[1:]
156 f = f[1:]
157 return filelog.filelog(self.opener, f)
157 return filelog.filelog(self.opener, f)
158
158
159 def getcwd(self):
159 def getcwd(self):
160 return self.dirstate.getcwd()
160 return self.dirstate.getcwd()
161
161
162 def wfile(self, f, mode='r'):
162 def wfile(self, f, mode='r'):
163 return self.wopener(f, mode)
163 return self.wopener(f, mode)
164
164
165 def wread(self, filename):
165 def wread(self, filename):
166 if self.encodepats == None:
166 if self.encodepats == None:
167 l = []
167 l = []
168 for pat, cmd in self.ui.configitems("encode"):
168 for pat, cmd in self.ui.configitems("encode"):
169 mf = util.matcher("", "/", [pat], [], [])[1]
169 mf = util.matcher("", "/", [pat], [], [])[1]
170 l.append((mf, cmd))
170 l.append((mf, cmd))
171 self.encodepats = l
171 self.encodepats = l
172
172
173 data = self.wopener(filename, 'r').read()
173 data = self.wopener(filename, 'r').read()
174
174
175 for mf, cmd in self.encodepats:
175 for mf, cmd in self.encodepats:
176 if mf(filename):
176 if mf(filename):
177 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
177 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
178 data = util.filter(data, cmd)
178 data = util.filter(data, cmd)
179 break
179 break
180
180
181 return data
181 return data
182
182
183 def wwrite(self, filename, data, fd=None):
183 def wwrite(self, filename, data, fd=None):
184 if self.decodepats == None:
184 if self.decodepats == None:
185 l = []
185 l = []
186 for pat, cmd in self.ui.configitems("decode"):
186 for pat, cmd in self.ui.configitems("decode"):
187 mf = util.matcher("", "/", [pat], [], [])[1]
187 mf = util.matcher("", "/", [pat], [], [])[1]
188 l.append((mf, cmd))
188 l.append((mf, cmd))
189 self.decodepats = l
189 self.decodepats = l
190
190
191 for mf, cmd in self.decodepats:
191 for mf, cmd in self.decodepats:
192 if mf(filename):
192 if mf(filename):
193 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
193 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
194 data = util.filter(data, cmd)
194 data = util.filter(data, cmd)
195 break
195 break
196
196
197 if fd:
197 if fd:
198 return fd.write(data)
198 return fd.write(data)
199 return self.wopener(filename, 'w').write(data)
199 return self.wopener(filename, 'w').write(data)
200
200
201 def transaction(self):
201 def transaction(self):
202 tr = self.transhandle
202 tr = self.transhandle
203 if tr != None and tr.running():
203 if tr != None and tr.running():
204 return tr.nest()
204 return tr.nest()
205
205
206 # save dirstate for undo
206 # save dirstate for undo
207 try:
207 try:
208 ds = self.opener("dirstate").read()
208 ds = self.opener("dirstate").read()
209 except IOError:
209 except IOError:
210 ds = ""
210 ds = ""
211 self.opener("journal.dirstate", "w").write(ds)
211 self.opener("journal.dirstate", "w").write(ds)
212
212
213 tr = transaction.transaction(self.ui.warn, self.opener,
213 tr = transaction.transaction(self.ui.warn, self.opener,
214 self.join("journal"),
214 self.join("journal"),
215 aftertrans(self.path))
215 aftertrans(self.path))
216 self.transhandle = tr
216 self.transhandle = tr
217 return tr
217 return tr
218
218
219 def recover(self):
219 def recover(self):
220 l = self.lock()
220 l = self.lock()
221 if os.path.exists(self.join("journal")):
221 if os.path.exists(self.join("journal")):
222 self.ui.status(_("rolling back interrupted transaction\n"))
222 self.ui.status(_("rolling back interrupted transaction\n"))
223 transaction.rollback(self.opener, self.join("journal"))
223 transaction.rollback(self.opener, self.join("journal"))
224 self.reload()
224 self.reload()
225 return True
225 return True
226 else:
226 else:
227 self.ui.warn(_("no interrupted transaction available\n"))
227 self.ui.warn(_("no interrupted transaction available\n"))
228 return False
228 return False
229
229
230 def undo(self, wlock=None):
230 def undo(self, wlock=None):
231 if not wlock:
231 if not wlock:
232 wlock = self.wlock()
232 wlock = self.wlock()
233 l = self.lock()
233 l = self.lock()
234 if os.path.exists(self.join("undo")):
234 if os.path.exists(self.join("undo")):
235 self.ui.status(_("rolling back last transaction\n"))
235 self.ui.status(_("rolling back last transaction\n"))
236 transaction.rollback(self.opener, self.join("undo"))
236 transaction.rollback(self.opener, self.join("undo"))
237 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
237 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
238 self.reload()
238 self.reload()
239 self.wreload()
239 self.wreload()
240 else:
240 else:
241 self.ui.warn(_("no undo information available\n"))
241 self.ui.warn(_("no undo information available\n"))
242
242
243 def wreload(self):
243 def wreload(self):
244 self.dirstate.read()
244 self.dirstate.read()
245
245
246 def reload(self):
246 def reload(self):
247 self.changelog.load()
247 self.changelog.load()
248 self.manifest.load()
248 self.manifest.load()
249 self.tagscache = None
249 self.tagscache = None
250 self.nodetagscache = None
250 self.nodetagscache = None
251
251
252 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None):
252 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None):
253 try:
253 try:
254 l = lock.lock(self.join(lockname), 0, releasefn)
254 l = lock.lock(self.join(lockname), 0, releasefn)
255 except lock.LockHeld, inst:
255 except lock.LockHeld, inst:
256 if not wait:
256 if not wait:
257 raise inst
257 raise inst
258 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
258 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
259 try:
259 try:
260 # default to 600 seconds timeout
260 # default to 600 seconds timeout
261 l = lock.lock(self.join(lockname),
261 l = lock.lock(self.join(lockname),
262 int(self.ui.config("ui", "timeout") or 600),
262 int(self.ui.config("ui", "timeout") or 600),
263 releasefn)
263 releasefn)
264 except lock.LockHeld, inst:
264 except lock.LockHeld, inst:
265 raise util.Abort(_("timeout while waiting for "
265 raise util.Abort(_("timeout while waiting for "
266 "lock held by %s") % inst.args[0])
266 "lock held by %s") % inst.args[0])
267 if acquirefn:
267 if acquirefn:
268 acquirefn()
268 acquirefn()
269 return l
269 return l
270
270
271 def lock(self, wait=1):
271 def lock(self, wait=1):
272 return self.do_lock("lock", wait, acquirefn=self.reload)
272 return self.do_lock("lock", wait, acquirefn=self.reload)
273
273
274 def wlock(self, wait=1):
274 def wlock(self, wait=1):
275 return self.do_lock("wlock", wait,
275 return self.do_lock("wlock", wait,
276 self.dirstate.write,
276 self.dirstate.write,
277 self.wreload)
277 self.wreload)
278
278
279 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
279 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
280 "determine whether a new filenode is needed"
280 "determine whether a new filenode is needed"
281 fp1 = manifest1.get(filename, nullid)
281 fp1 = manifest1.get(filename, nullid)
282 fp2 = manifest2.get(filename, nullid)
282 fp2 = manifest2.get(filename, nullid)
283
283
284 if fp2 != nullid:
284 if fp2 != nullid:
285 # is one parent an ancestor of the other?
285 # is one parent an ancestor of the other?
286 fpa = filelog.ancestor(fp1, fp2)
286 fpa = filelog.ancestor(fp1, fp2)
287 if fpa == fp1:
287 if fpa == fp1:
288 fp1, fp2 = fp2, nullid
288 fp1, fp2 = fp2, nullid
289 elif fpa == fp2:
289 elif fpa == fp2:
290 fp2 = nullid
290 fp2 = nullid
291
291
292 # is the file unmodified from the parent? report existing entry
292 # is the file unmodified from the parent? report existing entry
293 if fp2 == nullid and text == filelog.read(fp1):
293 if fp2 == nullid and text == filelog.read(fp1):
294 return (fp1, None, None)
294 return (fp1, None, None)
295
295
296 return (None, fp1, fp2)
296 return (None, fp1, fp2)
297
297
298 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
298 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
299 orig_parent = self.dirstate.parents()[0] or nullid
299 orig_parent = self.dirstate.parents()[0] or nullid
300 p1 = p1 or self.dirstate.parents()[0] or nullid
300 p1 = p1 or self.dirstate.parents()[0] or nullid
301 p2 = p2 or self.dirstate.parents()[1] or nullid
301 p2 = p2 or self.dirstate.parents()[1] or nullid
302 c1 = self.changelog.read(p1)
302 c1 = self.changelog.read(p1)
303 c2 = self.changelog.read(p2)
303 c2 = self.changelog.read(p2)
304 m1 = self.manifest.read(c1[0])
304 m1 = self.manifest.read(c1[0])
305 mf1 = self.manifest.readflags(c1[0])
305 mf1 = self.manifest.readflags(c1[0])
306 m2 = self.manifest.read(c2[0])
306 m2 = self.manifest.read(c2[0])
307 changed = []
307 changed = []
308
308
309 if orig_parent == p1:
309 if orig_parent == p1:
310 update_dirstate = 1
310 update_dirstate = 1
311 else:
311 else:
312 update_dirstate = 0
312 update_dirstate = 0
313
313
314 if not wlock:
314 if not wlock:
315 wlock = self.wlock()
315 wlock = self.wlock()
316 l = self.lock()
316 l = self.lock()
317 tr = self.transaction()
317 tr = self.transaction()
318 mm = m1.copy()
318 mm = m1.copy()
319 mfm = mf1.copy()
319 mfm = mf1.copy()
320 linkrev = self.changelog.count()
320 linkrev = self.changelog.count()
321 for f in files:
321 for f in files:
322 try:
322 try:
323 t = self.wread(f)
323 t = self.wread(f)
324 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
324 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
325 r = self.file(f)
325 r = self.file(f)
326 mfm[f] = tm
326 mfm[f] = tm
327
327
328 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
328 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
329 if entry:
329 if entry:
330 mm[f] = entry
330 mm[f] = entry
331 continue
331 continue
332
332
333 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
333 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
334 changed.append(f)
334 changed.append(f)
335 if update_dirstate:
335 if update_dirstate:
336 self.dirstate.update([f], "n")
336 self.dirstate.update([f], "n")
337 except IOError:
337 except IOError:
338 try:
338 try:
339 del mm[f]
339 del mm[f]
340 del mfm[f]
340 del mfm[f]
341 if update_dirstate:
341 if update_dirstate:
342 self.dirstate.forget([f])
342 self.dirstate.forget([f])
343 except:
343 except:
344 # deleted from p2?
344 # deleted from p2?
345 pass
345 pass
346
346
347 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
347 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
348 user = user or self.ui.username()
348 user = user or self.ui.username()
349 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
349 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
350 tr.close()
350 tr.close()
351 if update_dirstate:
351 if update_dirstate:
352 self.dirstate.setparents(n, nullid)
352 self.dirstate.setparents(n, nullid)
353
353
354 def commit(self, files=None, text="", user=None, date=None,
354 def commit(self, files=None, text="", user=None, date=None,
355 match=util.always, force=False, lock=None, wlock=None):
355 match=util.always, force=False, lock=None, wlock=None):
356 commit = []
356 commit = []
357 remove = []
357 remove = []
358 changed = []
358 changed = []
359
359
360 if files:
360 if files:
361 for f in files:
361 for f in files:
362 s = self.dirstate.state(f)
362 s = self.dirstate.state(f)
363 if s in 'nmai':
363 if s in 'nmai':
364 commit.append(f)
364 commit.append(f)
365 elif s == 'r':
365 elif s == 'r':
366 remove.append(f)
366 remove.append(f)
367 else:
367 else:
368 self.ui.warn(_("%s not tracked!\n") % f)
368 self.ui.warn(_("%s not tracked!\n") % f)
369 else:
369 else:
370 modified, added, removed, deleted, unknown = self.changes(match=match)
370 modified, added, removed, deleted, unknown = self.changes(match=match)
371 commit = modified + added
371 commit = modified + added
372 remove = removed
372 remove = removed
373
373
374 p1, p2 = self.dirstate.parents()
374 p1, p2 = self.dirstate.parents()
375 c1 = self.changelog.read(p1)
375 c1 = self.changelog.read(p1)
376 c2 = self.changelog.read(p2)
376 c2 = self.changelog.read(p2)
377 m1 = self.manifest.read(c1[0])
377 m1 = self.manifest.read(c1[0])
378 mf1 = self.manifest.readflags(c1[0])
378 mf1 = self.manifest.readflags(c1[0])
379 m2 = self.manifest.read(c2[0])
379 m2 = self.manifest.read(c2[0])
380
380
381 if not commit and not remove and not force and p2 == nullid:
381 if not commit and not remove and not force and p2 == nullid:
382 self.ui.status(_("nothing changed\n"))
382 self.ui.status(_("nothing changed\n"))
383 return None
383 return None
384
384
385 xp1 = hex(p1)
385 xp1 = hex(p1)
386 if p2 == nullid: xp2 = ''
386 if p2 == nullid: xp2 = ''
387 else: xp2 = hex(p2)
387 else: xp2 = hex(p2)
388
388
389 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
389 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
390
390
391 if not wlock:
391 if not wlock:
392 wlock = self.wlock()
392 wlock = self.wlock()
393 if not lock:
393 if not lock:
394 lock = self.lock()
394 lock = self.lock()
395 tr = self.transaction()
395 tr = self.transaction()
396
396
397 # check in files
397 # check in files
398 new = {}
398 new = {}
399 linkrev = self.changelog.count()
399 linkrev = self.changelog.count()
400 commit.sort()
400 commit.sort()
401 for f in commit:
401 for f in commit:
402 self.ui.note(f + "\n")
402 self.ui.note(f + "\n")
403 try:
403 try:
404 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
404 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
405 t = self.wread(f)
405 t = self.wread(f)
406 except IOError:
406 except IOError:
407 self.ui.warn(_("trouble committing %s!\n") % f)
407 self.ui.warn(_("trouble committing %s!\n") % f)
408 raise
408 raise
409
409
410 r = self.file(f)
410 r = self.file(f)
411
411
412 meta = {}
412 meta = {}
413 cp = self.dirstate.copied(f)
413 cp = self.dirstate.copied(f)
414 if cp:
414 if cp:
415 meta["copy"] = cp
415 meta["copy"] = cp
416 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
416 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
417 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
417 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
418 fp1, fp2 = nullid, nullid
418 fp1, fp2 = nullid, nullid
419 else:
419 else:
420 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
420 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
421 if entry:
421 if entry:
422 new[f] = entry
422 new[f] = entry
423 continue
423 continue
424
424
425 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
425 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
426 # remember what we've added so that we can later calculate
426 # remember what we've added so that we can later calculate
427 # the files to pull from a set of changesets
427 # the files to pull from a set of changesets
428 changed.append(f)
428 changed.append(f)
429
429
430 # update manifest
430 # update manifest
431 m1 = m1.copy()
431 m1 = m1.copy()
432 m1.update(new)
432 m1.update(new)
433 for f in remove:
433 for f in remove:
434 if f in m1:
434 if f in m1:
435 del m1[f]
435 del m1[f]
436 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
436 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
437 (new, remove))
437 (new, remove))
438
438
439 # add changeset
439 # add changeset
440 new = new.keys()
440 new = new.keys()
441 new.sort()
441 new.sort()
442
442
443 if not text:
443 if not text:
444 edittext = [""]
444 edittext = [""]
445 if p2 != nullid:
445 if p2 != nullid:
446 edittext.append("HG: branch merge")
446 edittext.append("HG: branch merge")
447 edittext.extend(["HG: changed %s" % f for f in changed])
447 edittext.extend(["HG: changed %s" % f for f in changed])
448 edittext.extend(["HG: removed %s" % f for f in remove])
448 edittext.extend(["HG: removed %s" % f for f in remove])
449 if not changed and not remove:
449 if not changed and not remove:
450 edittext.append("HG: no files changed")
450 edittext.append("HG: no files changed")
451 edittext.append("")
451 edittext.append("")
452 # run editor in the repository root
452 # run editor in the repository root
453 olddir = os.getcwd()
453 olddir = os.getcwd()
454 os.chdir(self.root)
454 os.chdir(self.root)
455 edittext = self.ui.edit("\n".join(edittext))
455 edittext = self.ui.edit("\n".join(edittext))
456 os.chdir(olddir)
456 os.chdir(olddir)
457 if not edittext.rstrip():
457 if not edittext.rstrip():
458 return None
458 return None
459 text = edittext
459 text = edittext
460
460
461 user = user or self.ui.username()
461 user = user or self.ui.username()
462 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
462 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
463 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
463 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
464 parent2=xp2)
464 parent2=xp2)
465 tr.close()
465 tr.close()
466
466
467 self.dirstate.setparents(n)
467 self.dirstate.setparents(n)
468 self.dirstate.update(new, "n")
468 self.dirstate.update(new, "n")
469 self.dirstate.forget(remove)
469 self.dirstate.forget(remove)
470
470
471 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
471 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
472 return n
472 return n
473
473
474 def walk(self, node=None, files=[], match=util.always):
474 def walk(self, node=None, files=[], match=util.always):
475 if node:
475 if node:
476 fdict = dict.fromkeys(files)
476 fdict = dict.fromkeys(files)
477 for fn in self.manifest.read(self.changelog.read(node)[0]):
477 for fn in self.manifest.read(self.changelog.read(node)[0]):
478 fdict.pop(fn, None)
478 fdict.pop(fn, None)
479 if match(fn):
479 if match(fn):
480 yield 'm', fn
480 yield 'm', fn
481 for fn in fdict:
481 for fn in fdict:
482 self.ui.warn(_('%s: No such file in rev %s\n') % (
482 self.ui.warn(_('%s: No such file in rev %s\n') % (
483 util.pathto(self.getcwd(), fn), short(node)))
483 util.pathto(self.getcwd(), fn), short(node)))
484 else:
484 else:
485 for src, fn in self.dirstate.walk(files, match):
485 for src, fn in self.dirstate.walk(files, match):
486 yield src, fn
486 yield src, fn
487
487
488 def changes(self, node1=None, node2=None, files=[], match=util.always,
488 def changes(self, node1=None, node2=None, files=[], match=util.always,
489 wlock=None):
489 wlock=None):
490 """return changes between two nodes or node and working directory
490 """return changes between two nodes or node and working directory
491
491
492 If node1 is None, use the first dirstate parent instead.
492 If node1 is None, use the first dirstate parent instead.
493 If node2 is None, compare node1 with working directory.
493 If node2 is None, compare node1 with working directory.
494 """
494 """
495
495
496 def fcmp(fn, mf):
496 def fcmp(fn, mf):
497 t1 = self.wread(fn)
497 t1 = self.wread(fn)
498 t2 = self.file(fn).read(mf.get(fn, nullid))
498 t2 = self.file(fn).read(mf.get(fn, nullid))
499 return cmp(t1, t2)
499 return cmp(t1, t2)
500
500
501 def mfmatches(node):
501 def mfmatches(node):
502 change = self.changelog.read(node)
502 change = self.changelog.read(node)
503 mf = dict(self.manifest.read(change[0]))
503 mf = dict(self.manifest.read(change[0]))
504 for fn in mf.keys():
504 for fn in mf.keys():
505 if not match(fn):
505 if not match(fn):
506 del mf[fn]
506 del mf[fn]
507 return mf
507 return mf
508
508
509 if node1:
509 if node1:
510 # read the manifest from node1 before the manifest from node2,
510 # read the manifest from node1 before the manifest from node2,
511 # so that we'll hit the manifest cache if we're going through
511 # so that we'll hit the manifest cache if we're going through
512 # all the revisions in parent->child order.
512 # all the revisions in parent->child order.
513 mf1 = mfmatches(node1)
513 mf1 = mfmatches(node1)
514
514
515 # are we comparing the working directory?
515 # are we comparing the working directory?
516 if not node2:
516 if not node2:
517 if not wlock:
517 if not wlock:
518 try:
518 try:
519 wlock = self.wlock(wait=0)
519 wlock = self.wlock(wait=0)
520 except lock.LockException:
520 except lock.LockException:
521 wlock = None
521 wlock = None
522 lookup, modified, added, removed, deleted, unknown = (
522 lookup, modified, added, removed, deleted, unknown = (
523 self.dirstate.changes(files, match))
523 self.dirstate.changes(files, match))
524
524
525 # are we comparing working dir against its parent?
525 # are we comparing working dir against its parent?
526 if not node1:
526 if not node1:
527 if lookup:
527 if lookup:
528 # do a full compare of any files that might have changed
528 # do a full compare of any files that might have changed
529 mf2 = mfmatches(self.dirstate.parents()[0])
529 mf2 = mfmatches(self.dirstate.parents()[0])
530 for f in lookup:
530 for f in lookup:
531 if fcmp(f, mf2):
531 if fcmp(f, mf2):
532 modified.append(f)
532 modified.append(f)
533 elif wlock is not None:
533 elif wlock is not None:
534 self.dirstate.update([f], "n")
534 self.dirstate.update([f], "n")
535 else:
535 else:
536 # we are comparing working dir against non-parent
536 # we are comparing working dir against non-parent
537 # generate a pseudo-manifest for the working dir
537 # generate a pseudo-manifest for the working dir
538 mf2 = mfmatches(self.dirstate.parents()[0])
538 mf2 = mfmatches(self.dirstate.parents()[0])
539 for f in lookup + modified + added:
539 for f in lookup + modified + added:
540 mf2[f] = ""
540 mf2[f] = ""
541 for f in removed:
541 for f in removed:
542 if f in mf2:
542 if f in mf2:
543 del mf2[f]
543 del mf2[f]
544 else:
544 else:
545 # we are comparing two revisions
545 # we are comparing two revisions
546 deleted, unknown = [], []
546 deleted, unknown = [], []
547 mf2 = mfmatches(node2)
547 mf2 = mfmatches(node2)
548
548
549 if node1:
549 if node1:
550 # flush lists from dirstate before comparing manifests
550 # flush lists from dirstate before comparing manifests
551 modified, added = [], []
551 modified, added = [], []
552
552
553 for fn in mf2:
553 for fn in mf2:
554 if mf1.has_key(fn):
554 if mf1.has_key(fn):
555 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
555 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
556 modified.append(fn)
556 modified.append(fn)
557 del mf1[fn]
557 del mf1[fn]
558 else:
558 else:
559 added.append(fn)
559 added.append(fn)
560
560
561 removed = mf1.keys()
561 removed = mf1.keys()
562
562
563 # sort and return results:
563 # sort and return results:
564 for l in modified, added, removed, deleted, unknown:
564 for l in modified, added, removed, deleted, unknown:
565 l.sort()
565 l.sort()
566 return (modified, added, removed, deleted, unknown)
566 return (modified, added, removed, deleted, unknown)
567
567
568 def add(self, list, wlock=None):
568 def add(self, list, wlock=None):
569 if not wlock:
569 if not wlock:
570 wlock = self.wlock()
570 wlock = self.wlock()
571 for f in list:
571 for f in list:
572 p = self.wjoin(f)
572 p = self.wjoin(f)
573 if not os.path.exists(p):
573 if not os.path.exists(p):
574 self.ui.warn(_("%s does not exist!\n") % f)
574 self.ui.warn(_("%s does not exist!\n") % f)
575 elif not os.path.isfile(p):
575 elif not os.path.isfile(p):
576 self.ui.warn(_("%s not added: only files supported currently\n")
576 self.ui.warn(_("%s not added: only files supported currently\n")
577 % f)
577 % f)
578 elif self.dirstate.state(f) in 'an':
578 elif self.dirstate.state(f) in 'an':
579 self.ui.warn(_("%s already tracked!\n") % f)
579 self.ui.warn(_("%s already tracked!\n") % f)
580 else:
580 else:
581 self.dirstate.update([f], "a")
581 self.dirstate.update([f], "a")
582
582
583 def forget(self, list, wlock=None):
583 def forget(self, list, wlock=None):
584 if not wlock:
584 if not wlock:
585 wlock = self.wlock()
585 wlock = self.wlock()
586 for f in list:
586 for f in list:
587 if self.dirstate.state(f) not in 'ai':
587 if self.dirstate.state(f) not in 'ai':
588 self.ui.warn(_("%s not added!\n") % f)
588 self.ui.warn(_("%s not added!\n") % f)
589 else:
589 else:
590 self.dirstate.forget([f])
590 self.dirstate.forget([f])
591
591
592 def remove(self, list, unlink=False, wlock=None):
592 def remove(self, list, unlink=False, wlock=None):
593 if unlink:
593 if unlink:
594 for f in list:
594 for f in list:
595 try:
595 try:
596 util.unlink(self.wjoin(f))
596 util.unlink(self.wjoin(f))
597 except OSError, inst:
597 except OSError, inst:
598 if inst.errno != errno.ENOENT:
598 if inst.errno != errno.ENOENT:
599 raise
599 raise
600 if not wlock:
600 if not wlock:
601 wlock = self.wlock()
601 wlock = self.wlock()
602 for f in list:
602 for f in list:
603 p = self.wjoin(f)
603 p = self.wjoin(f)
604 if os.path.exists(p):
604 if os.path.exists(p):
605 self.ui.warn(_("%s still exists!\n") % f)
605 self.ui.warn(_("%s still exists!\n") % f)
606 elif self.dirstate.state(f) == 'a':
606 elif self.dirstate.state(f) == 'a':
607 self.dirstate.forget([f])
607 self.dirstate.forget([f])
608 elif f not in self.dirstate:
608 elif f not in self.dirstate:
609 self.ui.warn(_("%s not tracked!\n") % f)
609 self.ui.warn(_("%s not tracked!\n") % f)
610 else:
610 else:
611 self.dirstate.update([f], "r")
611 self.dirstate.update([f], "r")
612
612
613 def undelete(self, list, wlock=None):
613 def undelete(self, list, wlock=None):
614 p = self.dirstate.parents()[0]
614 p = self.dirstate.parents()[0]
615 mn = self.changelog.read(p)[0]
615 mn = self.changelog.read(p)[0]
616 mf = self.manifest.readflags(mn)
616 mf = self.manifest.readflags(mn)
617 m = self.manifest.read(mn)
617 m = self.manifest.read(mn)
618 if not wlock:
618 if not wlock:
619 wlock = self.wlock()
619 wlock = self.wlock()
620 for f in list:
620 for f in list:
621 if self.dirstate.state(f) not in "r":
621 if self.dirstate.state(f) not in "r":
622 self.ui.warn("%s not removed!\n" % f)
622 self.ui.warn("%s not removed!\n" % f)
623 else:
623 else:
624 t = self.file(f).read(m[f])
624 t = self.file(f).read(m[f])
625 self.wwrite(f, t)
625 self.wwrite(f, t)
626 util.set_exec(self.wjoin(f), mf[f])
626 util.set_exec(self.wjoin(f), mf[f])
627 self.dirstate.update([f], "n")
627 self.dirstate.update([f], "n")
628
628
629 def copy(self, source, dest, wlock=None):
629 def copy(self, source, dest, wlock=None):
630 p = self.wjoin(dest)
630 p = self.wjoin(dest)
631 if not os.path.exists(p):
631 if not os.path.exists(p):
632 self.ui.warn(_("%s does not exist!\n") % dest)
632 self.ui.warn(_("%s does not exist!\n") % dest)
633 elif not os.path.isfile(p):
633 elif not os.path.isfile(p):
634 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
634 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
635 else:
635 else:
636 if not wlock:
636 if not wlock:
637 wlock = self.wlock()
637 wlock = self.wlock()
638 if self.dirstate.state(dest) == '?':
638 if self.dirstate.state(dest) == '?':
639 self.dirstate.update([dest], "a")
639 self.dirstate.update([dest], "a")
640 self.dirstate.copy(source, dest)
640 self.dirstate.copy(source, dest)
641
641
642 def heads(self, start=None):
642 def heads(self, start=None):
643 heads = self.changelog.heads(start)
643 heads = self.changelog.heads(start)
644 # sort the output in rev descending order
644 # sort the output in rev descending order
645 heads = [(-self.changelog.rev(h), h) for h in heads]
645 heads = [(-self.changelog.rev(h), h) for h in heads]
646 heads.sort()
646 heads.sort()
647 return [n for (r, n) in heads]
647 return [n for (r, n) in heads]
648
648
649 # branchlookup returns a dict giving a list of branches for
649 # branchlookup returns a dict giving a list of branches for
650 # each head. A branch is defined as the tag of a node or
650 # each head. A branch is defined as the tag of a node or
651 # the branch of the node's parents. If a node has multiple
651 # the branch of the node's parents. If a node has multiple
652 # branch tags, tags are eliminated if they are visible from other
652 # branch tags, tags are eliminated if they are visible from other
653 # branch tags.
653 # branch tags.
654 #
654 #
655 # So, for this graph: a->b->c->d->e
655 # So, for this graph: a->b->c->d->e
656 # \ /
656 # \ /
657 # aa -----/
657 # aa -----/
658 # a has tag 2.6.12
658 # a has tag 2.6.12
659 # d has tag 2.6.13
659 # d has tag 2.6.13
660 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
660 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
661 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
661 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
662 # from the list.
662 # from the list.
663 #
663 #
664 # It is possible that more than one head will have the same branch tag.
664 # It is possible that more than one head will have the same branch tag.
665 # callers need to check the result for multiple heads under the same
665 # callers need to check the result for multiple heads under the same
666 # branch tag if that is a problem for them (ie checkout of a specific
666 # branch tag if that is a problem for them (ie checkout of a specific
667 # branch).
667 # branch).
668 #
668 #
669 # passing in a specific branch will limit the depth of the search
669 # passing in a specific branch will limit the depth of the search
670 # through the parents. It won't limit the branches returned in the
670 # through the parents. It won't limit the branches returned in the
671 # result though.
671 # result though.
672 def branchlookup(self, heads=None, branch=None):
672 def branchlookup(self, heads=None, branch=None):
673 if not heads:
673 if not heads:
674 heads = self.heads()
674 heads = self.heads()
675 headt = [ h for h in heads ]
675 headt = [ h for h in heads ]
676 chlog = self.changelog
676 chlog = self.changelog
677 branches = {}
677 branches = {}
678 merges = []
678 merges = []
679 seenmerge = {}
679 seenmerge = {}
680
680
681 # traverse the tree once for each head, recording in the branches
681 # traverse the tree once for each head, recording in the branches
682 # dict which tags are visible from this head. The branches
682 # dict which tags are visible from this head. The branches
683 # dict also records which tags are visible from each tag
683 # dict also records which tags are visible from each tag
684 # while we traverse.
684 # while we traverse.
685 while headt or merges:
685 while headt or merges:
686 if merges:
686 if merges:
687 n, found = merges.pop()
687 n, found = merges.pop()
688 visit = [n]
688 visit = [n]
689 else:
689 else:
690 h = headt.pop()
690 h = headt.pop()
691 visit = [h]
691 visit = [h]
692 found = [h]
692 found = [h]
693 seen = {}
693 seen = {}
694 while visit:
694 while visit:
695 n = visit.pop()
695 n = visit.pop()
696 if n in seen:
696 if n in seen:
697 continue
697 continue
698 pp = chlog.parents(n)
698 pp = chlog.parents(n)
699 tags = self.nodetags(n)
699 tags = self.nodetags(n)
700 if tags:
700 if tags:
701 for x in tags:
701 for x in tags:
702 if x == 'tip':
702 if x == 'tip':
703 continue
703 continue
704 for f in found:
704 for f in found:
705 branches.setdefault(f, {})[n] = 1
705 branches.setdefault(f, {})[n] = 1
706 branches.setdefault(n, {})[n] = 1
706 branches.setdefault(n, {})[n] = 1
707 break
707 break
708 if n not in found:
708 if n not in found:
709 found.append(n)
709 found.append(n)
710 if branch in tags:
710 if branch in tags:
711 continue
711 continue
712 seen[n] = 1
712 seen[n] = 1
713 if pp[1] != nullid and n not in seenmerge:
713 if pp[1] != nullid and n not in seenmerge:
714 merges.append((pp[1], [x for x in found]))
714 merges.append((pp[1], [x for x in found]))
715 seenmerge[n] = 1
715 seenmerge[n] = 1
716 if pp[0] != nullid:
716 if pp[0] != nullid:
717 visit.append(pp[0])
717 visit.append(pp[0])
718 # traverse the branches dict, eliminating branch tags from each
718 # traverse the branches dict, eliminating branch tags from each
719 # head that are visible from another branch tag for that head.
719 # head that are visible from another branch tag for that head.
720 out = {}
720 out = {}
721 viscache = {}
721 viscache = {}
722 for h in heads:
722 for h in heads:
723 def visible(node):
723 def visible(node):
724 if node in viscache:
724 if node in viscache:
725 return viscache[node]
725 return viscache[node]
726 ret = {}
726 ret = {}
727 visit = [node]
727 visit = [node]
728 while visit:
728 while visit:
729 x = visit.pop()
729 x = visit.pop()
730 if x in viscache:
730 if x in viscache:
731 ret.update(viscache[x])
731 ret.update(viscache[x])
732 elif x not in ret:
732 elif x not in ret:
733 ret[x] = 1
733 ret[x] = 1
734 if x in branches:
734 if x in branches:
735 visit[len(visit):] = branches[x].keys()
735 visit[len(visit):] = branches[x].keys()
736 viscache[node] = ret
736 viscache[node] = ret
737 return ret
737 return ret
738 if h not in branches:
738 if h not in branches:
739 continue
739 continue
740 # O(n^2), but somewhat limited. This only searches the
740 # O(n^2), but somewhat limited. This only searches the
741 # tags visible from a specific head, not all the tags in the
741 # tags visible from a specific head, not all the tags in the
742 # whole repo.
742 # whole repo.
743 for b in branches[h]:
743 for b in branches[h]:
744 vis = False
744 vis = False
745 for bb in branches[h].keys():
745 for bb in branches[h].keys():
746 if b != bb:
746 if b != bb:
747 if b in visible(bb):
747 if b in visible(bb):
748 vis = True
748 vis = True
749 break
749 break
750 if not vis:
750 if not vis:
751 l = out.setdefault(h, [])
751 l = out.setdefault(h, [])
752 l[len(l):] = self.nodetags(b)
752 l[len(l):] = self.nodetags(b)
753 return out
753 return out
754
754
755 def branches(self, nodes):
755 def branches(self, nodes):
756 if not nodes:
756 if not nodes:
757 nodes = [self.changelog.tip()]
757 nodes = [self.changelog.tip()]
758 b = []
758 b = []
759 for n in nodes:
759 for n in nodes:
760 t = n
760 t = n
761 while n:
761 while n:
762 p = self.changelog.parents(n)
762 p = self.changelog.parents(n)
763 if p[1] != nullid or p[0] == nullid:
763 if p[1] != nullid or p[0] == nullid:
764 b.append((t, n, p[0], p[1]))
764 b.append((t, n, p[0], p[1]))
765 break
765 break
766 n = p[0]
766 n = p[0]
767 return b
767 return b
768
768
769 def between(self, pairs):
769 def between(self, pairs):
770 r = []
770 r = []
771
771
772 for top, bottom in pairs:
772 for top, bottom in pairs:
773 n, l, i = top, [], 0
773 n, l, i = top, [], 0
774 f = 1
774 f = 1
775
775
776 while n != bottom:
776 while n != bottom:
777 p = self.changelog.parents(n)[0]
777 p = self.changelog.parents(n)[0]
778 if i == f:
778 if i == f:
779 l.append(n)
779 l.append(n)
780 f = f * 2
780 f = f * 2
781 n = p
781 n = p
782 i += 1
782 i += 1
783
783
784 r.append(l)
784 r.append(l)
785
785
786 return r
786 return r
787
787
788 def findincoming(self, remote, base=None, heads=None):
788 def findincoming(self, remote, base=None, heads=None):
789 m = self.changelog.nodemap
789 m = self.changelog.nodemap
790 search = []
790 search = []
791 fetch = {}
791 fetch = {}
792 seen = {}
792 seen = {}
793 seenbranch = {}
793 seenbranch = {}
794 if base == None:
794 if base == None:
795 base = {}
795 base = {}
796
796
797 # assume we're closer to the tip than the root
797 # assume we're closer to the tip than the root
798 # and start by examining the heads
798 # and start by examining the heads
799 self.ui.status(_("searching for changes\n"))
799 self.ui.status(_("searching for changes\n"))
800
800
801 if not heads:
801 if not heads:
802 heads = remote.heads()
802 heads = remote.heads()
803
803
804 unknown = []
804 unknown = []
805 for h in heads:
805 for h in heads:
806 if h not in m:
806 if h not in m:
807 unknown.append(h)
807 unknown.append(h)
808 else:
808 else:
809 base[h] = 1
809 base[h] = 1
810
810
811 if not unknown:
811 if not unknown:
812 return None
812 return None
813
813
814 rep = {}
814 rep = {}
815 reqcnt = 0
815 reqcnt = 0
816
816
817 # search through remote branches
817 # search through remote branches
818 # a 'branch' here is a linear segment of history, with four parts:
818 # a 'branch' here is a linear segment of history, with four parts:
819 # head, root, first parent, second parent
819 # head, root, first parent, second parent
820 # (a branch always has two parents (or none) by definition)
820 # (a branch always has two parents (or none) by definition)
821 unknown = remote.branches(unknown)
821 unknown = remote.branches(unknown)
822 while unknown:
822 while unknown:
823 r = []
823 r = []
824 while unknown:
824 while unknown:
825 n = unknown.pop(0)
825 n = unknown.pop(0)
826 if n[0] in seen:
826 if n[0] in seen:
827 continue
827 continue
828
828
829 self.ui.debug(_("examining %s:%s\n")
829 self.ui.debug(_("examining %s:%s\n")
830 % (short(n[0]), short(n[1])))
830 % (short(n[0]), short(n[1])))
831 if n[0] == nullid:
831 if n[0] == nullid:
832 break
832 break
833 if n in seenbranch:
833 if n in seenbranch:
834 self.ui.debug(_("branch already found\n"))
834 self.ui.debug(_("branch already found\n"))
835 continue
835 continue
836 if n[1] and n[1] in m: # do we know the base?
836 if n[1] and n[1] in m: # do we know the base?
837 self.ui.debug(_("found incomplete branch %s:%s\n")
837 self.ui.debug(_("found incomplete branch %s:%s\n")
838 % (short(n[0]), short(n[1])))
838 % (short(n[0]), short(n[1])))
839 search.append(n) # schedule branch range for scanning
839 search.append(n) # schedule branch range for scanning
840 seenbranch[n] = 1
840 seenbranch[n] = 1
841 else:
841 else:
842 if n[1] not in seen and n[1] not in fetch:
842 if n[1] not in seen and n[1] not in fetch:
843 if n[2] in m and n[3] in m:
843 if n[2] in m and n[3] in m:
844 self.ui.debug(_("found new changeset %s\n") %
844 self.ui.debug(_("found new changeset %s\n") %
845 short(n[1]))
845 short(n[1]))
846 fetch[n[1]] = 1 # earliest unknown
846 fetch[n[1]] = 1 # earliest unknown
847 base[n[2]] = 1 # latest known
847 base[n[2]] = 1 # latest known
848 continue
848 continue
849
849
850 for a in n[2:4]:
850 for a in n[2:4]:
851 if a not in rep:
851 if a not in rep:
852 r.append(a)
852 r.append(a)
853 rep[a] = 1
853 rep[a] = 1
854
854
855 seen[n[0]] = 1
855 seen[n[0]] = 1
856
856
857 if r:
857 if r:
858 reqcnt += 1
858 reqcnt += 1
859 self.ui.debug(_("request %d: %s\n") %
859 self.ui.debug(_("request %d: %s\n") %
860 (reqcnt, " ".join(map(short, r))))
860 (reqcnt, " ".join(map(short, r))))
861 for p in range(0, len(r), 10):
861 for p in range(0, len(r), 10):
862 for b in remote.branches(r[p:p+10]):
862 for b in remote.branches(r[p:p+10]):
863 self.ui.debug(_("received %s:%s\n") %
863 self.ui.debug(_("received %s:%s\n") %
864 (short(b[0]), short(b[1])))
864 (short(b[0]), short(b[1])))
865 if b[0] in m:
865 if b[0] in m:
866 self.ui.debug(_("found base node %s\n")
866 self.ui.debug(_("found base node %s\n")
867 % short(b[0]))
867 % short(b[0]))
868 base[b[0]] = 1
868 base[b[0]] = 1
869 elif b[0] not in seen:
869 elif b[0] not in seen:
870 unknown.append(b)
870 unknown.append(b)
871
871
872 # do binary search on the branches we found
872 # do binary search on the branches we found
873 while search:
873 while search:
874 n = search.pop(0)
874 n = search.pop(0)
875 reqcnt += 1
875 reqcnt += 1
876 l = remote.between([(n[0], n[1])])[0]
876 l = remote.between([(n[0], n[1])])[0]
877 l.append(n[1])
877 l.append(n[1])
878 p = n[0]
878 p = n[0]
879 f = 1
879 f = 1
880 for i in l:
880 for i in l:
881 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
881 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
882 if i in m:
882 if i in m:
883 if f <= 2:
883 if f <= 2:
884 self.ui.debug(_("found new branch changeset %s\n") %
884 self.ui.debug(_("found new branch changeset %s\n") %
885 short(p))
885 short(p))
886 fetch[p] = 1
886 fetch[p] = 1
887 base[i] = 1
887 base[i] = 1
888 else:
888 else:
889 self.ui.debug(_("narrowed branch search to %s:%s\n")
889 self.ui.debug(_("narrowed branch search to %s:%s\n")
890 % (short(p), short(i)))
890 % (short(p), short(i)))
891 search.append((p, i))
891 search.append((p, i))
892 break
892 break
893 p, f = i, f * 2
893 p, f = i, f * 2
894
894
895 # sanity check our fetch list
895 # sanity check our fetch list
896 for f in fetch.keys():
896 for f in fetch.keys():
897 if f in m:
897 if f in m:
898 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
898 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
899
899
900 if base.keys() == [nullid]:
900 if base.keys() == [nullid]:
901 self.ui.warn(_("warning: pulling from an unrelated repository!\n"))
901 self.ui.warn(_("warning: pulling from an unrelated repository!\n"))
902
902
903 self.ui.note(_("found new changesets starting at ") +
903 self.ui.note(_("found new changesets starting at ") +
904 " ".join([short(f) for f in fetch]) + "\n")
904 " ".join([short(f) for f in fetch]) + "\n")
905
905
906 self.ui.debug(_("%d total queries\n") % reqcnt)
906 self.ui.debug(_("%d total queries\n") % reqcnt)
907
907
908 return fetch.keys()
908 return fetch.keys()
909
909
910 def findoutgoing(self, remote, base=None, heads=None):
910 def findoutgoing(self, remote, base=None, heads=None):
911 if base == None:
911 if base == None:
912 base = {}
912 base = {}
913 self.findincoming(remote, base, heads)
913 self.findincoming(remote, base, heads)
914
914
915 self.ui.debug(_("common changesets up to ")
915 self.ui.debug(_("common changesets up to ")
916 + " ".join(map(short, base.keys())) + "\n")
916 + " ".join(map(short, base.keys())) + "\n")
917
917
918 remain = dict.fromkeys(self.changelog.nodemap)
918 remain = dict.fromkeys(self.changelog.nodemap)
919
919
920 # prune everything remote has from the tree
920 # prune everything remote has from the tree
921 del remain[nullid]
921 del remain[nullid]
922 remove = base.keys()
922 remove = base.keys()
923 while remove:
923 while remove:
924 n = remove.pop(0)
924 n = remove.pop(0)
925 if n in remain:
925 if n in remain:
926 del remain[n]
926 del remain[n]
927 for p in self.changelog.parents(n):
927 for p in self.changelog.parents(n):
928 remove.append(p)
928 remove.append(p)
929
929
930 # find every node whose parents have been pruned
930 # find every node whose parents have been pruned
931 subset = []
931 subset = []
932 for n in remain:
932 for n in remain:
933 p1, p2 = self.changelog.parents(n)
933 p1, p2 = self.changelog.parents(n)
934 if p1 not in remain and p2 not in remain:
934 if p1 not in remain and p2 not in remain:
935 subset.append(n)
935 subset.append(n)
936
936
937 # this is the set of all roots we have to push
937 # this is the set of all roots we have to push
938 return subset
938 return subset
939
939
940 def pull(self, remote, heads=None):
940 def pull(self, remote, heads=None):
941 l = self.lock()
941 l = self.lock()
942
942
943 # if we have an empty repo, fetch everything
943 # if we have an empty repo, fetch everything
944 if self.changelog.tip() == nullid:
944 if self.changelog.tip() == nullid:
945 self.ui.status(_("requesting all changes\n"))
945 self.ui.status(_("requesting all changes\n"))
946 fetch = [nullid]
946 fetch = [nullid]
947 else:
947 else:
948 fetch = self.findincoming(remote)
948 fetch = self.findincoming(remote)
949
949
950 if not fetch:
950 if not fetch:
951 self.ui.status(_("no changes found\n"))
951 self.ui.status(_("no changes found\n"))
952 return 1
952 return 1
953
953
954 if heads is None:
954 if heads is None:
955 cg = remote.changegroup(fetch, 'pull')
955 cg = remote.changegroup(fetch, 'pull')
956 else:
956 else:
957 cg = remote.changegroupsubset(fetch, heads, 'pull')
957 cg = remote.changegroupsubset(fetch, heads, 'pull')
958 return self.addchangegroup(cg)
958 return self.addchangegroup(cg)
959
959
960 def push(self, remote, force=False, revs=None):
960 def push(self, remote, force=False, revs=None):
961 lock = remote.lock()
961 lock = remote.lock()
962
962
963 base = {}
963 base = {}
964 heads = remote.heads()
964 heads = remote.heads()
965 inc = self.findincoming(remote, base, heads)
965 inc = self.findincoming(remote, base, heads)
966 if not force and inc:
966 if not force and inc:
967 self.ui.warn(_("abort: unsynced remote changes!\n"))
967 self.ui.warn(_("abort: unsynced remote changes!\n"))
968 self.ui.status(_("(did you forget to sync? use push -f to force)\n"))
968 self.ui.status(_("(did you forget to sync? use push -f to force)\n"))
969 return 1
969 return 1
970
970
971 update = self.findoutgoing(remote, base)
971 update = self.findoutgoing(remote, base)
972 if revs is not None:
972 if revs is not None:
973 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
973 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
974 else:
974 else:
975 bases, heads = update, self.changelog.heads()
975 bases, heads = update, self.changelog.heads()
976
976
977 if not bases:
977 if not bases:
978 self.ui.status(_("no changes found\n"))
978 self.ui.status(_("no changes found\n"))
979 return 1
979 return 1
980 elif not force:
980 elif not force:
981 if len(bases) < len(heads):
981 if len(bases) < len(heads):
982 self.ui.warn(_("abort: push creates new remote branches!\n"))
982 self.ui.warn(_("abort: push creates new remote branches!\n"))
983 self.ui.status(_("(did you forget to merge?"
983 self.ui.status(_("(did you forget to merge?"
984 " use push -f to force)\n"))
984 " use push -f to force)\n"))
985 return 1
985 return 1
986
986
987 if revs is None:
987 if revs is None:
988 cg = self.changegroup(update, 'push')
988 cg = self.changegroup(update, 'push')
989 else:
989 else:
990 cg = self.changegroupsubset(update, revs, 'push')
990 cg = self.changegroupsubset(update, revs, 'push')
991 return remote.addchangegroup(cg)
991 return remote.addchangegroup(cg)
992
992
993 def changegroupsubset(self, bases, heads, source):
993 def changegroupsubset(self, bases, heads, source):
994 """This function generates a changegroup consisting of all the nodes
994 """This function generates a changegroup consisting of all the nodes
995 that are descendents of any of the bases, and ancestors of any of
995 that are descendents of any of the bases, and ancestors of any of
996 the heads.
996 the heads.
997
997
998 It is fairly complex as determining which filenodes and which
998 It is fairly complex as determining which filenodes and which
999 manifest nodes need to be included for the changeset to be complete
999 manifest nodes need to be included for the changeset to be complete
1000 is non-trivial.
1000 is non-trivial.
1001
1001
1002 Another wrinkle is doing the reverse, figuring out which changeset in
1002 Another wrinkle is doing the reverse, figuring out which changeset in
1003 the changegroup a particular filenode or manifestnode belongs to."""
1003 the changegroup a particular filenode or manifestnode belongs to."""
1004
1004
1005 self.hook('preoutgoing', throw=True, source=source)
1005 self.hook('preoutgoing', throw=True, source=source)
1006
1006
1007 # Set up some initial variables
1007 # Set up some initial variables
1008 # Make it easy to refer to self.changelog
1008 # Make it easy to refer to self.changelog
1009 cl = self.changelog
1009 cl = self.changelog
1010 # msng is short for missing - compute the list of changesets in this
1010 # msng is short for missing - compute the list of changesets in this
1011 # changegroup.
1011 # changegroup.
1012 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1012 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1013 # Some bases may turn out to be superfluous, and some heads may be
1013 # Some bases may turn out to be superfluous, and some heads may be
1014 # too. nodesbetween will return the minimal set of bases and heads
1014 # too. nodesbetween will return the minimal set of bases and heads
1015 # necessary to re-create the changegroup.
1015 # necessary to re-create the changegroup.
1016
1016
1017 # Known heads are the list of heads that it is assumed the recipient
1017 # Known heads are the list of heads that it is assumed the recipient
1018 # of this changegroup will know about.
1018 # of this changegroup will know about.
1019 knownheads = {}
1019 knownheads = {}
1020 # We assume that all parents of bases are known heads.
1020 # We assume that all parents of bases are known heads.
1021 for n in bases:
1021 for n in bases:
1022 for p in cl.parents(n):
1022 for p in cl.parents(n):
1023 if p != nullid:
1023 if p != nullid:
1024 knownheads[p] = 1
1024 knownheads[p] = 1
1025 knownheads = knownheads.keys()
1025 knownheads = knownheads.keys()
1026 if knownheads:
1026 if knownheads:
1027 # Now that we know what heads are known, we can compute which
1027 # Now that we know what heads are known, we can compute which
1028 # changesets are known. The recipient must know about all
1028 # changesets are known. The recipient must know about all
1029 # changesets required to reach the known heads from the null
1029 # changesets required to reach the known heads from the null
1030 # changeset.
1030 # changeset.
1031 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1031 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1032 junk = None
1032 junk = None
1033 # Transform the list into an ersatz set.
1033 # Transform the list into an ersatz set.
1034 has_cl_set = dict.fromkeys(has_cl_set)
1034 has_cl_set = dict.fromkeys(has_cl_set)
1035 else:
1035 else:
1036 # If there were no known heads, the recipient cannot be assumed to
1036 # If there were no known heads, the recipient cannot be assumed to
1037 # know about any changesets.
1037 # know about any changesets.
1038 has_cl_set = {}
1038 has_cl_set = {}
1039
1039
1040 # Make it easy to refer to self.manifest
1040 # Make it easy to refer to self.manifest
1041 mnfst = self.manifest
1041 mnfst = self.manifest
1042 # We don't know which manifests are missing yet
1042 # We don't know which manifests are missing yet
1043 msng_mnfst_set = {}
1043 msng_mnfst_set = {}
1044 # Nor do we know which filenodes are missing.
1044 # Nor do we know which filenodes are missing.
1045 msng_filenode_set = {}
1045 msng_filenode_set = {}
1046
1046
1047 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1047 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1048 junk = None
1048 junk = None
1049
1049
1050 # A changeset always belongs to itself, so the changenode lookup
1050 # A changeset always belongs to itself, so the changenode lookup
1051 # function for a changenode is identity.
1051 # function for a changenode is identity.
1052 def identity(x):
1052 def identity(x):
1053 return x
1053 return x
1054
1054
1055 # A function generating function. Sets up an environment for the
1055 # A function generating function. Sets up an environment for the
1056 # inner function.
1056 # inner function.
1057 def cmp_by_rev_func(revlog):
1057 def cmp_by_rev_func(revlog):
1058 # Compare two nodes by their revision number in the environment's
1058 # Compare two nodes by their revision number in the environment's
1059 # revision history. Since the revision number both represents the
1059 # revision history. Since the revision number both represents the
1060 # most efficient order to read the nodes in, and represents a
1060 # most efficient order to read the nodes in, and represents a
1061 # topological sorting of the nodes, this function is often useful.
1061 # topological sorting of the nodes, this function is often useful.
1062 def cmp_by_rev(a, b):
1062 def cmp_by_rev(a, b):
1063 return cmp(revlog.rev(a), revlog.rev(b))
1063 return cmp(revlog.rev(a), revlog.rev(b))
1064 return cmp_by_rev
1064 return cmp_by_rev
1065
1065
1066 # If we determine that a particular file or manifest node must be a
1066 # If we determine that a particular file or manifest node must be a
1067 # node that the recipient of the changegroup will already have, we can
1067 # node that the recipient of the changegroup will already have, we can
1068 # also assume the recipient will have all the parents. This function
1068 # also assume the recipient will have all the parents. This function
1069 # prunes them from the set of missing nodes.
1069 # prunes them from the set of missing nodes.
1070 def prune_parents(revlog, hasset, msngset):
1070 def prune_parents(revlog, hasset, msngset):
1071 haslst = hasset.keys()
1071 haslst = hasset.keys()
1072 haslst.sort(cmp_by_rev_func(revlog))
1072 haslst.sort(cmp_by_rev_func(revlog))
1073 for node in haslst:
1073 for node in haslst:
1074 parentlst = [p for p in revlog.parents(node) if p != nullid]
1074 parentlst = [p for p in revlog.parents(node) if p != nullid]
1075 while parentlst:
1075 while parentlst:
1076 n = parentlst.pop()
1076 n = parentlst.pop()
1077 if n not in hasset:
1077 if n not in hasset:
1078 hasset[n] = 1
1078 hasset[n] = 1
1079 p = [p for p in revlog.parents(n) if p != nullid]
1079 p = [p for p in revlog.parents(n) if p != nullid]
1080 parentlst.extend(p)
1080 parentlst.extend(p)
1081 for n in hasset:
1081 for n in hasset:
1082 msngset.pop(n, None)
1082 msngset.pop(n, None)
1083
1083
1084 # This is a function generating function used to set up an environment
1084 # This is a function generating function used to set up an environment
1085 # for the inner function to execute in.
1085 # for the inner function to execute in.
1086 def manifest_and_file_collector(changedfileset):
1086 def manifest_and_file_collector(changedfileset):
1087 # This is an information gathering function that gathers
1087 # This is an information gathering function that gathers
1088 # information from each changeset node that goes out as part of
1088 # information from each changeset node that goes out as part of
1089 # the changegroup. The information gathered is a list of which
1089 # the changegroup. The information gathered is a list of which
1090 # manifest nodes are potentially required (the recipient may
1090 # manifest nodes are potentially required (the recipient may
1091 # already have them) and total list of all files which were
1091 # already have them) and total list of all files which were
1092 # changed in any changeset in the changegroup.
1092 # changed in any changeset in the changegroup.
1093 #
1093 #
1094 # We also remember the first changenode we saw any manifest
1094 # We also remember the first changenode we saw any manifest
1095 # referenced by so we can later determine which changenode 'owns'
1095 # referenced by so we can later determine which changenode 'owns'
1096 # the manifest.
1096 # the manifest.
1097 def collect_manifests_and_files(clnode):
1097 def collect_manifests_and_files(clnode):
1098 c = cl.read(clnode)
1098 c = cl.read(clnode)
1099 for f in c[3]:
1099 for f in c[3]:
1100 # This is to make sure we only have one instance of each
1100 # This is to make sure we only have one instance of each
1101 # filename string for each filename.
1101 # filename string for each filename.
1102 changedfileset.setdefault(f, f)
1102 changedfileset.setdefault(f, f)
1103 msng_mnfst_set.setdefault(c[0], clnode)
1103 msng_mnfst_set.setdefault(c[0], clnode)
1104 return collect_manifests_and_files
1104 return collect_manifests_and_files
1105
1105
1106 # Figure out which manifest nodes (of the ones we think might be part
1106 # Figure out which manifest nodes (of the ones we think might be part
1107 # of the changegroup) the recipient must know about and remove them
1107 # of the changegroup) the recipient must know about and remove them
1108 # from the changegroup.
1108 # from the changegroup.
1109 def prune_manifests():
1109 def prune_manifests():
1110 has_mnfst_set = {}
1110 has_mnfst_set = {}
1111 for n in msng_mnfst_set:
1111 for n in msng_mnfst_set:
1112 # If a 'missing' manifest thinks it belongs to a changenode
1112 # If a 'missing' manifest thinks it belongs to a changenode
1113 # the recipient is assumed to have, obviously the recipient
1113 # the recipient is assumed to have, obviously the recipient
1114 # must have that manifest.
1114 # must have that manifest.
1115 linknode = cl.node(mnfst.linkrev(n))
1115 linknode = cl.node(mnfst.linkrev(n))
1116 if linknode in has_cl_set:
1116 if linknode in has_cl_set:
1117 has_mnfst_set[n] = 1
1117 has_mnfst_set[n] = 1
1118 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1118 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1119
1119
1120 # Use the information collected in collect_manifests_and_files to say
1120 # Use the information collected in collect_manifests_and_files to say
1121 # which changenode any manifestnode belongs to.
1121 # which changenode any manifestnode belongs to.
1122 def lookup_manifest_link(mnfstnode):
1122 def lookup_manifest_link(mnfstnode):
1123 return msng_mnfst_set[mnfstnode]
1123 return msng_mnfst_set[mnfstnode]
1124
1124
1125 # A function generating function that sets up the initial environment
1125 # A function generating function that sets up the initial environment
1126 # the inner function.
1126 # the inner function.
1127 def filenode_collector(changedfiles):
1127 def filenode_collector(changedfiles):
1128 next_rev = [0]
1128 next_rev = [0]
1129 # This gathers information from each manifestnode included in the
1129 # This gathers information from each manifestnode included in the
1130 # changegroup about which filenodes the manifest node references
1130 # changegroup about which filenodes the manifest node references
1131 # so we can include those in the changegroup too.
1131 # so we can include those in the changegroup too.
1132 #
1132 #
1133 # It also remembers which changenode each filenode belongs to. It
1133 # It also remembers which changenode each filenode belongs to. It
1134 # does this by assuming the a filenode belongs to the changenode
1134 # does this by assuming the a filenode belongs to the changenode
1135 # the first manifest that references it belongs to.
1135 # the first manifest that references it belongs to.
1136 def collect_msng_filenodes(mnfstnode):
1136 def collect_msng_filenodes(mnfstnode):
1137 r = mnfst.rev(mnfstnode)
1137 r = mnfst.rev(mnfstnode)
1138 if r == next_rev[0]:
1138 if r == next_rev[0]:
1139 # If the last rev we looked at was the one just previous,
1139 # If the last rev we looked at was the one just previous,
1140 # we only need to see a diff.
1140 # we only need to see a diff.
1141 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1141 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1142 # For each line in the delta
1142 # For each line in the delta
1143 for dline in delta.splitlines():
1143 for dline in delta.splitlines():
1144 # get the filename and filenode for that line
1144 # get the filename and filenode for that line
1145 f, fnode = dline.split('\0')
1145 f, fnode = dline.split('\0')
1146 fnode = bin(fnode[:40])
1146 fnode = bin(fnode[:40])
1147 f = changedfiles.get(f, None)
1147 f = changedfiles.get(f, None)
1148 # And if the file is in the list of files we care
1148 # And if the file is in the list of files we care
1149 # about.
1149 # about.
1150 if f is not None:
1150 if f is not None:
1151 # Get the changenode this manifest belongs to
1151 # Get the changenode this manifest belongs to
1152 clnode = msng_mnfst_set[mnfstnode]
1152 clnode = msng_mnfst_set[mnfstnode]
1153 # Create the set of filenodes for the file if
1153 # Create the set of filenodes for the file if
1154 # there isn't one already.
1154 # there isn't one already.
1155 ndset = msng_filenode_set.setdefault(f, {})
1155 ndset = msng_filenode_set.setdefault(f, {})
1156 # And set the filenode's changelog node to the
1156 # And set the filenode's changelog node to the
1157 # manifest's if it hasn't been set already.
1157 # manifest's if it hasn't been set already.
1158 ndset.setdefault(fnode, clnode)
1158 ndset.setdefault(fnode, clnode)
1159 else:
1159 else:
1160 # Otherwise we need a full manifest.
1160 # Otherwise we need a full manifest.
1161 m = mnfst.read(mnfstnode)
1161 m = mnfst.read(mnfstnode)
1162 # For every file in we care about.
1162 # For every file in we care about.
1163 for f in changedfiles:
1163 for f in changedfiles:
1164 fnode = m.get(f, None)
1164 fnode = m.get(f, None)
1165 # If it's in the manifest
1165 # If it's in the manifest
1166 if fnode is not None:
1166 if fnode is not None:
1167 # See comments above.
1167 # See comments above.
1168 clnode = msng_mnfst_set[mnfstnode]
1168 clnode = msng_mnfst_set[mnfstnode]
1169 ndset = msng_filenode_set.setdefault(f, {})
1169 ndset = msng_filenode_set.setdefault(f, {})
1170 ndset.setdefault(fnode, clnode)
1170 ndset.setdefault(fnode, clnode)
1171 # Remember the revision we hope to see next.
1171 # Remember the revision we hope to see next.
1172 next_rev[0] = r + 1
1172 next_rev[0] = r + 1
1173 return collect_msng_filenodes
1173 return collect_msng_filenodes
1174
1174
1175 # We have a list of filenodes we think we need for a file, lets remove
1175 # We have a list of filenodes we think we need for a file, lets remove
1176 # all those we now the recipient must have.
1176 # all those we now the recipient must have.
1177 def prune_filenodes(f, filerevlog):
1177 def prune_filenodes(f, filerevlog):
1178 msngset = msng_filenode_set[f]
1178 msngset = msng_filenode_set[f]
1179 hasset = {}
1179 hasset = {}
1180 # If a 'missing' filenode thinks it belongs to a changenode we
1180 # If a 'missing' filenode thinks it belongs to a changenode we
1181 # assume the recipient must have, then the recipient must have
1181 # assume the recipient must have, then the recipient must have
1182 # that filenode.
1182 # that filenode.
1183 for n in msngset:
1183 for n in msngset:
1184 clnode = cl.node(filerevlog.linkrev(n))
1184 clnode = cl.node(filerevlog.linkrev(n))
1185 if clnode in has_cl_set:
1185 if clnode in has_cl_set:
1186 hasset[n] = 1
1186 hasset[n] = 1
1187 prune_parents(filerevlog, hasset, msngset)
1187 prune_parents(filerevlog, hasset, msngset)
1188
1188
1189 # A function generator function that sets up the a context for the
1189 # A function generator function that sets up the a context for the
1190 # inner function.
1190 # inner function.
1191 def lookup_filenode_link_func(fname):
1191 def lookup_filenode_link_func(fname):
1192 msngset = msng_filenode_set[fname]
1192 msngset = msng_filenode_set[fname]
1193 # Lookup the changenode the filenode belongs to.
1193 # Lookup the changenode the filenode belongs to.
1194 def lookup_filenode_link(fnode):
1194 def lookup_filenode_link(fnode):
1195 return msngset[fnode]
1195 return msngset[fnode]
1196 return lookup_filenode_link
1196 return lookup_filenode_link
1197
1197
1198 # Now that we have all theses utility functions to help out and
1198 # Now that we have all theses utility functions to help out and
1199 # logically divide up the task, generate the group.
1199 # logically divide up the task, generate the group.
1200 def gengroup():
1200 def gengroup():
1201 # The set of changed files starts empty.
1201 # The set of changed files starts empty.
1202 changedfiles = {}
1202 changedfiles = {}
1203 # Create a changenode group generator that will call our functions
1203 # Create a changenode group generator that will call our functions
1204 # back to lookup the owning changenode and collect information.
1204 # back to lookup the owning changenode and collect information.
1205 group = cl.group(msng_cl_lst, identity,
1205 group = cl.group(msng_cl_lst, identity,
1206 manifest_and_file_collector(changedfiles))
1206 manifest_and_file_collector(changedfiles))
1207 for chnk in group:
1207 for chnk in group:
1208 yield chnk
1208 yield chnk
1209
1209
1210 # The list of manifests has been collected by the generator
1210 # The list of manifests has been collected by the generator
1211 # calling our functions back.
1211 # calling our functions back.
1212 prune_manifests()
1212 prune_manifests()
1213 msng_mnfst_lst = msng_mnfst_set.keys()
1213 msng_mnfst_lst = msng_mnfst_set.keys()
1214 # Sort the manifestnodes by revision number.
1214 # Sort the manifestnodes by revision number.
1215 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1215 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1216 # Create a generator for the manifestnodes that calls our lookup
1216 # Create a generator for the manifestnodes that calls our lookup
1217 # and data collection functions back.
1217 # and data collection functions back.
1218 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1218 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1219 filenode_collector(changedfiles))
1219 filenode_collector(changedfiles))
1220 for chnk in group:
1220 for chnk in group:
1221 yield chnk
1221 yield chnk
1222
1222
1223 # These are no longer needed, dereference and toss the memory for
1223 # These are no longer needed, dereference and toss the memory for
1224 # them.
1224 # them.
1225 msng_mnfst_lst = None
1225 msng_mnfst_lst = None
1226 msng_mnfst_set.clear()
1226 msng_mnfst_set.clear()
1227
1227
1228 changedfiles = changedfiles.keys()
1228 changedfiles = changedfiles.keys()
1229 changedfiles.sort()
1229 changedfiles.sort()
1230 # Go through all our files in order sorted by name.
1230 # Go through all our files in order sorted by name.
1231 for fname in changedfiles:
1231 for fname in changedfiles:
1232 filerevlog = self.file(fname)
1232 filerevlog = self.file(fname)
1233 # Toss out the filenodes that the recipient isn't really
1233 # Toss out the filenodes that the recipient isn't really
1234 # missing.
1234 # missing.
1235 if msng_filenode_set.has_key(fname):
1235 if msng_filenode_set.has_key(fname):
1236 prune_filenodes(fname, filerevlog)
1236 prune_filenodes(fname, filerevlog)
1237 msng_filenode_lst = msng_filenode_set[fname].keys()
1237 msng_filenode_lst = msng_filenode_set[fname].keys()
1238 else:
1238 else:
1239 msng_filenode_lst = []
1239 msng_filenode_lst = []
1240 # If any filenodes are left, generate the group for them,
1240 # If any filenodes are left, generate the group for them,
1241 # otherwise don't bother.
1241 # otherwise don't bother.
1242 if len(msng_filenode_lst) > 0:
1242 if len(msng_filenode_lst) > 0:
1243 yield struct.pack(">l", len(fname) + 4) + fname
1243 yield struct.pack(">l", len(fname) + 4) + fname
1244 # Sort the filenodes by their revision #
1244 # Sort the filenodes by their revision #
1245 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1245 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1246 # Create a group generator and only pass in a changenode
1246 # Create a group generator and only pass in a changenode
1247 # lookup function as we need to collect no information
1247 # lookup function as we need to collect no information
1248 # from filenodes.
1248 # from filenodes.
1249 group = filerevlog.group(msng_filenode_lst,
1249 group = filerevlog.group(msng_filenode_lst,
1250 lookup_filenode_link_func(fname))
1250 lookup_filenode_link_func(fname))
1251 for chnk in group:
1251 for chnk in group:
1252 yield chnk
1252 yield chnk
1253 if msng_filenode_set.has_key(fname):
1253 if msng_filenode_set.has_key(fname):
1254 # Don't need this anymore, toss it to free memory.
1254 # Don't need this anymore, toss it to free memory.
1255 del msng_filenode_set[fname]
1255 del msng_filenode_set[fname]
1256 # Signal that no more groups are left.
1256 # Signal that no more groups are left.
1257 yield struct.pack(">l", 0)
1257 yield struct.pack(">l", 0)
1258
1258
1259 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1259 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1260
1260
1261 return util.chunkbuffer(gengroup())
1261 return util.chunkbuffer(gengroup())
1262
1262
1263 def changegroup(self, basenodes, source):
1263 def changegroup(self, basenodes, source):
1264 """Generate a changegroup of all nodes that we have that a recipient
1264 """Generate a changegroup of all nodes that we have that a recipient
1265 doesn't.
1265 doesn't.
1266
1266
1267 This is much easier than the previous function as we can assume that
1267 This is much easier than the previous function as we can assume that
1268 the recipient has any changenode we aren't sending them."""
1268 the recipient has any changenode we aren't sending them."""
1269
1269
1270 self.hook('preoutgoing', throw=True, source=source)
1270 self.hook('preoutgoing', throw=True, source=source)
1271
1271
1272 cl = self.changelog
1272 cl = self.changelog
1273 nodes = cl.nodesbetween(basenodes, None)[0]
1273 nodes = cl.nodesbetween(basenodes, None)[0]
1274 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1274 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1275
1275
1276 def identity(x):
1276 def identity(x):
1277 return x
1277 return x
1278
1278
1279 def gennodelst(revlog):
1279 def gennodelst(revlog):
1280 for r in xrange(0, revlog.count()):
1280 for r in xrange(0, revlog.count()):
1281 n = revlog.node(r)
1281 n = revlog.node(r)
1282 if revlog.linkrev(n) in revset:
1282 if revlog.linkrev(n) in revset:
1283 yield n
1283 yield n
1284
1284
1285 def changed_file_collector(changedfileset):
1285 def changed_file_collector(changedfileset):
1286 def collect_changed_files(clnode):
1286 def collect_changed_files(clnode):
1287 c = cl.read(clnode)
1287 c = cl.read(clnode)
1288 for fname in c[3]:
1288 for fname in c[3]:
1289 changedfileset[fname] = 1
1289 changedfileset[fname] = 1
1290 return collect_changed_files
1290 return collect_changed_files
1291
1291
1292 def lookuprevlink_func(revlog):
1292 def lookuprevlink_func(revlog):
1293 def lookuprevlink(n):
1293 def lookuprevlink(n):
1294 return cl.node(revlog.linkrev(n))
1294 return cl.node(revlog.linkrev(n))
1295 return lookuprevlink
1295 return lookuprevlink
1296
1296
1297 def gengroup():
1297 def gengroup():
1298 # construct a list of all changed files
1298 # construct a list of all changed files
1299 changedfiles = {}
1299 changedfiles = {}
1300
1300
1301 for chnk in cl.group(nodes, identity,
1301 for chnk in cl.group(nodes, identity,
1302 changed_file_collector(changedfiles)):
1302 changed_file_collector(changedfiles)):
1303 yield chnk
1303 yield chnk
1304 changedfiles = changedfiles.keys()
1304 changedfiles = changedfiles.keys()
1305 changedfiles.sort()
1305 changedfiles.sort()
1306
1306
1307 mnfst = self.manifest
1307 mnfst = self.manifest
1308 nodeiter = gennodelst(mnfst)
1308 nodeiter = gennodelst(mnfst)
1309 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1309 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1310 yield chnk
1310 yield chnk
1311
1311
1312 for fname in changedfiles:
1312 for fname in changedfiles:
1313 filerevlog = self.file(fname)
1313 filerevlog = self.file(fname)
1314 nodeiter = gennodelst(filerevlog)
1314 nodeiter = gennodelst(filerevlog)
1315 nodeiter = list(nodeiter)
1315 nodeiter = list(nodeiter)
1316 if nodeiter:
1316 if nodeiter:
1317 yield struct.pack(">l", len(fname) + 4) + fname
1317 yield struct.pack(">l", len(fname) + 4) + fname
1318 lookup = lookuprevlink_func(filerevlog)
1318 lookup = lookuprevlink_func(filerevlog)
1319 for chnk in filerevlog.group(nodeiter, lookup):
1319 for chnk in filerevlog.group(nodeiter, lookup):
1320 yield chnk
1320 yield chnk
1321
1321
1322 yield struct.pack(">l", 0)
1322 yield struct.pack(">l", 0)
1323 self.hook('outgoing', node=hex(nodes[0]), source=source)
1323 self.hook('outgoing', node=hex(nodes[0]), source=source)
1324
1324
1325 return util.chunkbuffer(gengroup())
1325 return util.chunkbuffer(gengroup())
1326
1326
1327 def addchangegroup(self, source):
1327 def addchangegroup(self, source):
1328
1328
1329 def getchunk():
1329 def getchunk():
1330 d = source.read(4)
1330 d = source.read(4)
1331 if not d:
1331 if not d:
1332 return ""
1332 return ""
1333 l = struct.unpack(">l", d)[0]
1333 l = struct.unpack(">l", d)[0]
1334 if l <= 4:
1334 if l <= 4:
1335 return ""
1335 return ""
1336 d = source.read(l - 4)
1336 d = source.read(l - 4)
1337 if len(d) < l - 4:
1337 if len(d) < l - 4:
1338 raise repo.RepoError(_("premature EOF reading chunk"
1338 raise repo.RepoError(_("premature EOF reading chunk"
1339 " (got %d bytes, expected %d)")
1339 " (got %d bytes, expected %d)")
1340 % (len(d), l - 4))
1340 % (len(d), l - 4))
1341 return d
1341 return d
1342
1342
1343 def getgroup():
1343 def getgroup():
1344 while 1:
1344 while 1:
1345 c = getchunk()
1345 c = getchunk()
1346 if not c:
1346 if not c:
1347 break
1347 break
1348 yield c
1348 yield c
1349
1349
1350 def csmap(x):
1350 def csmap(x):
1351 self.ui.debug(_("add changeset %s\n") % short(x))
1351 self.ui.debug(_("add changeset %s\n") % short(x))
1352 return self.changelog.count()
1352 return self.changelog.count()
1353
1353
1354 def revmap(x):
1354 def revmap(x):
1355 return self.changelog.rev(x)
1355 return self.changelog.rev(x)
1356
1356
1357 if not source:
1357 if not source:
1358 return
1358 return
1359
1359
1360 self.hook('prechangegroup', throw=True)
1360 self.hook('prechangegroup', throw=True)
1361
1361
1362 changesets = files = revisions = 0
1362 changesets = files = revisions = 0
1363
1363
1364 tr = self.transaction()
1364 tr = self.transaction()
1365
1365
1366 oldheads = len(self.changelog.heads())
1366 oldheads = len(self.changelog.heads())
1367
1367
1368 # pull off the changeset group
1368 # pull off the changeset group
1369 self.ui.status(_("adding changesets\n"))
1369 self.ui.status(_("adding changesets\n"))
1370 co = self.changelog.tip()
1370 co = self.changelog.tip()
1371 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1371 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1372 cnr, cor = map(self.changelog.rev, (cn, co))
1372 cnr, cor = map(self.changelog.rev, (cn, co))
1373 if cn == nullid:
1373 if cn == nullid:
1374 cnr = cor
1374 cnr = cor
1375 changesets = cnr - cor
1375 changesets = cnr - cor
1376
1376
1377 # pull off the manifest group
1377 # pull off the manifest group
1378 self.ui.status(_("adding manifests\n"))
1378 self.ui.status(_("adding manifests\n"))
1379 mm = self.manifest.tip()
1379 mm = self.manifest.tip()
1380 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1380 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1381
1381
1382 # process the files
1382 # process the files
1383 self.ui.status(_("adding file changes\n"))
1383 self.ui.status(_("adding file changes\n"))
1384 while 1:
1384 while 1:
1385 f = getchunk()
1385 f = getchunk()
1386 if not f:
1386 if not f:
1387 break
1387 break
1388 self.ui.debug(_("adding %s revisions\n") % f)
1388 self.ui.debug(_("adding %s revisions\n") % f)
1389 fl = self.file(f)
1389 fl = self.file(f)
1390 o = fl.count()
1390 o = fl.count()
1391 n = fl.addgroup(getgroup(), revmap, tr)
1391 n = fl.addgroup(getgroup(), revmap, tr)
1392 revisions += fl.count() - o
1392 revisions += fl.count() - o
1393 files += 1
1393 files += 1
1394
1394
1395 newheads = len(self.changelog.heads())
1395 newheads = len(self.changelog.heads())
1396 heads = ""
1396 heads = ""
1397 if oldheads and newheads > oldheads:
1397 if oldheads and newheads > oldheads:
1398 heads = _(" (+%d heads)") % (newheads - oldheads)
1398 heads = _(" (+%d heads)") % (newheads - oldheads)
1399
1399
1400 self.ui.status(_("added %d changesets"
1400 self.ui.status(_("added %d changesets"
1401 " with %d changes to %d files%s\n")
1401 " with %d changes to %d files%s\n")
1402 % (changesets, revisions, files, heads))
1402 % (changesets, revisions, files, heads))
1403
1403
1404 self.hook('pretxnchangegroup', throw=True,
1404 self.hook('pretxnchangegroup', throw=True,
1405 node=hex(self.changelog.node(cor+1)))
1405 node=hex(self.changelog.node(cor+1)))
1406
1406
1407 tr.close()
1407 tr.close()
1408
1408
1409 if changesets > 0:
1409 if changesets > 0:
1410 self.hook("changegroup", node=hex(self.changelog.node(cor+1)))
1410 self.hook("changegroup", node=hex(self.changelog.node(cor+1)))
1411
1411
1412 for i in range(cor + 1, cnr + 1):
1412 for i in range(cor + 1, cnr + 1):
1413 self.hook("incoming", node=hex(self.changelog.node(i)))
1413 self.hook("incoming", node=hex(self.changelog.node(i)))
1414
1414
1415 def update(self, node, allow=False, force=False, choose=None,
1415 def update(self, node, allow=False, force=False, choose=None,
1416 moddirstate=True, forcemerge=False, wlock=None):
1416 moddirstate=True, forcemerge=False, wlock=None):
1417 pl = self.dirstate.parents()
1417 pl = self.dirstate.parents()
1418 if not force and pl[1] != nullid:
1418 if not force and pl[1] != nullid:
1419 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1419 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1420 return 1
1420 return 1
1421
1421
1422 err = False
1422 err = False
1423
1423
1424 p1, p2 = pl[0], node
1424 p1, p2 = pl[0], node
1425 pa = self.changelog.ancestor(p1, p2)
1425 pa = self.changelog.ancestor(p1, p2)
1426 m1n = self.changelog.read(p1)[0]
1426 m1n = self.changelog.read(p1)[0]
1427 m2n = self.changelog.read(p2)[0]
1427 m2n = self.changelog.read(p2)[0]
1428 man = self.manifest.ancestor(m1n, m2n)
1428 man = self.manifest.ancestor(m1n, m2n)
1429 m1 = self.manifest.read(m1n)
1429 m1 = self.manifest.read(m1n)
1430 mf1 = self.manifest.readflags(m1n)
1430 mf1 = self.manifest.readflags(m1n)
1431 m2 = self.manifest.read(m2n).copy()
1431 m2 = self.manifest.read(m2n).copy()
1432 mf2 = self.manifest.readflags(m2n)
1432 mf2 = self.manifest.readflags(m2n)
1433 ma = self.manifest.read(man)
1433 ma = self.manifest.read(man)
1434 mfa = self.manifest.readflags(man)
1434 mfa = self.manifest.readflags(man)
1435
1435
1436 modified, added, removed, deleted, unknown = self.changes()
1436 modified, added, removed, deleted, unknown = self.changes()
1437
1437
1438 # is this a jump, or a merge? i.e. is there a linear path
1438 # is this a jump, or a merge? i.e. is there a linear path
1439 # from p1 to p2?
1439 # from p1 to p2?
1440 linear_path = (pa == p1 or pa == p2)
1440 linear_path = (pa == p1 or pa == p2)
1441
1441
1442 if allow and linear_path:
1442 if allow and linear_path:
1443 raise util.Abort(_("there is nothing to merge, "
1443 raise util.Abort(_("there is nothing to merge, "
1444 "just use 'hg update'"))
1444 "just use 'hg update'"))
1445 if allow and not forcemerge:
1445 if allow and not forcemerge:
1446 if modified or added or removed:
1446 if modified or added or removed:
1447 raise util.Abort(_("outstanding uncommited changes"))
1447 raise util.Abort(_("outstanding uncommited changes"))
1448 if not forcemerge and not force:
1448 if not forcemerge and not force:
1449 for f in unknown:
1449 for f in unknown:
1450 if f in m2:
1450 if f in m2:
1451 t1 = self.wread(f)
1451 t1 = self.wread(f)
1452 t2 = self.file(f).read(m2[f])
1452 t2 = self.file(f).read(m2[f])
1453 if cmp(t1, t2) != 0:
1453 if cmp(t1, t2) != 0:
1454 raise util.Abort(_("'%s' already exists in the working"
1454 raise util.Abort(_("'%s' already exists in the working"
1455 " dir and differs from remote") % f)
1455 " dir and differs from remote") % f)
1456
1456
1457 # resolve the manifest to determine which files
1457 # resolve the manifest to determine which files
1458 # we care about merging
1458 # we care about merging
1459 self.ui.note(_("resolving manifests\n"))
1459 self.ui.note(_("resolving manifests\n"))
1460 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1460 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1461 (force, allow, moddirstate, linear_path))
1461 (force, allow, moddirstate, linear_path))
1462 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1462 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1463 (short(man), short(m1n), short(m2n)))
1463 (short(man), short(m1n), short(m2n)))
1464
1464
1465 merge = {}
1465 merge = {}
1466 get = {}
1466 get = {}
1467 remove = []
1467 remove = []
1468
1468
1469 # construct a working dir manifest
1469 # construct a working dir manifest
1470 mw = m1.copy()
1470 mw = m1.copy()
1471 mfw = mf1.copy()
1471 mfw = mf1.copy()
1472 umap = dict.fromkeys(unknown)
1472 umap = dict.fromkeys(unknown)
1473
1473
1474 for f in added + modified + unknown:
1474 for f in added + modified + unknown:
1475 mw[f] = ""
1475 mw[f] = ""
1476 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1476 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1477
1477
1478 if moddirstate and not wlock:
1478 if moddirstate and not wlock:
1479 wlock = self.wlock()
1479 wlock = self.wlock()
1480
1480
1481 for f in deleted + removed:
1481 for f in deleted + removed:
1482 if f in mw:
1482 if f in mw:
1483 del mw[f]
1483 del mw[f]
1484
1484
1485 # If we're jumping between revisions (as opposed to merging),
1485 # If we're jumping between revisions (as opposed to merging),
1486 # and if neither the working directory nor the target rev has
1486 # and if neither the working directory nor the target rev has
1487 # the file, then we need to remove it from the dirstate, to
1487 # the file, then we need to remove it from the dirstate, to
1488 # prevent the dirstate from listing the file when it is no
1488 # prevent the dirstate from listing the file when it is no
1489 # longer in the manifest.
1489 # longer in the manifest.
1490 if moddirstate and linear_path and f not in m2:
1490 if moddirstate and linear_path and f not in m2:
1491 self.dirstate.forget((f,))
1491 self.dirstate.forget((f,))
1492
1492
1493 # Compare manifests
1493 # Compare manifests
1494 for f, n in mw.iteritems():
1494 for f, n in mw.iteritems():
1495 if choose and not choose(f):
1495 if choose and not choose(f):
1496 continue
1496 continue
1497 if f in m2:
1497 if f in m2:
1498 s = 0
1498 s = 0
1499
1499
1500 # is the wfile new since m1, and match m2?
1500 # is the wfile new since m1, and match m2?
1501 if f not in m1:
1501 if f not in m1:
1502 t1 = self.wread(f)
1502 t1 = self.wread(f)
1503 t2 = self.file(f).read(m2[f])
1503 t2 = self.file(f).read(m2[f])
1504 if cmp(t1, t2) == 0:
1504 if cmp(t1, t2) == 0:
1505 n = m2[f]
1505 n = m2[f]
1506 del t1, t2
1506 del t1, t2
1507
1507
1508 # are files different?
1508 # are files different?
1509 if n != m2[f]:
1509 if n != m2[f]:
1510 a = ma.get(f, nullid)
1510 a = ma.get(f, nullid)
1511 # are both different from the ancestor?
1511 # are both different from the ancestor?
1512 if n != a and m2[f] != a:
1512 if n != a and m2[f] != a:
1513 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1513 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1514 # merge executable bits
1514 # merge executable bits
1515 # "if we changed or they changed, change in merge"
1515 # "if we changed or they changed, change in merge"
1516 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1516 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1517 mode = ((a^b) | (a^c)) ^ a
1517 mode = ((a^b) | (a^c)) ^ a
1518 merge[f] = (m1.get(f, nullid), m2[f], mode)
1518 merge[f] = (m1.get(f, nullid), m2[f], mode)
1519 s = 1
1519 s = 1
1520 # are we clobbering?
1520 # are we clobbering?
1521 # is remote's version newer?
1521 # is remote's version newer?
1522 # or are we going back in time?
1522 # or are we going back in time?
1523 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1523 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1524 self.ui.debug(_(" remote %s is newer, get\n") % f)
1524 self.ui.debug(_(" remote %s is newer, get\n") % f)
1525 get[f] = m2[f]
1525 get[f] = m2[f]
1526 s = 1
1526 s = 1
1527 elif f in umap:
1527 elif f in umap:
1528 # this unknown file is the same as the checkout
1528 # this unknown file is the same as the checkout
1529 get[f] = m2[f]
1529 get[f] = m2[f]
1530
1530
1531 if not s and mfw[f] != mf2[f]:
1531 if not s and mfw[f] != mf2[f]:
1532 if force:
1532 if force:
1533 self.ui.debug(_(" updating permissions for %s\n") % f)
1533 self.ui.debug(_(" updating permissions for %s\n") % f)
1534 util.set_exec(self.wjoin(f), mf2[f])
1534 util.set_exec(self.wjoin(f), mf2[f])
1535 else:
1535 else:
1536 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1536 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1537 mode = ((a^b) | (a^c)) ^ a
1537 mode = ((a^b) | (a^c)) ^ a
1538 if mode != b:
1538 if mode != b:
1539 self.ui.debug(_(" updating permissions for %s\n")
1539 self.ui.debug(_(" updating permissions for %s\n")
1540 % f)
1540 % f)
1541 util.set_exec(self.wjoin(f), mode)
1541 util.set_exec(self.wjoin(f), mode)
1542 del m2[f]
1542 del m2[f]
1543 elif f in ma:
1543 elif f in ma:
1544 if n != ma[f]:
1544 if n != ma[f]:
1545 r = _("d")
1545 r = _("d")
1546 if not force and (linear_path or allow):
1546 if not force and (linear_path or allow):
1547 r = self.ui.prompt(
1547 r = self.ui.prompt(
1548 (_(" local changed %s which remote deleted\n") % f) +
1548 (_(" local changed %s which remote deleted\n") % f) +
1549 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1549 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1550 if r == _("d"):
1550 if r == _("d"):
1551 remove.append(f)
1551 remove.append(f)
1552 else:
1552 else:
1553 self.ui.debug(_("other deleted %s\n") % f)
1553 self.ui.debug(_("other deleted %s\n") % f)
1554 remove.append(f) # other deleted it
1554 remove.append(f) # other deleted it
1555 else:
1555 else:
1556 # file is created on branch or in working directory
1556 # file is created on branch or in working directory
1557 if force and f not in umap:
1557 if force and f not in umap:
1558 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1558 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1559 remove.append(f)
1559 remove.append(f)
1560 elif n == m1.get(f, nullid): # same as parent
1560 elif n == m1.get(f, nullid): # same as parent
1561 if p2 == pa: # going backwards?
1561 if p2 == pa: # going backwards?
1562 self.ui.debug(_("remote deleted %s\n") % f)
1562 self.ui.debug(_("remote deleted %s\n") % f)
1563 remove.append(f)
1563 remove.append(f)
1564 else:
1564 else:
1565 self.ui.debug(_("local modified %s, keeping\n") % f)
1565 self.ui.debug(_("local modified %s, keeping\n") % f)
1566 else:
1566 else:
1567 self.ui.debug(_("working dir created %s, keeping\n") % f)
1567 self.ui.debug(_("working dir created %s, keeping\n") % f)
1568
1568
1569 for f, n in m2.iteritems():
1569 for f, n in m2.iteritems():
1570 if choose and not choose(f):
1570 if choose and not choose(f):
1571 continue
1571 continue
1572 if f[0] == "/":
1572 if f[0] == "/":
1573 continue
1573 continue
1574 if f in ma and n != ma[f]:
1574 if f in ma and n != ma[f]:
1575 r = _("k")
1575 r = _("k")
1576 if not force and (linear_path or allow):
1576 if not force and (linear_path or allow):
1577 r = self.ui.prompt(
1577 r = self.ui.prompt(
1578 (_("remote changed %s which local deleted\n") % f) +
1578 (_("remote changed %s which local deleted\n") % f) +
1579 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1579 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1580 if r == _("k"):
1580 if r == _("k"):
1581 get[f] = n
1581 get[f] = n
1582 elif f not in ma:
1582 elif f not in ma:
1583 self.ui.debug(_("remote created %s\n") % f)
1583 self.ui.debug(_("remote created %s\n") % f)
1584 get[f] = n
1584 get[f] = n
1585 else:
1585 else:
1586 if force or p2 == pa: # going backwards?
1586 if force or p2 == pa: # going backwards?
1587 self.ui.debug(_("local deleted %s, recreating\n") % f)
1587 self.ui.debug(_("local deleted %s, recreating\n") % f)
1588 get[f] = n
1588 get[f] = n
1589 else:
1589 else:
1590 self.ui.debug(_("local deleted %s\n") % f)
1590 self.ui.debug(_("local deleted %s\n") % f)
1591
1591
1592 del mw, m1, m2, ma
1592 del mw, m1, m2, ma
1593
1593
1594 if force:
1594 if force:
1595 for f in merge:
1595 for f in merge:
1596 get[f] = merge[f][1]
1596 get[f] = merge[f][1]
1597 merge = {}
1597 merge = {}
1598
1598
1599 if linear_path or force:
1599 if linear_path or force:
1600 # we don't need to do any magic, just jump to the new rev
1600 # we don't need to do any magic, just jump to the new rev
1601 branch_merge = False
1601 branch_merge = False
1602 p1, p2 = p2, nullid
1602 p1, p2 = p2, nullid
1603 else:
1603 else:
1604 if not allow:
1604 if not allow:
1605 self.ui.status(_("this update spans a branch"
1605 self.ui.status(_("this update spans a branch"
1606 " affecting the following files:\n"))
1606 " affecting the following files:\n"))
1607 fl = merge.keys() + get.keys()
1607 fl = merge.keys() + get.keys()
1608 fl.sort()
1608 fl.sort()
1609 for f in fl:
1609 for f in fl:
1610 cf = ""
1610 cf = ""
1611 if f in merge:
1611 if f in merge:
1612 cf = _(" (resolve)")
1612 cf = _(" (resolve)")
1613 self.ui.status(" %s%s\n" % (f, cf))
1613 self.ui.status(" %s%s\n" % (f, cf))
1614 self.ui.warn(_("aborting update spanning branches!\n"))
1614 self.ui.warn(_("aborting update spanning branches!\n"))
1615 self.ui.status(_("(use update -m to merge across branches"
1615 self.ui.status(_("(use update -m to merge across branches"
1616 " or -C to lose changes)\n"))
1616 " or -C to lose changes)\n"))
1617 return 1
1617 return 1
1618 branch_merge = True
1618 branch_merge = True
1619
1619
1620 # get the files we don't need to change
1620 # get the files we don't need to change
1621 files = get.keys()
1621 files = get.keys()
1622 files.sort()
1622 files.sort()
1623 for f in files:
1623 for f in files:
1624 if f[0] == "/":
1624 if f[0] == "/":
1625 continue
1625 continue
1626 self.ui.note(_("getting %s\n") % f)
1626 self.ui.note(_("getting %s\n") % f)
1627 t = self.file(f).read(get[f])
1627 t = self.file(f).read(get[f])
1628 self.wwrite(f, t)
1628 self.wwrite(f, t)
1629 util.set_exec(self.wjoin(f), mf2[f])
1629 util.set_exec(self.wjoin(f), mf2[f])
1630 if moddirstate:
1630 if moddirstate:
1631 if branch_merge:
1631 if branch_merge:
1632 self.dirstate.update([f], 'n', st_mtime=-1)
1632 self.dirstate.update([f], 'n', st_mtime=-1)
1633 else:
1633 else:
1634 self.dirstate.update([f], 'n')
1634 self.dirstate.update([f], 'n')
1635
1635
1636 # merge the tricky bits
1636 # merge the tricky bits
1637 files = merge.keys()
1637 files = merge.keys()
1638 files.sort()
1638 files.sort()
1639 xp1 = hex(p1)
1639 xp1 = hex(p1)
1640 xp2 = hex(p2)
1640 xp2 = hex(p2)
1641 for f in files:
1641 for f in files:
1642 self.ui.status(_("merging %s\n") % f)
1642 self.ui.status(_("merging %s\n") % f)
1643 my, other, flag = merge[f]
1643 my, other, flag = merge[f]
1644 ret = self.merge3(f, my, other, xp1, xp2)
1644 ret = self.merge3(f, my, other, xp1, xp2)
1645 if ret:
1645 if ret:
1646 err = True
1646 err = True
1647 util.set_exec(self.wjoin(f), flag)
1647 util.set_exec(self.wjoin(f), flag)
1648 if moddirstate:
1648 if moddirstate:
1649 if branch_merge:
1649 if branch_merge:
1650 # We've done a branch merge, mark this file as merged
1650 # We've done a branch merge, mark this file as merged
1651 # so that we properly record the merger later
1651 # so that we properly record the merger later
1652 self.dirstate.update([f], 'm')
1652 self.dirstate.update([f], 'm')
1653 else:
1653 else:
1654 # We've update-merged a locally modified file, so
1654 # We've update-merged a locally modified file, so
1655 # we set the dirstate to emulate a normal checkout
1655 # we set the dirstate to emulate a normal checkout
1656 # of that file some time in the past. Thus our
1656 # of that file some time in the past. Thus our
1657 # merge will appear as a normal local file
1657 # merge will appear as a normal local file
1658 # modification.
1658 # modification.
1659 f_len = len(self.file(f).read(other))
1659 f_len = len(self.file(f).read(other))
1660 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1660 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1661
1661
1662 remove.sort()
1662 remove.sort()
1663 for f in remove:
1663 for f in remove:
1664 self.ui.note(_("removing %s\n") % f)
1664 self.ui.note(_("removing %s\n") % f)
1665 util.audit_path(f)
1665 util.audit_path(f)
1666 try:
1666 try:
1667 util.unlink(self.wjoin(f))
1667 util.unlink(self.wjoin(f))
1668 except OSError, inst:
1668 except OSError, inst:
1669 if inst.errno != errno.ENOENT:
1669 if inst.errno != errno.ENOENT:
1670 self.ui.warn(_("update failed to remove %s: %s!\n") %
1670 self.ui.warn(_("update failed to remove %s: %s!\n") %
1671 (f, inst.strerror))
1671 (f, inst.strerror))
1672 if moddirstate:
1672 if moddirstate:
1673 if branch_merge:
1673 if branch_merge:
1674 self.dirstate.update(remove, 'r')
1674 self.dirstate.update(remove, 'r')
1675 else:
1675 else:
1676 self.dirstate.forget(remove)
1676 self.dirstate.forget(remove)
1677
1677
1678 if moddirstate:
1678 if moddirstate:
1679 self.dirstate.setparents(p1, p2)
1679 self.dirstate.setparents(p1, p2)
1680 return err
1680 return err
1681
1681
1682 def merge3(self, fn, my, other, p1, p2):
1682 def merge3(self, fn, my, other, p1, p2):
1683 """perform a 3-way merge in the working directory"""
1683 """perform a 3-way merge in the working directory"""
1684
1684
1685 def temp(prefix, node):
1685 def temp(prefix, node):
1686 pre = "%s~%s." % (os.path.basename(fn), prefix)
1686 pre = "%s~%s." % (os.path.basename(fn), prefix)
1687 (fd, name) = tempfile.mkstemp("", pre)
1687 (fd, name) = tempfile.mkstemp("", pre)
1688 f = os.fdopen(fd, "wb")
1688 f = os.fdopen(fd, "wb")
1689 self.wwrite(fn, fl.read(node), f)
1689 self.wwrite(fn, fl.read(node), f)
1690 f.close()
1690 f.close()
1691 return name
1691 return name
1692
1692
1693 fl = self.file(fn)
1693 fl = self.file(fn)
1694 base = fl.ancestor(my, other)
1694 base = fl.ancestor(my, other)
1695 a = self.wjoin(fn)
1695 a = self.wjoin(fn)
1696 b = temp("base", base)
1696 b = temp("base", base)
1697 c = temp("other", other)
1697 c = temp("other", other)
1698
1698
1699 self.ui.note(_("resolving %s\n") % fn)
1699 self.ui.note(_("resolving %s\n") % fn)
1700 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1700 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1701 (fn, short(my), short(other), short(base)))
1701 (fn, short(my), short(other), short(base)))
1702
1702
1703 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1703 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1704 or "hgmerge")
1704 or "hgmerge")
1705 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
1705 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
1706 environ={'HG_FILE': fn,
1706 environ={'HG_FILE': fn,
1707 'HG_MY_NODE': p1,
1707 'HG_MY_NODE': p1,
1708 'HG_OTHER_NODE': p2,
1708 'HG_OTHER_NODE': p2,
1709 'HG_FILE_MY_NODE': hex(my),
1709 'HG_FILE_MY_NODE': hex(my),
1710 'HG_FILE_OTHER_NODE': hex(other),
1710 'HG_FILE_OTHER_NODE': hex(other),
1711 'HG_FILE_BASE_NODE': hex(base)})
1711 'HG_FILE_BASE_NODE': hex(base)})
1712 if r:
1712 if r:
1713 self.ui.warn(_("merging %s failed!\n") % fn)
1713 self.ui.warn(_("merging %s failed!\n") % fn)
1714
1714
1715 os.unlink(b)
1715 os.unlink(b)
1716 os.unlink(c)
1716 os.unlink(c)
1717 return r
1717 return r
1718
1718
1719 def verify(self):
1719 def verify(self):
1720 filelinkrevs = {}
1720 filelinkrevs = {}
1721 filenodes = {}
1721 filenodes = {}
1722 changesets = revisions = files = 0
1722 changesets = revisions = files = 0
1723 errors = [0]
1723 errors = [0]
1724 neededmanifests = {}
1724 neededmanifests = {}
1725
1725
1726 def err(msg):
1726 def err(msg):
1727 self.ui.warn(msg + "\n")
1727 self.ui.warn(msg + "\n")
1728 errors[0] += 1
1728 errors[0] += 1
1729
1729
1730 def checksize(obj, name):
1730 def checksize(obj, name):
1731 d = obj.checksize()
1731 d = obj.checksize()
1732 if d[0]:
1732 if d[0]:
1733 err(_("%s data length off by %d bytes") % (name, d[0]))
1733 err(_("%s data length off by %d bytes") % (name, d[0]))
1734 if d[1]:
1734 if d[1]:
1735 err(_("%s index contains %d extra bytes") % (name, d[1]))
1735 err(_("%s index contains %d extra bytes") % (name, d[1]))
1736
1736
1737 seen = {}
1737 seen = {}
1738 self.ui.status(_("checking changesets\n"))
1738 self.ui.status(_("checking changesets\n"))
1739 checksize(self.changelog, "changelog")
1739 checksize(self.changelog, "changelog")
1740
1740
1741 for i in range(self.changelog.count()):
1741 for i in range(self.changelog.count()):
1742 changesets += 1
1742 changesets += 1
1743 n = self.changelog.node(i)
1743 n = self.changelog.node(i)
1744 l = self.changelog.linkrev(n)
1744 l = self.changelog.linkrev(n)
1745 if l != i:
1745 if l != i:
1746 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
1746 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
1747 if n in seen:
1747 if n in seen:
1748 err(_("duplicate changeset at revision %d") % i)
1748 err(_("duplicate changeset at revision %d") % i)
1749 seen[n] = 1
1749 seen[n] = 1
1750
1750
1751 for p in self.changelog.parents(n):
1751 for p in self.changelog.parents(n):
1752 if p not in self.changelog.nodemap:
1752 if p not in self.changelog.nodemap:
1753 err(_("changeset %s has unknown parent %s") %
1753 err(_("changeset %s has unknown parent %s") %
1754 (short(n), short(p)))
1754 (short(n), short(p)))
1755 try:
1755 try:
1756 changes = self.changelog.read(n)
1756 changes = self.changelog.read(n)
1757 except KeyboardInterrupt:
1757 except KeyboardInterrupt:
1758 self.ui.warn(_("interrupted"))
1758 self.ui.warn(_("interrupted"))
1759 raise
1759 raise
1760 except Exception, inst:
1760 except Exception, inst:
1761 err(_("unpacking changeset %s: %s") % (short(n), inst))
1761 err(_("unpacking changeset %s: %s") % (short(n), inst))
1762 continue
1762 continue
1763
1763
1764 neededmanifests[changes[0]] = n
1764 neededmanifests[changes[0]] = n
1765
1765
1766 for f in changes[3]:
1766 for f in changes[3]:
1767 filelinkrevs.setdefault(f, []).append(i)
1767 filelinkrevs.setdefault(f, []).append(i)
1768
1768
1769 seen = {}
1769 seen = {}
1770 self.ui.status(_("checking manifests\n"))
1770 self.ui.status(_("checking manifests\n"))
1771 checksize(self.manifest, "manifest")
1771 checksize(self.manifest, "manifest")
1772
1772
1773 for i in range(self.manifest.count()):
1773 for i in range(self.manifest.count()):
1774 n = self.manifest.node(i)
1774 n = self.manifest.node(i)
1775 l = self.manifest.linkrev(n)
1775 l = self.manifest.linkrev(n)
1776
1776
1777 if l < 0 or l >= self.changelog.count():
1777 if l < 0 or l >= self.changelog.count():
1778 err(_("bad manifest link (%d) at revision %d") % (l, i))
1778 err(_("bad manifest link (%d) at revision %d") % (l, i))
1779
1779
1780 if n in neededmanifests:
1780 if n in neededmanifests:
1781 del neededmanifests[n]
1781 del neededmanifests[n]
1782
1782
1783 if n in seen:
1783 if n in seen:
1784 err(_("duplicate manifest at revision %d") % i)
1784 err(_("duplicate manifest at revision %d") % i)
1785
1785
1786 seen[n] = 1
1786 seen[n] = 1
1787
1787
1788 for p in self.manifest.parents(n):
1788 for p in self.manifest.parents(n):
1789 if p not in self.manifest.nodemap:
1789 if p not in self.manifest.nodemap:
1790 err(_("manifest %s has unknown parent %s") %
1790 err(_("manifest %s has unknown parent %s") %
1791 (short(n), short(p)))
1791 (short(n), short(p)))
1792
1792
1793 try:
1793 try:
1794 delta = mdiff.patchtext(self.manifest.delta(n))
1794 delta = mdiff.patchtext(self.manifest.delta(n))
1795 except KeyboardInterrupt:
1795 except KeyboardInterrupt:
1796 self.ui.warn(_("interrupted"))
1796 self.ui.warn(_("interrupted"))
1797 raise
1797 raise
1798 except Exception, inst:
1798 except Exception, inst:
1799 err(_("unpacking manifest %s: %s") % (short(n), inst))
1799 err(_("unpacking manifest %s: %s") % (short(n), inst))
1800 continue
1800 continue
1801
1801
1802 try:
1802 try:
1803 ff = [ l.split('\0') for l in delta.splitlines() ]
1803 ff = [ l.split('\0') for l in delta.splitlines() ]
1804 for f, fn in ff:
1804 for f, fn in ff:
1805 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1805 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1806 except (ValueError, TypeError), inst:
1806 except (ValueError, TypeError), inst:
1807 err(_("broken delta in manifest %s: %s") % (short(n), inst))
1807 err(_("broken delta in manifest %s: %s") % (short(n), inst))
1808
1808
1809 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1809 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1810
1810
1811 for m, c in neededmanifests.items():
1811 for m, c in neededmanifests.items():
1812 err(_("Changeset %s refers to unknown manifest %s") %
1812 err(_("Changeset %s refers to unknown manifest %s") %
1813 (short(m), short(c)))
1813 (short(m), short(c)))
1814 del neededmanifests
1814 del neededmanifests
1815
1815
1816 for f in filenodes:
1816 for f in filenodes:
1817 if f not in filelinkrevs:
1817 if f not in filelinkrevs:
1818 err(_("file %s in manifest but not in changesets") % f)
1818 err(_("file %s in manifest but not in changesets") % f)
1819
1819
1820 for f in filelinkrevs:
1820 for f in filelinkrevs:
1821 if f not in filenodes:
1821 if f not in filenodes:
1822 err(_("file %s in changeset but not in manifest") % f)
1822 err(_("file %s in changeset but not in manifest") % f)
1823
1823
1824 self.ui.status(_("checking files\n"))
1824 self.ui.status(_("checking files\n"))
1825 ff = filenodes.keys()
1825 ff = filenodes.keys()
1826 ff.sort()
1826 ff.sort()
1827 for f in ff:
1827 for f in ff:
1828 if f == "/dev/null":
1828 if f == "/dev/null":
1829 continue
1829 continue
1830 files += 1
1830 files += 1
1831 if not f:
1831 if not f:
1832 err(_("file without name in manifest %s") % short(n))
1832 err(_("file without name in manifest %s") % short(n))
1833 continue
1833 continue
1834 fl = self.file(f)
1834 fl = self.file(f)
1835 checksize(fl, f)
1835 checksize(fl, f)
1836
1836
1837 nodes = {nullid: 1}
1837 nodes = {nullid: 1}
1838 seen = {}
1838 seen = {}
1839 for i in range(fl.count()):
1839 for i in range(fl.count()):
1840 revisions += 1
1840 revisions += 1
1841 n = fl.node(i)
1841 n = fl.node(i)
1842
1842
1843 if n in seen:
1843 if n in seen:
1844 err(_("%s: duplicate revision %d") % (f, i))
1844 err(_("%s: duplicate revision %d") % (f, i))
1845 if n not in filenodes[f]:
1845 if n not in filenodes[f]:
1846 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
1846 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
1847 else:
1847 else:
1848 del filenodes[f][n]
1848 del filenodes[f][n]
1849
1849
1850 flr = fl.linkrev(n)
1850 flr = fl.linkrev(n)
1851 if flr not in filelinkrevs.get(f, []):
1851 if flr not in filelinkrevs.get(f, []):
1852 err(_("%s:%s points to unexpected changeset %d")
1852 err(_("%s:%s points to unexpected changeset %d")
1853 % (f, short(n), flr))
1853 % (f, short(n), flr))
1854 else:
1854 else:
1855 filelinkrevs[f].remove(flr)
1855 filelinkrevs[f].remove(flr)
1856
1856
1857 # verify contents
1857 # verify contents
1858 try:
1858 try:
1859 t = fl.read(n)
1859 t = fl.read(n)
1860 except KeyboardInterrupt:
1860 except KeyboardInterrupt:
1861 self.ui.warn(_("interrupted"))
1861 self.ui.warn(_("interrupted"))
1862 raise
1862 raise
1863 except Exception, inst:
1863 except Exception, inst:
1864 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
1864 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
1865
1865
1866 # verify parents
1866 # verify parents
1867 (p1, p2) = fl.parents(n)
1867 (p1, p2) = fl.parents(n)
1868 if p1 not in nodes:
1868 if p1 not in nodes:
1869 err(_("file %s:%s unknown parent 1 %s") %
1869 err(_("file %s:%s unknown parent 1 %s") %
1870 (f, short(n), short(p1)))
1870 (f, short(n), short(p1)))
1871 if p2 not in nodes:
1871 if p2 not in nodes:
1872 err(_("file %s:%s unknown parent 2 %s") %
1872 err(_("file %s:%s unknown parent 2 %s") %
1873 (f, short(n), short(p1)))
1873 (f, short(n), short(p1)))
1874 nodes[n] = 1
1874 nodes[n] = 1
1875
1875
1876 # cross-check
1876 # cross-check
1877 for node in filenodes[f]:
1877 for node in filenodes[f]:
1878 err(_("node %s in manifests not in %s") % (hex(node), f))
1878 err(_("node %s in manifests not in %s") % (hex(node), f))
1879
1879
1880 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
1880 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
1881 (files, changesets, revisions))
1881 (files, changesets, revisions))
1882
1882
1883 if errors[0]:
1883 if errors[0]:
1884 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1884 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1885 return 1
1885 return 1
1886
1886
1887 # used to avoid circular references so destructors work
1887 # used to avoid circular references so destructors work
1888 def aftertrans(base):
1888 def aftertrans(base):
1889 p = base
1889 p = base
1890 def a():
1890 def a():
1891 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
1891 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
1892 util.rename(os.path.join(p, "journal.dirstate"),
1892 util.rename(os.path.join(p, "journal.dirstate"),
1893 os.path.join(p, "undo.dirstate"))
1893 os.path.join(p, "undo.dirstate"))
1894 return a
1894 return a
1895
1895
@@ -1,225 +1,224 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import ConfigParser
8 import ConfigParser
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "os re socket sys util")
11 demandload(globals(), "os re socket sys util")
12
12
13 class ui(object):
13 class ui(object):
14 def __init__(self, verbose=False, debug=False, quiet=False,
14 def __init__(self, verbose=False, debug=False, quiet=False,
15 interactive=True, parentui=None):
15 interactive=True, parentui=None):
16 self.overlay = {}
16 self.overlay = {}
17 if parentui is None:
17 if parentui is None:
18 # this is the parent of all ui children
18 # this is the parent of all ui children
19 self.parentui = None
19 self.parentui = None
20 self.cdata = ConfigParser.SafeConfigParser()
20 self.cdata = ConfigParser.SafeConfigParser()
21 self.readconfig(util.rcpath)
21 self.readconfig(util.rcpath)
22
22
23 self.quiet = self.configbool("ui", "quiet")
23 self.quiet = self.configbool("ui", "quiet")
24 self.verbose = self.configbool("ui", "verbose")
24 self.verbose = self.configbool("ui", "verbose")
25 self.debugflag = self.configbool("ui", "debug")
25 self.debugflag = self.configbool("ui", "debug")
26 self.interactive = self.configbool("ui", "interactive", True)
26 self.interactive = self.configbool("ui", "interactive", True)
27
27
28 self.updateopts(verbose, debug, quiet, interactive)
28 self.updateopts(verbose, debug, quiet, interactive)
29 self.diffcache = None
29 self.diffcache = None
30 else:
30 else:
31 # parentui may point to an ui object which is already a child
31 # parentui may point to an ui object which is already a child
32 self.parentui = parentui.parentui or parentui
32 self.parentui = parentui.parentui or parentui
33 parent_cdata = self.parentui.cdata
33 parent_cdata = self.parentui.cdata
34 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
34 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
35 # make interpolation work
35 # make interpolation work
36 for section in parent_cdata.sections():
36 for section in parent_cdata.sections():
37 self.cdata.add_section(section)
37 self.cdata.add_section(section)
38 for name, value in parent_cdata.items(section, raw=True):
38 for name, value in parent_cdata.items(section, raw=True):
39 self.cdata.set(section, name, value)
39 self.cdata.set(section, name, value)
40
40
41 def __getattr__(self, key):
41 def __getattr__(self, key):
42 return getattr(self.parentui, key)
42 return getattr(self.parentui, key)
43
43
44 def updateopts(self, verbose=False, debug=False, quiet=False,
44 def updateopts(self, verbose=False, debug=False, quiet=False,
45 interactive=True):
45 interactive=True):
46 self.quiet = (self.quiet or quiet) and not verbose and not debug
46 self.quiet = (self.quiet or quiet) and not verbose and not debug
47 self.verbose = (self.verbose or verbose) or debug
47 self.verbose = (self.verbose or verbose) or debug
48 self.debugflag = (self.debugflag or debug)
48 self.debugflag = (self.debugflag or debug)
49 self.interactive = (self.interactive and interactive)
49 self.interactive = (self.interactive and interactive)
50
50
51 def readconfig(self, fn):
51 def readconfig(self, fn, root=None):
52 if isinstance(fn, basestring):
52 if isinstance(fn, basestring):
53 fn = [fn]
53 fn = [fn]
54 for f in fn:
54 for f in fn:
55 try:
55 try:
56 self.cdata.read(f)
56 self.cdata.read(f)
57 except ConfigParser.ParsingError, inst:
57 except ConfigParser.ParsingError, inst:
58 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
58 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
59 # translate paths relative to root (or home) into absolute paths
60 if root is None:
61 root = os.path.expanduser('~')
62 for name, path in self.configitems("paths"):
63 if path.find("://") == -1 and not os.path.isabs(path):
64 self.cdata.set("paths", name, os.path.join(root, path))
59
65
60 def setconfig(self, section, name, val):
66 def setconfig(self, section, name, val):
61 self.overlay[(section, name)] = val
67 self.overlay[(section, name)] = val
62
68
63 def config(self, section, name, default=None):
69 def config(self, section, name, default=None):
64 if self.overlay.has_key((section, name)):
70 if self.overlay.has_key((section, name)):
65 return self.overlay[(section, name)]
71 return self.overlay[(section, name)]
66 if self.cdata.has_option(section, name):
72 if self.cdata.has_option(section, name):
67 try:
73 try:
68 return self.cdata.get(section, name)
74 return self.cdata.get(section, name)
69 except ConfigParser.InterpolationError, inst:
75 except ConfigParser.InterpolationError, inst:
70 raise util.Abort(_("Error in configuration:\n%s") % inst)
76 raise util.Abort(_("Error in configuration:\n%s") % inst)
71 if self.parentui is None:
77 if self.parentui is None:
72 return default
78 return default
73 else:
79 else:
74 return self.parentui.config(section, name, default)
80 return self.parentui.config(section, name, default)
75
81
76 def configbool(self, section, name, default=False):
82 def configbool(self, section, name, default=False):
77 if self.overlay.has_key((section, name)):
83 if self.overlay.has_key((section, name)):
78 return self.overlay[(section, name)]
84 return self.overlay[(section, name)]
79 if self.cdata.has_option(section, name):
85 if self.cdata.has_option(section, name):
80 try:
86 try:
81 return self.cdata.getboolean(section, name)
87 return self.cdata.getboolean(section, name)
82 except ConfigParser.InterpolationError, inst:
88 except ConfigParser.InterpolationError, inst:
83 raise util.Abort(_("Error in configuration:\n%s") % inst)
89 raise util.Abort(_("Error in configuration:\n%s") % inst)
84 if self.parentui is None:
90 if self.parentui is None:
85 return default
91 return default
86 else:
92 else:
87 return self.parentui.configbool(section, name, default)
93 return self.parentui.configbool(section, name, default)
88
94
89 def configitems(self, section):
95 def configitems(self, section):
90 items = {}
96 items = {}
91 if self.parentui is not None:
97 if self.parentui is not None:
92 items = dict(self.parentui.configitems(section))
98 items = dict(self.parentui.configitems(section))
93 if self.cdata.has_section(section):
99 if self.cdata.has_section(section):
94 try:
100 try:
95 items.update(dict(self.cdata.items(section)))
101 items.update(dict(self.cdata.items(section)))
96 except ConfigParser.InterpolationError, inst:
102 except ConfigParser.InterpolationError, inst:
97 raise util.Abort(_("Error in configuration:\n%s") % inst)
103 raise util.Abort(_("Error in configuration:\n%s") % inst)
98 x = items.items()
104 x = items.items()
99 x.sort()
105 x.sort()
100 return x
106 return x
101
107
102 def walkconfig(self, seen=None):
108 def walkconfig(self, seen=None):
103 if seen is None:
109 if seen is None:
104 seen = {}
110 seen = {}
105 for (section, name), value in self.overlay.iteritems():
111 for (section, name), value in self.overlay.iteritems():
106 yield section, name, value
112 yield section, name, value
107 seen[section, name] = 1
113 seen[section, name] = 1
108 for section in self.cdata.sections():
114 for section in self.cdata.sections():
109 for name, value in self.cdata.items(section):
115 for name, value in self.cdata.items(section):
110 if (section, name) in seen: continue
116 if (section, name) in seen: continue
111 yield section, name, value.replace('\n', '\\n')
117 yield section, name, value.replace('\n', '\\n')
112 seen[section, name] = 1
118 seen[section, name] = 1
113 if self.parentui is not None:
119 if self.parentui is not None:
114 for parent in self.parentui.walkconfig(seen):
120 for parent in self.parentui.walkconfig(seen):
115 yield parent
121 yield parent
116
122
117 def extensions(self):
123 def extensions(self):
118 return self.configitems("extensions")
124 return self.configitems("extensions")
119
125
120 def diffopts(self):
126 def diffopts(self):
121 if self.diffcache:
127 if self.diffcache:
122 return self.diffcache
128 return self.diffcache
123 ret = { 'showfunc' : True, 'ignorews' : False}
129 ret = { 'showfunc' : True, 'ignorews' : False}
124 for x in self.configitems("diff"):
130 for x in self.configitems("diff"):
125 k = x[0].lower()
131 k = x[0].lower()
126 v = x[1]
132 v = x[1]
127 if v:
133 if v:
128 v = v.lower()
134 v = v.lower()
129 if v == 'true':
135 if v == 'true':
130 value = True
136 value = True
131 else:
137 else:
132 value = False
138 value = False
133 ret[k] = value
139 ret[k] = value
134 self.diffcache = ret
140 self.diffcache = ret
135 return ret
141 return ret
136
142
137 def username(self):
143 def username(self):
138 return (os.environ.get("HGUSER") or
144 return (os.environ.get("HGUSER") or
139 self.config("ui", "username") or
145 self.config("ui", "username") or
140 os.environ.get("EMAIL") or
146 os.environ.get("EMAIL") or
141 (os.environ.get("LOGNAME",
147 (os.environ.get("LOGNAME",
142 os.environ.get("USERNAME", "unknown"))
148 os.environ.get("USERNAME", "unknown"))
143 + '@' + socket.getfqdn()))
149 + '@' + socket.getfqdn()))
144
150
145 def shortuser(self, user):
151 def shortuser(self, user):
146 """Return a short representation of a user name or email address."""
152 """Return a short representation of a user name or email address."""
147 if not self.verbose:
153 if not self.verbose:
148 f = user.find('@')
154 f = user.find('@')
149 if f >= 0:
155 if f >= 0:
150 user = user[:f]
156 user = user[:f]
151 f = user.find('<')
157 f = user.find('<')
152 if f >= 0:
158 if f >= 0:
153 user = user[f+1:]
159 user = user[f+1:]
154 return user
160 return user
155
161
156 def expandpath(self, loc, root=""):
162 def expandpath(self, loc):
157 """Return repository location relative to cwd or from [paths]"""
163 """Return repository location relative to cwd or from [paths]"""
158 if os.path.exists(loc):
164 if loc.find("://") != -1 or os.path.exists(loc):
159 return loc
165 return loc
160
166
161 paths = {}
167 return self.config("paths", loc, loc)
162 for name, path in self.configitems("paths"):
163 m = path.find("://")
164 if m == -1:
165 path = os.path.join(root, path)
166 paths[name] = path
167
168 return paths.get(loc, loc)
169
168
170 def write(self, *args):
169 def write(self, *args):
171 for a in args:
170 for a in args:
172 sys.stdout.write(str(a))
171 sys.stdout.write(str(a))
173
172
174 def write_err(self, *args):
173 def write_err(self, *args):
175 if not sys.stdout.closed: sys.stdout.flush()
174 if not sys.stdout.closed: sys.stdout.flush()
176 for a in args:
175 for a in args:
177 sys.stderr.write(str(a))
176 sys.stderr.write(str(a))
178
177
179 def flush(self):
178 def flush(self):
180 try:
179 try:
181 sys.stdout.flush()
180 sys.stdout.flush()
182 finally:
181 finally:
183 sys.stderr.flush()
182 sys.stderr.flush()
184
183
185 def readline(self):
184 def readline(self):
186 return sys.stdin.readline()[:-1]
185 return sys.stdin.readline()[:-1]
187 def prompt(self, msg, pat, default="y"):
186 def prompt(self, msg, pat, default="y"):
188 if not self.interactive: return default
187 if not self.interactive: return default
189 while 1:
188 while 1:
190 self.write(msg, " ")
189 self.write(msg, " ")
191 r = self.readline()
190 r = self.readline()
192 if re.match(pat, r):
191 if re.match(pat, r):
193 return r
192 return r
194 else:
193 else:
195 self.write(_("unrecognized response\n"))
194 self.write(_("unrecognized response\n"))
196 def status(self, *msg):
195 def status(self, *msg):
197 if not self.quiet: self.write(*msg)
196 if not self.quiet: self.write(*msg)
198 def warn(self, *msg):
197 def warn(self, *msg):
199 self.write_err(*msg)
198 self.write_err(*msg)
200 def note(self, *msg):
199 def note(self, *msg):
201 if self.verbose: self.write(*msg)
200 if self.verbose: self.write(*msg)
202 def debug(self, *msg):
201 def debug(self, *msg):
203 if self.debugflag: self.write(*msg)
202 if self.debugflag: self.write(*msg)
204 def edit(self, text):
203 def edit(self, text):
205 import tempfile
204 import tempfile
206 (fd, name) = tempfile.mkstemp("hg")
205 (fd, name) = tempfile.mkstemp("hg")
207 f = os.fdopen(fd, "w")
206 f = os.fdopen(fd, "w")
208 f.write(text)
207 f.write(text)
209 f.close()
208 f.close()
210
209
211 editor = (os.environ.get("HGEDITOR") or
210 editor = (os.environ.get("HGEDITOR") or
212 self.config("ui", "editor") or
211 self.config("ui", "editor") or
213 os.environ.get("EDITOR", "vi"))
212 os.environ.get("EDITOR", "vi"))
214
213
215 os.environ["HGUSER"] = self.username()
214 os.environ["HGUSER"] = self.username()
216 util.system("%s \"%s\"" % (editor, name),
215 util.system("%s \"%s\"" % (editor, name),
217 environ={'HGUSER': self.username()},
216 environ={'HGUSER': self.username()},
218 onerr=util.Abort, errprefix=_("edit failed"))
217 onerr=util.Abort, errprefix=_("edit failed"))
219
218
220 t = open(name).read()
219 t = open(name).read()
221 t = re.sub("(?m)^HG:.*\n", "", t)
220 t = re.sub("(?m)^HG:.*\n", "", t)
222
221
223 os.unlink(name)
222 os.unlink(name)
224
223
225 return t
224 return t
General Comments 0
You need to be logged in to leave comments. Login now