##// END OF EJS Templates
Implementing clone -r, which clones all changesets needed to reach a...
Eric Hopper -
r1461:02099220 default
parent child Browse files
Show More
@@ -1,2222 +1,2226 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 demandload(globals(), "os re sys signal shutil imp urllib pdb")
10 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "fancyopts ui hg util lock revlog")
11 demandload(globals(), "fancyopts ui hg util lock revlog")
12 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
13 demandload(globals(), "errno socket version struct atexit sets bz2")
13 demandload(globals(), "errno socket version struct atexit sets bz2")
14
14
15 class UnknownCommand(Exception):
15 class UnknownCommand(Exception):
16 """Exception raised if command is not in the command table."""
16 """Exception raised if command is not in the command table."""
17
17
18 def filterfiles(filters, files):
18 def filterfiles(filters, files):
19 l = [x for x in files if x in filters]
19 l = [x for x in files if x in filters]
20
20
21 for t in filters:
21 for t in filters:
22 if t and t[-1] != "/":
22 if t and t[-1] != "/":
23 t += "/"
23 t += "/"
24 l += [x for x in files if x.startswith(t)]
24 l += [x for x in files if x.startswith(t)]
25 return l
25 return l
26
26
27 def relpath(repo, args):
27 def relpath(repo, args):
28 cwd = repo.getcwd()
28 cwd = repo.getcwd()
29 if cwd:
29 if cwd:
30 return [util.normpath(os.path.join(cwd, x)) for x in args]
30 return [util.normpath(os.path.join(cwd, x)) for x in args]
31 return args
31 return args
32
32
33 def matchpats(repo, cwd, pats=[], opts={}, head=''):
33 def matchpats(repo, cwd, pats=[], opts={}, head=''):
34 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
34 return util.matcher(repo.root, cwd, pats or ['.'], opts.get('include'),
35 opts.get('exclude'), head)
35 opts.get('exclude'), head)
36
36
37 def makewalk(repo, pats, opts, head=''):
37 def makewalk(repo, pats, opts, head=''):
38 cwd = repo.getcwd()
38 cwd = repo.getcwd()
39 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
39 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
40 exact = dict(zip(files, files))
40 exact = dict(zip(files, files))
41 def walk():
41 def walk():
42 for src, fn in repo.walk(files=files, match=matchfn):
42 for src, fn in repo.walk(files=files, match=matchfn):
43 yield src, fn, util.pathto(cwd, fn), fn in exact
43 yield src, fn, util.pathto(cwd, fn), fn in exact
44 return files, matchfn, walk()
44 return files, matchfn, walk()
45
45
46 def walk(repo, pats, opts, head=''):
46 def walk(repo, pats, opts, head=''):
47 files, matchfn, results = makewalk(repo, pats, opts, head)
47 files, matchfn, results = makewalk(repo, pats, opts, head)
48 for r in results:
48 for r in results:
49 yield r
49 yield r
50
50
51 def walkchangerevs(ui, repo, cwd, pats, opts):
51 def walkchangerevs(ui, repo, cwd, pats, opts):
52 '''Iterate over files and the revs they changed in.
52 '''Iterate over files and the revs they changed in.
53
53
54 Callers most commonly need to iterate backwards over the history
54 Callers most commonly need to iterate backwards over the history
55 it is interested in. Doing so has awful (quadratic-looking)
55 it is interested in. Doing so has awful (quadratic-looking)
56 performance, so we use iterators in a "windowed" way.
56 performance, so we use iterators in a "windowed" way.
57
57
58 We walk a window of revisions in the desired order. Within the
58 We walk a window of revisions in the desired order. Within the
59 window, we first walk forwards to gather data, then in the desired
59 window, we first walk forwards to gather data, then in the desired
60 order (usually backwards) to display it.
60 order (usually backwards) to display it.
61
61
62 This function returns an (iterator, getchange) pair. The
62 This function returns an (iterator, getchange) pair. The
63 getchange function returns the changelog entry for a numeric
63 getchange function returns the changelog entry for a numeric
64 revision. The iterator yields 3-tuples. They will be of one of
64 revision. The iterator yields 3-tuples. They will be of one of
65 the following forms:
65 the following forms:
66
66
67 "window", incrementing, lastrev: stepping through a window,
67 "window", incrementing, lastrev: stepping through a window,
68 positive if walking forwards through revs, last rev in the
68 positive if walking forwards through revs, last rev in the
69 sequence iterated over - use to reset state for the current window
69 sequence iterated over - use to reset state for the current window
70
70
71 "add", rev, fns: out-of-order traversal of the given file names
71 "add", rev, fns: out-of-order traversal of the given file names
72 fns, which changed during revision rev - use to gather data for
72 fns, which changed during revision rev - use to gather data for
73 possible display
73 possible display
74
74
75 "iter", rev, None: in-order traversal of the revs earlier iterated
75 "iter", rev, None: in-order traversal of the revs earlier iterated
76 over with "add" - use to display data'''
76 over with "add" - use to display data'''
77
77
78 if repo.changelog.count() == 0:
78 if repo.changelog.count() == 0:
79 return [], False
79 return [], False
80
80
81 cwd = repo.getcwd()
81 cwd = repo.getcwd()
82 if not pats and cwd:
82 if not pats and cwd:
83 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
83 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
84 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
84 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
85 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
85 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
86 pats, opts)
86 pats, opts)
87 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
87 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
88 wanted = {}
88 wanted = {}
89 slowpath = anypats
89 slowpath = anypats
90 window = 300
90 window = 300
91 fncache = {}
91 fncache = {}
92
92
93 chcache = {}
93 chcache = {}
94 def getchange(rev):
94 def getchange(rev):
95 ch = chcache.get(rev)
95 ch = chcache.get(rev)
96 if ch is None:
96 if ch is None:
97 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
97 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
98 return ch
98 return ch
99
99
100 if not slowpath and not files:
100 if not slowpath and not files:
101 # No files, no patterns. Display all revs.
101 # No files, no patterns. Display all revs.
102 wanted = dict(zip(revs, revs))
102 wanted = dict(zip(revs, revs))
103 if not slowpath:
103 if not slowpath:
104 # Only files, no patterns. Check the history of each file.
104 # Only files, no patterns. Check the history of each file.
105 def filerevgen(filelog):
105 def filerevgen(filelog):
106 for i in xrange(filelog.count() - 1, -1, -window):
106 for i in xrange(filelog.count() - 1, -1, -window):
107 revs = []
107 revs = []
108 for j in xrange(max(0, i - window), i + 1):
108 for j in xrange(max(0, i - window), i + 1):
109 revs.append(filelog.linkrev(filelog.node(j)))
109 revs.append(filelog.linkrev(filelog.node(j)))
110 revs.reverse()
110 revs.reverse()
111 for rev in revs:
111 for rev in revs:
112 yield rev
112 yield rev
113
113
114 minrev, maxrev = min(revs), max(revs)
114 minrev, maxrev = min(revs), max(revs)
115 for file in files:
115 for file in files:
116 filelog = repo.file(file)
116 filelog = repo.file(file)
117 # A zero count may be a directory or deleted file, so
117 # A zero count may be a directory or deleted file, so
118 # try to find matching entries on the slow path.
118 # try to find matching entries on the slow path.
119 if filelog.count() == 0:
119 if filelog.count() == 0:
120 slowpath = True
120 slowpath = True
121 break
121 break
122 for rev in filerevgen(filelog):
122 for rev in filerevgen(filelog):
123 if rev <= maxrev:
123 if rev <= maxrev:
124 if rev < minrev:
124 if rev < minrev:
125 break
125 break
126 fncache.setdefault(rev, [])
126 fncache.setdefault(rev, [])
127 fncache[rev].append(file)
127 fncache[rev].append(file)
128 wanted[rev] = 1
128 wanted[rev] = 1
129 if slowpath:
129 if slowpath:
130 # The slow path checks files modified in every changeset.
130 # The slow path checks files modified in every changeset.
131 def changerevgen():
131 def changerevgen():
132 for i in xrange(repo.changelog.count() - 1, -1, -window):
132 for i in xrange(repo.changelog.count() - 1, -1, -window):
133 for j in xrange(max(0, i - window), i + 1):
133 for j in xrange(max(0, i - window), i + 1):
134 yield j, getchange(j)[3]
134 yield j, getchange(j)[3]
135
135
136 for rev, changefiles in changerevgen():
136 for rev, changefiles in changerevgen():
137 matches = filter(matchfn, changefiles)
137 matches = filter(matchfn, changefiles)
138 if matches:
138 if matches:
139 fncache[rev] = matches
139 fncache[rev] = matches
140 wanted[rev] = 1
140 wanted[rev] = 1
141
141
142 def iterate():
142 def iterate():
143 for i in xrange(0, len(revs), window):
143 for i in xrange(0, len(revs), window):
144 yield 'window', revs[0] < revs[-1], revs[-1]
144 yield 'window', revs[0] < revs[-1], revs[-1]
145 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
145 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
146 if rev in wanted]
146 if rev in wanted]
147 srevs = list(nrevs)
147 srevs = list(nrevs)
148 srevs.sort()
148 srevs.sort()
149 for rev in srevs:
149 for rev in srevs:
150 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
150 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
151 yield 'add', rev, fns
151 yield 'add', rev, fns
152 for rev in nrevs:
152 for rev in nrevs:
153 yield 'iter', rev, None
153 yield 'iter', rev, None
154 return iterate(), getchange
154 return iterate(), getchange
155
155
156 revrangesep = ':'
156 revrangesep = ':'
157
157
158 def revrange(ui, repo, revs, revlog=None):
158 def revrange(ui, repo, revs, revlog=None):
159 """Yield revision as strings from a list of revision specifications."""
159 """Yield revision as strings from a list of revision specifications."""
160 if revlog is None:
160 if revlog is None:
161 revlog = repo.changelog
161 revlog = repo.changelog
162 revcount = revlog.count()
162 revcount = revlog.count()
163 def fix(val, defval):
163 def fix(val, defval):
164 if not val:
164 if not val:
165 return defval
165 return defval
166 try:
166 try:
167 num = int(val)
167 num = int(val)
168 if str(num) != val:
168 if str(num) != val:
169 raise ValueError
169 raise ValueError
170 if num < 0: num += revcount
170 if num < 0: num += revcount
171 if num < 0: num = 0
171 if num < 0: num = 0
172 elif num >= revcount:
172 elif num >= revcount:
173 raise ValueError
173 raise ValueError
174 except ValueError:
174 except ValueError:
175 try:
175 try:
176 num = repo.changelog.rev(repo.lookup(val))
176 num = repo.changelog.rev(repo.lookup(val))
177 except KeyError:
177 except KeyError:
178 try:
178 try:
179 num = revlog.rev(revlog.lookup(val))
179 num = revlog.rev(revlog.lookup(val))
180 except KeyError:
180 except KeyError:
181 raise util.Abort('invalid revision identifier %s', val)
181 raise util.Abort('invalid revision identifier %s', val)
182 return num
182 return num
183 seen = {}
183 seen = {}
184 for spec in revs:
184 for spec in revs:
185 if spec.find(revrangesep) >= 0:
185 if spec.find(revrangesep) >= 0:
186 start, end = spec.split(revrangesep, 1)
186 start, end = spec.split(revrangesep, 1)
187 start = fix(start, 0)
187 start = fix(start, 0)
188 end = fix(end, revcount - 1)
188 end = fix(end, revcount - 1)
189 step = start > end and -1 or 1
189 step = start > end and -1 or 1
190 for rev in xrange(start, end+step, step):
190 for rev in xrange(start, end+step, step):
191 if rev in seen: continue
191 if rev in seen: continue
192 seen[rev] = 1
192 seen[rev] = 1
193 yield str(rev)
193 yield str(rev)
194 else:
194 else:
195 rev = fix(spec, None)
195 rev = fix(spec, None)
196 if rev in seen: continue
196 if rev in seen: continue
197 seen[rev] = 1
197 seen[rev] = 1
198 yield str(rev)
198 yield str(rev)
199
199
200 def make_filename(repo, r, pat, node=None,
200 def make_filename(repo, r, pat, node=None,
201 total=None, seqno=None, revwidth=None, pathname=None):
201 total=None, seqno=None, revwidth=None, pathname=None):
202 node_expander = {
202 node_expander = {
203 'H': lambda: hex(node),
203 'H': lambda: hex(node),
204 'R': lambda: str(r.rev(node)),
204 'R': lambda: str(r.rev(node)),
205 'h': lambda: short(node),
205 'h': lambda: short(node),
206 }
206 }
207 expander = {
207 expander = {
208 '%': lambda: '%',
208 '%': lambda: '%',
209 'b': lambda: os.path.basename(repo.root),
209 'b': lambda: os.path.basename(repo.root),
210 }
210 }
211
211
212 try:
212 try:
213 if node:
213 if node:
214 expander.update(node_expander)
214 expander.update(node_expander)
215 if node and revwidth is not None:
215 if node and revwidth is not None:
216 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
216 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
217 if total is not None:
217 if total is not None:
218 expander['N'] = lambda: str(total)
218 expander['N'] = lambda: str(total)
219 if seqno is not None:
219 if seqno is not None:
220 expander['n'] = lambda: str(seqno)
220 expander['n'] = lambda: str(seqno)
221 if total is not None and seqno is not None:
221 if total is not None and seqno is not None:
222 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
222 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
223 if pathname is not None:
223 if pathname is not None:
224 expander['s'] = lambda: os.path.basename(pathname)
224 expander['s'] = lambda: os.path.basename(pathname)
225 expander['d'] = lambda: os.path.dirname(pathname) or '.'
225 expander['d'] = lambda: os.path.dirname(pathname) or '.'
226 expander['p'] = lambda: pathname
226 expander['p'] = lambda: pathname
227
227
228 newname = []
228 newname = []
229 patlen = len(pat)
229 patlen = len(pat)
230 i = 0
230 i = 0
231 while i < patlen:
231 while i < patlen:
232 c = pat[i]
232 c = pat[i]
233 if c == '%':
233 if c == '%':
234 i += 1
234 i += 1
235 c = pat[i]
235 c = pat[i]
236 c = expander[c]()
236 c = expander[c]()
237 newname.append(c)
237 newname.append(c)
238 i += 1
238 i += 1
239 return ''.join(newname)
239 return ''.join(newname)
240 except KeyError, inst:
240 except KeyError, inst:
241 raise util.Abort("invalid format spec '%%%s' in output file name",
241 raise util.Abort("invalid format spec '%%%s' in output file name",
242 inst.args[0])
242 inst.args[0])
243
243
244 def make_file(repo, r, pat, node=None,
244 def make_file(repo, r, pat, node=None,
245 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
245 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
246 if not pat or pat == '-':
246 if not pat or pat == '-':
247 return 'w' in mode and sys.stdout or sys.stdin
247 return 'w' in mode and sys.stdout or sys.stdin
248 if hasattr(pat, 'write') and 'w' in mode:
248 if hasattr(pat, 'write') and 'w' in mode:
249 return pat
249 return pat
250 if hasattr(pat, 'read') and 'r' in mode:
250 if hasattr(pat, 'read') and 'r' in mode:
251 return pat
251 return pat
252 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
252 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
253 pathname),
253 pathname),
254 mode)
254 mode)
255
255
256 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
256 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
257 changes=None, text=False):
257 changes=None, text=False):
258 if not changes:
258 if not changes:
259 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
259 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
260 else:
260 else:
261 (c, a, d, u) = changes
261 (c, a, d, u) = changes
262 if files:
262 if files:
263 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
263 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
264
264
265 if not c and not a and not d:
265 if not c and not a and not d:
266 return
266 return
267
267
268 if node2:
268 if node2:
269 change = repo.changelog.read(node2)
269 change = repo.changelog.read(node2)
270 mmap2 = repo.manifest.read(change[0])
270 mmap2 = repo.manifest.read(change[0])
271 date2 = util.datestr(change[2])
271 date2 = util.datestr(change[2])
272 def read(f):
272 def read(f):
273 return repo.file(f).read(mmap2[f])
273 return repo.file(f).read(mmap2[f])
274 else:
274 else:
275 date2 = util.datestr()
275 date2 = util.datestr()
276 if not node1:
276 if not node1:
277 node1 = repo.dirstate.parents()[0]
277 node1 = repo.dirstate.parents()[0]
278 def read(f):
278 def read(f):
279 return repo.wfile(f).read()
279 return repo.wfile(f).read()
280
280
281 if ui.quiet:
281 if ui.quiet:
282 r = None
282 r = None
283 else:
283 else:
284 hexfunc = ui.verbose and hex or short
284 hexfunc = ui.verbose and hex or short
285 r = [hexfunc(node) for node in [node1, node2] if node]
285 r = [hexfunc(node) for node in [node1, node2] if node]
286
286
287 change = repo.changelog.read(node1)
287 change = repo.changelog.read(node1)
288 mmap = repo.manifest.read(change[0])
288 mmap = repo.manifest.read(change[0])
289 date1 = util.datestr(change[2])
289 date1 = util.datestr(change[2])
290
290
291 for f in c:
291 for f in c:
292 to = None
292 to = None
293 if f in mmap:
293 if f in mmap:
294 to = repo.file(f).read(mmap[f])
294 to = repo.file(f).read(mmap[f])
295 tn = read(f)
295 tn = read(f)
296 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
296 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
297 for f in a:
297 for f in a:
298 to = None
298 to = None
299 tn = read(f)
299 tn = read(f)
300 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
300 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
301 for f in d:
301 for f in d:
302 to = repo.file(f).read(mmap[f])
302 to = repo.file(f).read(mmap[f])
303 tn = None
303 tn = None
304 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
304 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
305
305
306 def trimuser(ui, name, rev, revcache):
306 def trimuser(ui, name, rev, revcache):
307 """trim the name of the user who committed a change"""
307 """trim the name of the user who committed a change"""
308 user = revcache.get(rev)
308 user = revcache.get(rev)
309 if user is None:
309 if user is None:
310 user = revcache[rev] = ui.shortuser(name)
310 user = revcache[rev] = ui.shortuser(name)
311 return user
311 return user
312
312
313 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
313 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
314 """show a single changeset or file revision"""
314 """show a single changeset or file revision"""
315 log = repo.changelog
315 log = repo.changelog
316 if changenode is None:
316 if changenode is None:
317 changenode = log.node(rev)
317 changenode = log.node(rev)
318 elif not rev:
318 elif not rev:
319 rev = log.rev(changenode)
319 rev = log.rev(changenode)
320
320
321 if ui.quiet:
321 if ui.quiet:
322 ui.write("%d:%s\n" % (rev, short(changenode)))
322 ui.write("%d:%s\n" % (rev, short(changenode)))
323 return
323 return
324
324
325 changes = log.read(changenode)
325 changes = log.read(changenode)
326 date = util.datestr(changes[2])
326 date = util.datestr(changes[2])
327
327
328 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
328 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
329 for p in log.parents(changenode)
329 for p in log.parents(changenode)
330 if ui.debugflag or p != nullid]
330 if ui.debugflag or p != nullid]
331 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
331 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
332 parents = []
332 parents = []
333
333
334 if ui.verbose:
334 if ui.verbose:
335 ui.write("changeset: %d:%s\n" % (rev, hex(changenode)))
335 ui.write("changeset: %d:%s\n" % (rev, hex(changenode)))
336 else:
336 else:
337 ui.write("changeset: %d:%s\n" % (rev, short(changenode)))
337 ui.write("changeset: %d:%s\n" % (rev, short(changenode)))
338
338
339 for tag in repo.nodetags(changenode):
339 for tag in repo.nodetags(changenode):
340 ui.status("tag: %s\n" % tag)
340 ui.status("tag: %s\n" % tag)
341 for parent in parents:
341 for parent in parents:
342 ui.write("parent: %d:%s\n" % parent)
342 ui.write("parent: %d:%s\n" % parent)
343
343
344 if brinfo and changenode in brinfo:
344 if brinfo and changenode in brinfo:
345 br = brinfo[changenode]
345 br = brinfo[changenode]
346 ui.write("branch: %s\n" % " ".join(br))
346 ui.write("branch: %s\n" % " ".join(br))
347
347
348 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
348 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
349 hex(changes[0])))
349 hex(changes[0])))
350 ui.status("user: %s\n" % changes[1])
350 ui.status("user: %s\n" % changes[1])
351 ui.status("date: %s\n" % date)
351 ui.status("date: %s\n" % date)
352
352
353 if ui.debugflag:
353 if ui.debugflag:
354 files = repo.changes(log.parents(changenode)[0], changenode)
354 files = repo.changes(log.parents(changenode)[0], changenode)
355 for key, value in zip(["files:", "files+:", "files-:"], files):
355 for key, value in zip(["files:", "files+:", "files-:"], files):
356 if value:
356 if value:
357 ui.note("%-12s %s\n" % (key, " ".join(value)))
357 ui.note("%-12s %s\n" % (key, " ".join(value)))
358 else:
358 else:
359 ui.note("files: %s\n" % " ".join(changes[3]))
359 ui.note("files: %s\n" % " ".join(changes[3]))
360
360
361 description = changes[4].strip()
361 description = changes[4].strip()
362 if description:
362 if description:
363 if ui.verbose:
363 if ui.verbose:
364 ui.status("description:\n")
364 ui.status("description:\n")
365 ui.status(description)
365 ui.status(description)
366 ui.status("\n\n")
366 ui.status("\n\n")
367 else:
367 else:
368 ui.status("summary: %s\n" % description.splitlines()[0])
368 ui.status("summary: %s\n" % description.splitlines()[0])
369 ui.status("\n")
369 ui.status("\n")
370
370
371 def show_version(ui):
371 def show_version(ui):
372 """output version and copyright information"""
372 """output version and copyright information"""
373 ui.write("Mercurial Distributed SCM (version %s)\n"
373 ui.write("Mercurial Distributed SCM (version %s)\n"
374 % version.get_version())
374 % version.get_version())
375 ui.status(
375 ui.status(
376 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
376 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
377 "This is free software; see the source for copying conditions. "
377 "This is free software; see the source for copying conditions. "
378 "There is NO\nwarranty; "
378 "There is NO\nwarranty; "
379 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
379 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
380 )
380 )
381
381
382 def help_(ui, cmd=None, with_version=False):
382 def help_(ui, cmd=None, with_version=False):
383 """show help for a given command or all commands"""
383 """show help for a given command or all commands"""
384 option_lists = []
384 option_lists = []
385 if cmd and cmd != 'shortlist':
385 if cmd and cmd != 'shortlist':
386 if with_version:
386 if with_version:
387 show_version(ui)
387 show_version(ui)
388 ui.write('\n')
388 ui.write('\n')
389 key, i = find(cmd)
389 key, i = find(cmd)
390 # synopsis
390 # synopsis
391 ui.write("%s\n\n" % i[2])
391 ui.write("%s\n\n" % i[2])
392
392
393 # description
393 # description
394 doc = i[0].__doc__
394 doc = i[0].__doc__
395 if ui.quiet:
395 if ui.quiet:
396 doc = doc.splitlines(0)[0]
396 doc = doc.splitlines(0)[0]
397 ui.write("%s\n" % doc.rstrip())
397 ui.write("%s\n" % doc.rstrip())
398
398
399 if not ui.quiet:
399 if not ui.quiet:
400 # aliases
400 # aliases
401 aliases = ', '.join(key.split('|')[1:])
401 aliases = ', '.join(key.split('|')[1:])
402 if aliases:
402 if aliases:
403 ui.write("\naliases: %s\n" % aliases)
403 ui.write("\naliases: %s\n" % aliases)
404
404
405 # options
405 # options
406 if i[1]:
406 if i[1]:
407 option_lists.append(("options", i[1]))
407 option_lists.append(("options", i[1]))
408
408
409 else:
409 else:
410 # program name
410 # program name
411 if ui.verbose or with_version:
411 if ui.verbose or with_version:
412 show_version(ui)
412 show_version(ui)
413 else:
413 else:
414 ui.status("Mercurial Distributed SCM\n")
414 ui.status("Mercurial Distributed SCM\n")
415 ui.status('\n')
415 ui.status('\n')
416
416
417 # list of commands
417 # list of commands
418 if cmd == "shortlist":
418 if cmd == "shortlist":
419 ui.status('basic commands (use "hg help" '
419 ui.status('basic commands (use "hg help" '
420 'for the full list or option "-v" for details):\n\n')
420 'for the full list or option "-v" for details):\n\n')
421 elif ui.verbose:
421 elif ui.verbose:
422 ui.status('list of commands:\n\n')
422 ui.status('list of commands:\n\n')
423 else:
423 else:
424 ui.status('list of commands (use "hg help -v" '
424 ui.status('list of commands (use "hg help -v" '
425 'to show aliases and global options):\n\n')
425 'to show aliases and global options):\n\n')
426
426
427 h = {}
427 h = {}
428 cmds = {}
428 cmds = {}
429 for c, e in table.items():
429 for c, e in table.items():
430 f = c.split("|")[0]
430 f = c.split("|")[0]
431 if cmd == "shortlist" and not f.startswith("^"):
431 if cmd == "shortlist" and not f.startswith("^"):
432 continue
432 continue
433 f = f.lstrip("^")
433 f = f.lstrip("^")
434 if not ui.debugflag and f.startswith("debug"):
434 if not ui.debugflag and f.startswith("debug"):
435 continue
435 continue
436 d = ""
436 d = ""
437 if e[0].__doc__:
437 if e[0].__doc__:
438 d = e[0].__doc__.splitlines(0)[0].rstrip()
438 d = e[0].__doc__.splitlines(0)[0].rstrip()
439 h[f] = d
439 h[f] = d
440 cmds[f]=c.lstrip("^")
440 cmds[f]=c.lstrip("^")
441
441
442 fns = h.keys()
442 fns = h.keys()
443 fns.sort()
443 fns.sort()
444 m = max(map(len, fns))
444 m = max(map(len, fns))
445 for f in fns:
445 for f in fns:
446 if ui.verbose:
446 if ui.verbose:
447 commands = cmds[f].replace("|",", ")
447 commands = cmds[f].replace("|",", ")
448 ui.write(" %s:\n %s\n"%(commands,h[f]))
448 ui.write(" %s:\n %s\n"%(commands,h[f]))
449 else:
449 else:
450 ui.write(' %-*s %s\n' % (m, f, h[f]))
450 ui.write(' %-*s %s\n' % (m, f, h[f]))
451
451
452 # global options
452 # global options
453 if ui.verbose:
453 if ui.verbose:
454 option_lists.append(("global options", globalopts))
454 option_lists.append(("global options", globalopts))
455
455
456 # list all option lists
456 # list all option lists
457 opt_output = []
457 opt_output = []
458 for title, options in option_lists:
458 for title, options in option_lists:
459 opt_output.append(("\n%s:\n" % title, None))
459 opt_output.append(("\n%s:\n" % title, None))
460 for shortopt, longopt, default, desc in options:
460 for shortopt, longopt, default, desc in options:
461 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
461 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
462 longopt and " --%s" % longopt),
462 longopt and " --%s" % longopt),
463 "%s%s" % (desc,
463 "%s%s" % (desc,
464 default and " (default: %s)" % default
464 default and " (default: %s)" % default
465 or "")))
465 or "")))
466
466
467 if opt_output:
467 if opt_output:
468 opts_len = max([len(line[0]) for line in opt_output if line[1]])
468 opts_len = max([len(line[0]) for line in opt_output if line[1]])
469 for first, second in opt_output:
469 for first, second in opt_output:
470 if second:
470 if second:
471 ui.write(" %-*s %s\n" % (opts_len, first, second))
471 ui.write(" %-*s %s\n" % (opts_len, first, second))
472 else:
472 else:
473 ui.write("%s\n" % first)
473 ui.write("%s\n" % first)
474
474
475 # Commands start here, listed alphabetically
475 # Commands start here, listed alphabetically
476
476
477 def add(ui, repo, *pats, **opts):
477 def add(ui, repo, *pats, **opts):
478 '''add the specified files on the next commit'''
478 '''add the specified files on the next commit'''
479 names = []
479 names = []
480 for src, abs, rel, exact in walk(repo, pats, opts):
480 for src, abs, rel, exact in walk(repo, pats, opts):
481 if exact:
481 if exact:
482 if ui.verbose: ui.status('adding %s\n' % rel)
482 if ui.verbose: ui.status('adding %s\n' % rel)
483 names.append(abs)
483 names.append(abs)
484 elif repo.dirstate.state(abs) == '?':
484 elif repo.dirstate.state(abs) == '?':
485 ui.status('adding %s\n' % rel)
485 ui.status('adding %s\n' % rel)
486 names.append(abs)
486 names.append(abs)
487 repo.add(names)
487 repo.add(names)
488
488
489 def addremove(ui, repo, *pats, **opts):
489 def addremove(ui, repo, *pats, **opts):
490 """add all new files, delete all missing files"""
490 """add all new files, delete all missing files"""
491 add, remove = [], []
491 add, remove = [], []
492 for src, abs, rel, exact in walk(repo, pats, opts):
492 for src, abs, rel, exact in walk(repo, pats, opts):
493 if src == 'f' and repo.dirstate.state(abs) == '?':
493 if src == 'f' and repo.dirstate.state(abs) == '?':
494 add.append(abs)
494 add.append(abs)
495 if ui.verbose or not exact:
495 if ui.verbose or not exact:
496 ui.status('adding ', rel, '\n')
496 ui.status('adding ', rel, '\n')
497 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
497 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
498 remove.append(abs)
498 remove.append(abs)
499 if ui.verbose or not exact:
499 if ui.verbose or not exact:
500 ui.status('removing ', rel, '\n')
500 ui.status('removing ', rel, '\n')
501 repo.add(add)
501 repo.add(add)
502 repo.remove(remove)
502 repo.remove(remove)
503
503
504 def annotate(ui, repo, *pats, **opts):
504 def annotate(ui, repo, *pats, **opts):
505 """show changeset information per file line"""
505 """show changeset information per file line"""
506 def getnode(rev):
506 def getnode(rev):
507 return short(repo.changelog.node(rev))
507 return short(repo.changelog.node(rev))
508
508
509 ucache = {}
509 ucache = {}
510 def getname(rev):
510 def getname(rev):
511 cl = repo.changelog.read(repo.changelog.node(rev))
511 cl = repo.changelog.read(repo.changelog.node(rev))
512 return trimuser(ui, cl[1], rev, ucache)
512 return trimuser(ui, cl[1], rev, ucache)
513
513
514 if not pats:
514 if not pats:
515 raise util.Abort('at least one file name or pattern required')
515 raise util.Abort('at least one file name or pattern required')
516
516
517 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
517 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
518 if not opts['user'] and not opts['changeset']:
518 if not opts['user'] and not opts['changeset']:
519 opts['number'] = 1
519 opts['number'] = 1
520
520
521 if opts['rev']:
521 if opts['rev']:
522 node = repo.changelog.lookup(opts['rev'])
522 node = repo.changelog.lookup(opts['rev'])
523 else:
523 else:
524 node = repo.dirstate.parents()[0]
524 node = repo.dirstate.parents()[0]
525 change = repo.changelog.read(node)
525 change = repo.changelog.read(node)
526 mmap = repo.manifest.read(change[0])
526 mmap = repo.manifest.read(change[0])
527
527
528 for src, abs, rel, exact in walk(repo, pats, opts):
528 for src, abs, rel, exact in walk(repo, pats, opts):
529 if abs not in mmap:
529 if abs not in mmap:
530 ui.warn("warning: %s is not in the repository!\n" % rel)
530 ui.warn("warning: %s is not in the repository!\n" % rel)
531 continue
531 continue
532
532
533 f = repo.file(abs)
533 f = repo.file(abs)
534 if not opts['text'] and util.binary(f.read(mmap[abs])):
534 if not opts['text'] and util.binary(f.read(mmap[abs])):
535 ui.write("%s: binary file\n" % rel)
535 ui.write("%s: binary file\n" % rel)
536 continue
536 continue
537
537
538 lines = f.annotate(mmap[abs])
538 lines = f.annotate(mmap[abs])
539 pieces = []
539 pieces = []
540
540
541 for o, f in opmap:
541 for o, f in opmap:
542 if opts[o]:
542 if opts[o]:
543 l = [f(n) for n, dummy in lines]
543 l = [f(n) for n, dummy in lines]
544 if l:
544 if l:
545 m = max(map(len, l))
545 m = max(map(len, l))
546 pieces.append(["%*s" % (m, x) for x in l])
546 pieces.append(["%*s" % (m, x) for x in l])
547
547
548 if pieces:
548 if pieces:
549 for p, l in zip(zip(*pieces), lines):
549 for p, l in zip(zip(*pieces), lines):
550 ui.write("%s: %s" % (" ".join(p), l[1]))
550 ui.write("%s: %s" % (" ".join(p), l[1]))
551
551
552 def bundle(ui, repo, fname, dest="default-push", **opts):
552 def bundle(ui, repo, fname, dest="default-push", **opts):
553 """create a changegroup file"""
553 """create a changegroup file"""
554 f = open(fname, "wb")
554 f = open(fname, "wb")
555 dest = ui.expandpath(dest)
555 dest = ui.expandpath(dest)
556 other = hg.repository(ui, dest)
556 other = hg.repository(ui, dest)
557 o = repo.findoutgoing(other)
557 o = repo.findoutgoing(other)
558 cg = repo.changegroup(o)
558 cg = repo.changegroup(o)
559
559
560 try:
560 try:
561 f.write("HG10")
561 f.write("HG10")
562 z = bz2.BZ2Compressor(9)
562 z = bz2.BZ2Compressor(9)
563 while 1:
563 while 1:
564 chunk = cg.read(4096)
564 chunk = cg.read(4096)
565 if not chunk:
565 if not chunk:
566 break
566 break
567 f.write(z.compress(chunk))
567 f.write(z.compress(chunk))
568 f.write(z.flush())
568 f.write(z.flush())
569 except:
569 except:
570 os.unlink(fname)
570 os.unlink(fname)
571 raise
571 raise
572
572
573 def cat(ui, repo, file1, *pats, **opts):
573 def cat(ui, repo, file1, *pats, **opts):
574 """output the latest or given revisions of files"""
574 """output the latest or given revisions of files"""
575 mf = {}
575 mf = {}
576 if opts['rev']:
576 if opts['rev']:
577 change = repo.changelog.read(repo.lookup(opts['rev']))
577 change = repo.changelog.read(repo.lookup(opts['rev']))
578 mf = repo.manifest.read(change[0])
578 mf = repo.manifest.read(change[0])
579 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
579 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
580 r = repo.file(abs)
580 r = repo.file(abs)
581 if opts['rev']:
581 if opts['rev']:
582 try:
582 try:
583 n = mf[abs]
583 n = mf[abs]
584 except (hg.RepoError, KeyError):
584 except (hg.RepoError, KeyError):
585 try:
585 try:
586 n = r.lookup(rev)
586 n = r.lookup(rev)
587 except KeyError, inst:
587 except KeyError, inst:
588 raise util.Abort('cannot find file %s in rev %s', rel, rev)
588 raise util.Abort('cannot find file %s in rev %s', rel, rev)
589 else:
589 else:
590 n = r.tip()
590 n = r.tip()
591 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
591 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
592 fp.write(r.read(n))
592 fp.write(r.read(n))
593
593
594 def clone(ui, source, dest=None, **opts):
594 def clone(ui, source, dest=None, **opts):
595 """make a copy of an existing repository"""
595 """make a copy of an existing repository"""
596 if dest is None:
596 if dest is None:
597 dest = os.path.basename(os.path.normpath(source))
597 dest = os.path.basename(os.path.normpath(source))
598
598
599 if os.path.exists(dest):
599 if os.path.exists(dest):
600 raise util.Abort("destination '%s' already exists", dest)
600 raise util.Abort("destination '%s' already exists", dest)
601
601
602 dest = os.path.realpath(dest)
602 dest = os.path.realpath(dest)
603
603
604 class Dircleanup:
604 class Dircleanup:
605 def __init__(self, dir_):
605 def __init__(self, dir_):
606 self.rmtree = shutil.rmtree
606 self.rmtree = shutil.rmtree
607 self.dir_ = dir_
607 self.dir_ = dir_
608 os.mkdir(dir_)
608 os.mkdir(dir_)
609 def close(self):
609 def close(self):
610 self.dir_ = None
610 self.dir_ = None
611 def __del__(self):
611 def __del__(self):
612 if self.dir_:
612 if self.dir_:
613 self.rmtree(self.dir_, True)
613 self.rmtree(self.dir_, True)
614
614
615 if opts['ssh']:
615 if opts['ssh']:
616 ui.setconfig("ui", "ssh", opts['ssh'])
616 ui.setconfig("ui", "ssh", opts['ssh'])
617 if opts['remotecmd']:
617 if opts['remotecmd']:
618 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
618 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
619
619
620 if not os.path.exists(source):
620 if not os.path.exists(source):
621 source = ui.expandpath(source)
621 source = ui.expandpath(source)
622
622
623 d = Dircleanup(dest)
623 d = Dircleanup(dest)
624 abspath = source
624 abspath = source
625 other = hg.repository(ui, source)
625 other = hg.repository(ui, source)
626
626
627 copy = False
627 copy = False
628 if other.dev() != -1:
628 if other.dev() != -1:
629 abspath = os.path.abspath(source)
629 abspath = os.path.abspath(source)
630 if not opts['pull']:
630 if not opts['pull'] and not opts['rev']:
631 copy = True
631 copy = True
632
632
633 if copy:
633 if copy:
634 try:
634 try:
635 # we use a lock here because if we race with commit, we
635 # we use a lock here because if we race with commit, we
636 # can end up with extra data in the cloned revlogs that's
636 # can end up with extra data in the cloned revlogs that's
637 # not pointed to by changesets, thus causing verify to
637 # not pointed to by changesets, thus causing verify to
638 # fail
638 # fail
639 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
639 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
640 except OSError:
640 except OSError:
641 copy = False
641 copy = False
642
642
643 if copy:
643 if copy:
644 # we lock here to avoid premature writing to the target
644 # we lock here to avoid premature writing to the target
645 os.mkdir(os.path.join(dest, ".hg"))
645 os.mkdir(os.path.join(dest, ".hg"))
646 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
646 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
647
647
648 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
648 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
649 for f in files.split():
649 for f in files.split():
650 src = os.path.join(source, ".hg", f)
650 src = os.path.join(source, ".hg", f)
651 dst = os.path.join(dest, ".hg", f)
651 dst = os.path.join(dest, ".hg", f)
652 util.copyfiles(src, dst)
652 util.copyfiles(src, dst)
653
653
654 repo = hg.repository(ui, dest)
654 repo = hg.repository(ui, dest)
655
655
656 else:
656 else:
657 repo = hg.repository(ui, dest, create=1)
657 repo = hg.repository(ui, dest, create=1)
658 repo.pull(other)
658 rev = None
659 if opts['rev']:
660 rev = [other.lookup(opts['rev'])]
661 repo.pull(other, heads = rev)
659
662
660 f = repo.opener("hgrc", "w", text=True)
663 f = repo.opener("hgrc", "w", text=True)
661 f.write("[paths]\n")
664 f.write("[paths]\n")
662 f.write("default = %s\n" % abspath)
665 f.write("default = %s\n" % abspath)
663
666
664 if not opts['noupdate']:
667 if not opts['noupdate']:
665 update(ui, repo)
668 update(ui, repo)
666
669
667 d.close()
670 d.close()
668
671
669 def commit(ui, repo, *pats, **opts):
672 def commit(ui, repo, *pats, **opts):
670 """commit the specified files or all outstanding changes"""
673 """commit the specified files or all outstanding changes"""
671 if opts['text']:
674 if opts['text']:
672 ui.warn("Warning: -t and --text is deprecated,"
675 ui.warn("Warning: -t and --text is deprecated,"
673 " please use -m or --message instead.\n")
676 " please use -m or --message instead.\n")
674 message = opts['message'] or opts['text']
677 message = opts['message'] or opts['text']
675 logfile = opts['logfile']
678 logfile = opts['logfile']
676
679
677 if message and logfile:
680 if message and logfile:
678 raise util.Abort('options --message and --logfile are mutually '
681 raise util.Abort('options --message and --logfile are mutually '
679 'exclusive')
682 'exclusive')
680 if not message and logfile:
683 if not message and logfile:
681 try:
684 try:
682 if logfile == '-':
685 if logfile == '-':
683 message = sys.stdin.read()
686 message = sys.stdin.read()
684 else:
687 else:
685 message = open(logfile).read()
688 message = open(logfile).read()
686 except IOError, inst:
689 except IOError, inst:
687 raise util.Abort("can't read commit message '%s': %s" %
690 raise util.Abort("can't read commit message '%s': %s" %
688 (logfile, inst.strerror))
691 (logfile, inst.strerror))
689
692
690 if opts['addremove']:
693 if opts['addremove']:
691 addremove(ui, repo, *pats, **opts)
694 addremove(ui, repo, *pats, **opts)
692 cwd = repo.getcwd()
695 cwd = repo.getcwd()
693 if not pats and cwd:
696 if not pats and cwd:
694 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
697 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
695 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
698 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
696 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
699 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
697 pats, opts)
700 pats, opts)
698 if pats:
701 if pats:
699 c, a, d, u = repo.changes(files=fns, match=match)
702 c, a, d, u = repo.changes(files=fns, match=match)
700 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
703 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
701 else:
704 else:
702 files = []
705 files = []
703 try:
706 try:
704 repo.commit(files, message, opts['user'], opts['date'], match)
707 repo.commit(files, message, opts['user'], opts['date'], match)
705 except ValueError, inst:
708 except ValueError, inst:
706 raise util.Abort(str(inst))
709 raise util.Abort(str(inst))
707
710
708 def docopy(ui, repo, pats, opts):
711 def docopy(ui, repo, pats, opts):
709 if not pats:
712 if not pats:
710 raise util.Abort('no source or destination specified')
713 raise util.Abort('no source or destination specified')
711 elif len(pats) == 1:
714 elif len(pats) == 1:
712 raise util.Abort('no destination specified')
715 raise util.Abort('no destination specified')
713 pats = list(pats)
716 pats = list(pats)
714 dest = pats.pop()
717 dest = pats.pop()
715 sources = []
718 sources = []
716
719
717 def okaytocopy(abs, rel, exact):
720 def okaytocopy(abs, rel, exact):
718 reasons = {'?': 'is not managed',
721 reasons = {'?': 'is not managed',
719 'a': 'has been marked for add'}
722 'a': 'has been marked for add'}
720 reason = reasons.get(repo.dirstate.state(abs))
723 reason = reasons.get(repo.dirstate.state(abs))
721 if reason:
724 if reason:
722 if exact: ui.warn('%s: not copying - file %s\n' % (rel, reason))
725 if exact: ui.warn('%s: not copying - file %s\n' % (rel, reason))
723 else:
726 else:
724 return True
727 return True
725
728
726 for src, abs, rel, exact in walk(repo, pats, opts):
729 for src, abs, rel, exact in walk(repo, pats, opts):
727 if okaytocopy(abs, rel, exact):
730 if okaytocopy(abs, rel, exact):
728 sources.append((abs, rel, exact))
731 sources.append((abs, rel, exact))
729 if not sources:
732 if not sources:
730 raise util.Abort('no files to copy')
733 raise util.Abort('no files to copy')
731
734
732 cwd = repo.getcwd()
735 cwd = repo.getcwd()
733 absdest = util.canonpath(repo.root, cwd, dest)
736 absdest = util.canonpath(repo.root, cwd, dest)
734 reldest = util.pathto(cwd, absdest)
737 reldest = util.pathto(cwd, absdest)
735 if os.path.exists(reldest):
738 if os.path.exists(reldest):
736 destisfile = not os.path.isdir(reldest)
739 destisfile = not os.path.isdir(reldest)
737 else:
740 else:
738 destisfile = len(sources) == 1 or repo.dirstate.state(absdest) != '?'
741 destisfile = len(sources) == 1 or repo.dirstate.state(absdest) != '?'
739
742
740 if destisfile:
743 if destisfile:
741 if opts['parents']:
744 if opts['parents']:
742 raise util.Abort('with --parents, destination must be a directory')
745 raise util.Abort('with --parents, destination must be a directory')
743 elif len(sources) > 1:
746 elif len(sources) > 1:
744 raise util.Abort('with multiple sources, destination must be a '
747 raise util.Abort('with multiple sources, destination must be a '
745 'directory')
748 'directory')
746 errs, copied = 0, []
749 errs, copied = 0, []
747 for abs, rel, exact in sources:
750 for abs, rel, exact in sources:
748 if opts['parents']:
751 if opts['parents']:
749 mydest = os.path.join(dest, rel)
752 mydest = os.path.join(dest, rel)
750 elif destisfile:
753 elif destisfile:
751 mydest = reldest
754 mydest = reldest
752 else:
755 else:
753 mydest = os.path.join(dest, os.path.basename(rel))
756 mydest = os.path.join(dest, os.path.basename(rel))
754 myabsdest = util.canonpath(repo.root, cwd, mydest)
757 myabsdest = util.canonpath(repo.root, cwd, mydest)
755 myreldest = util.pathto(cwd, myabsdest)
758 myreldest = util.pathto(cwd, myabsdest)
756 if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
759 if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
757 ui.warn('%s: not overwriting - file already managed\n' % myreldest)
760 ui.warn('%s: not overwriting - file already managed\n' % myreldest)
758 continue
761 continue
759 mydestdir = os.path.dirname(myreldest) or '.'
762 mydestdir = os.path.dirname(myreldest) or '.'
760 if not opts['after']:
763 if not opts['after']:
761 try:
764 try:
762 if opts['parents']: os.makedirs(mydestdir)
765 if opts['parents']: os.makedirs(mydestdir)
763 elif not destisfile: os.mkdir(mydestdir)
766 elif not destisfile: os.mkdir(mydestdir)
764 except OSError, inst:
767 except OSError, inst:
765 if inst.errno != errno.EEXIST: raise
768 if inst.errno != errno.EEXIST: raise
766 if ui.verbose or not exact:
769 if ui.verbose or not exact:
767 ui.status('copying %s to %s\n' % (rel, myreldest))
770 ui.status('copying %s to %s\n' % (rel, myreldest))
768 if not opts['after']:
771 if not opts['after']:
769 try:
772 try:
770 shutil.copyfile(rel, myreldest)
773 shutil.copyfile(rel, myreldest)
771 shutil.copymode(rel, myreldest)
774 shutil.copymode(rel, myreldest)
772 except shutil.Error, inst:
775 except shutil.Error, inst:
773 raise util.Abort(str(inst))
776 raise util.Abort(str(inst))
774 except IOError, inst:
777 except IOError, inst:
775 if inst.errno == errno.ENOENT:
778 if inst.errno == errno.ENOENT:
776 ui.warn('%s: deleted in working copy\n' % rel)
779 ui.warn('%s: deleted in working copy\n' % rel)
777 else:
780 else:
778 ui.warn('%s: cannot copy - %s\n' % (rel, inst.strerror))
781 ui.warn('%s: cannot copy - %s\n' % (rel, inst.strerror))
779 errs += 1
782 errs += 1
780 continue
783 continue
781 repo.copy(abs, myabsdest)
784 repo.copy(abs, myabsdest)
782 copied.append((abs, rel, exact))
785 copied.append((abs, rel, exact))
783 if errs:
786 if errs:
784 ui.warn('(consider using --after)\n')
787 ui.warn('(consider using --after)\n')
785 return errs, copied
788 return errs, copied
786
789
787 def copy(ui, repo, *pats, **opts):
790 def copy(ui, repo, *pats, **opts):
788 """mark files as copied for the next commit"""
791 """mark files as copied for the next commit"""
789 errs, copied = docopy(ui, repo, pats, opts)
792 errs, copied = docopy(ui, repo, pats, opts)
790 return errs
793 return errs
791
794
792 def debugancestor(ui, index, rev1, rev2):
795 def debugancestor(ui, index, rev1, rev2):
793 """find the ancestor revision of two revisions in a given index"""
796 """find the ancestor revision of two revisions in a given index"""
794 r = revlog.revlog(file, index, "")
797 r = revlog.revlog(file, index, "")
795 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
798 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
796 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
799 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
797
800
798 def debugcheckstate(ui, repo):
801 def debugcheckstate(ui, repo):
799 """validate the correctness of the current dirstate"""
802 """validate the correctness of the current dirstate"""
800 parent1, parent2 = repo.dirstate.parents()
803 parent1, parent2 = repo.dirstate.parents()
801 repo.dirstate.read()
804 repo.dirstate.read()
802 dc = repo.dirstate.map
805 dc = repo.dirstate.map
803 keys = dc.keys()
806 keys = dc.keys()
804 keys.sort()
807 keys.sort()
805 m1n = repo.changelog.read(parent1)[0]
808 m1n = repo.changelog.read(parent1)[0]
806 m2n = repo.changelog.read(parent2)[0]
809 m2n = repo.changelog.read(parent2)[0]
807 m1 = repo.manifest.read(m1n)
810 m1 = repo.manifest.read(m1n)
808 m2 = repo.manifest.read(m2n)
811 m2 = repo.manifest.read(m2n)
809 errors = 0
812 errors = 0
810 for f in dc:
813 for f in dc:
811 state = repo.dirstate.state(f)
814 state = repo.dirstate.state(f)
812 if state in "nr" and f not in m1:
815 if state in "nr" and f not in m1:
813 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
816 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
814 errors += 1
817 errors += 1
815 if state in "a" and f in m1:
818 if state in "a" and f in m1:
816 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
819 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
817 errors += 1
820 errors += 1
818 if state in "m" and f not in m1 and f not in m2:
821 if state in "m" and f not in m1 and f not in m2:
819 ui.warn("%s in state %s, but not in either manifest\n" %
822 ui.warn("%s in state %s, but not in either manifest\n" %
820 (f, state))
823 (f, state))
821 errors += 1
824 errors += 1
822 for f in m1:
825 for f in m1:
823 state = repo.dirstate.state(f)
826 state = repo.dirstate.state(f)
824 if state not in "nrm":
827 if state not in "nrm":
825 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
828 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
826 errors += 1
829 errors += 1
827 if errors:
830 if errors:
828 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
831 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
829
832
830 def debugconfig(ui):
833 def debugconfig(ui):
831 """show combined config settings from all hgrc files"""
834 """show combined config settings from all hgrc files"""
832 try:
835 try:
833 repo = hg.repository(ui)
836 repo = hg.repository(ui)
834 except hg.RepoError:
837 except hg.RepoError:
835 pass
838 pass
836 for section, name, value in ui.walkconfig():
839 for section, name, value in ui.walkconfig():
837 ui.write('%s.%s=%s\n' % (section, name, value))
840 ui.write('%s.%s=%s\n' % (section, name, value))
838
841
839 def debugstate(ui, repo):
842 def debugstate(ui, repo):
840 """show the contents of the current dirstate"""
843 """show the contents of the current dirstate"""
841 repo.dirstate.read()
844 repo.dirstate.read()
842 dc = repo.dirstate.map
845 dc = repo.dirstate.map
843 keys = dc.keys()
846 keys = dc.keys()
844 keys.sort()
847 keys.sort()
845 for file_ in keys:
848 for file_ in keys:
846 ui.write("%c %3o %10d %s %s\n"
849 ui.write("%c %3o %10d %s %s\n"
847 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
850 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
848 time.strftime("%x %X",
851 time.strftime("%x %X",
849 time.localtime(dc[file_][3])), file_))
852 time.localtime(dc[file_][3])), file_))
850 for f in repo.dirstate.copies:
853 for f in repo.dirstate.copies:
851 ui.write("copy: %s -> %s\n" % (repo.dirstate.copies[f], f))
854 ui.write("copy: %s -> %s\n" % (repo.dirstate.copies[f], f))
852
855
853 def debugdata(ui, file_, rev):
856 def debugdata(ui, file_, rev):
854 """dump the contents of an data file revision"""
857 """dump the contents of an data file revision"""
855 r = revlog.revlog(file, file_[:-2] + ".i", file_)
858 r = revlog.revlog(file, file_[:-2] + ".i", file_)
856 try:
859 try:
857 ui.write(r.revision(r.lookup(rev)))
860 ui.write(r.revision(r.lookup(rev)))
858 except KeyError:
861 except KeyError:
859 raise util.Abort('invalid revision identifier %s', rev)
862 raise util.Abort('invalid revision identifier %s', rev)
860
863
861 def debugindex(ui, file_):
864 def debugindex(ui, file_):
862 """dump the contents of an index file"""
865 """dump the contents of an index file"""
863 r = revlog.revlog(file, file_, "")
866 r = revlog.revlog(file, file_, "")
864 ui.write(" rev offset length base linkrev" +
867 ui.write(" rev offset length base linkrev" +
865 " nodeid p1 p2\n")
868 " nodeid p1 p2\n")
866 for i in range(r.count()):
869 for i in range(r.count()):
867 e = r.index[i]
870 e = r.index[i]
868 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
871 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
869 i, e[0], e[1], e[2], e[3],
872 i, e[0], e[1], e[2], e[3],
870 short(e[6]), short(e[4]), short(e[5])))
873 short(e[6]), short(e[4]), short(e[5])))
871
874
872 def debugindexdot(ui, file_):
875 def debugindexdot(ui, file_):
873 """dump an index DAG as a .dot file"""
876 """dump an index DAG as a .dot file"""
874 r = revlog.revlog(file, file_, "")
877 r = revlog.revlog(file, file_, "")
875 ui.write("digraph G {\n")
878 ui.write("digraph G {\n")
876 for i in range(r.count()):
879 for i in range(r.count()):
877 e = r.index[i]
880 e = r.index[i]
878 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
881 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
879 if e[5] != nullid:
882 if e[5] != nullid:
880 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
883 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
881 ui.write("}\n")
884 ui.write("}\n")
882
885
883 def debugrename(ui, repo, file, rev=None):
886 def debugrename(ui, repo, file, rev=None):
884 """dump rename information"""
887 """dump rename information"""
885 r = repo.file(relpath(repo, [file])[0])
888 r = repo.file(relpath(repo, [file])[0])
886 if rev:
889 if rev:
887 try:
890 try:
888 # assume all revision numbers are for changesets
891 # assume all revision numbers are for changesets
889 n = repo.lookup(rev)
892 n = repo.lookup(rev)
890 change = repo.changelog.read(n)
893 change = repo.changelog.read(n)
891 m = repo.manifest.read(change[0])
894 m = repo.manifest.read(change[0])
892 n = m[relpath(repo, [file])[0]]
895 n = m[relpath(repo, [file])[0]]
893 except hg.RepoError, KeyError:
896 except hg.RepoError, KeyError:
894 n = r.lookup(rev)
897 n = r.lookup(rev)
895 else:
898 else:
896 n = r.tip()
899 n = r.tip()
897 m = r.renamed(n)
900 m = r.renamed(n)
898 if m:
901 if m:
899 ui.write("renamed from %s:%s\n" % (m[0], hex(m[1])))
902 ui.write("renamed from %s:%s\n" % (m[0], hex(m[1])))
900 else:
903 else:
901 ui.write("not renamed\n")
904 ui.write("not renamed\n")
902
905
903 def debugwalk(ui, repo, *pats, **opts):
906 def debugwalk(ui, repo, *pats, **opts):
904 """show how files match on given patterns"""
907 """show how files match on given patterns"""
905 items = list(walk(repo, pats, opts))
908 items = list(walk(repo, pats, opts))
906 if not items:
909 if not items:
907 return
910 return
908 fmt = '%%s %%-%ds %%-%ds %%s' % (
911 fmt = '%%s %%-%ds %%-%ds %%s' % (
909 max([len(abs) for (src, abs, rel, exact) in items]),
912 max([len(abs) for (src, abs, rel, exact) in items]),
910 max([len(rel) for (src, abs, rel, exact) in items]))
913 max([len(rel) for (src, abs, rel, exact) in items]))
911 for src, abs, rel, exact in items:
914 for src, abs, rel, exact in items:
912 line = fmt % (src, abs, rel, exact and 'exact' or '')
915 line = fmt % (src, abs, rel, exact and 'exact' or '')
913 ui.write("%s\n" % line.rstrip())
916 ui.write("%s\n" % line.rstrip())
914
917
915 def diff(ui, repo, *pats, **opts):
918 def diff(ui, repo, *pats, **opts):
916 """diff working directory (or selected files)"""
919 """diff working directory (or selected files)"""
917 node1, node2 = None, None
920 node1, node2 = None, None
918 revs = [repo.lookup(x) for x in opts['rev']]
921 revs = [repo.lookup(x) for x in opts['rev']]
919
922
920 if len(revs) > 0:
923 if len(revs) > 0:
921 node1 = revs[0]
924 node1 = revs[0]
922 if len(revs) > 1:
925 if len(revs) > 1:
923 node2 = revs[1]
926 node2 = revs[1]
924 if len(revs) > 2:
927 if len(revs) > 2:
925 raise util.Abort("too many revisions to diff")
928 raise util.Abort("too many revisions to diff")
926
929
927 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
930 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
928
931
929 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
932 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
930 text=opts['text'])
933 text=opts['text'])
931
934
932 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
935 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
933 node = repo.lookup(changeset)
936 node = repo.lookup(changeset)
934 prev, other = repo.changelog.parents(node)
937 prev, other = repo.changelog.parents(node)
935 change = repo.changelog.read(node)
938 change = repo.changelog.read(node)
936
939
937 fp = make_file(repo, repo.changelog, opts['output'],
940 fp = make_file(repo, repo.changelog, opts['output'],
938 node=node, total=total, seqno=seqno,
941 node=node, total=total, seqno=seqno,
939 revwidth=revwidth)
942 revwidth=revwidth)
940 if fp != sys.stdout:
943 if fp != sys.stdout:
941 ui.note("%s\n" % fp.name)
944 ui.note("%s\n" % fp.name)
942
945
943 fp.write("# HG changeset patch\n")
946 fp.write("# HG changeset patch\n")
944 fp.write("# User %s\n" % change[1])
947 fp.write("# User %s\n" % change[1])
945 fp.write("# Node ID %s\n" % hex(node))
948 fp.write("# Node ID %s\n" % hex(node))
946 fp.write("# Parent %s\n" % hex(prev))
949 fp.write("# Parent %s\n" % hex(prev))
947 if other != nullid:
950 if other != nullid:
948 fp.write("# Parent %s\n" % hex(other))
951 fp.write("# Parent %s\n" % hex(other))
949 fp.write(change[4].rstrip())
952 fp.write(change[4].rstrip())
950 fp.write("\n\n")
953 fp.write("\n\n")
951
954
952 dodiff(fp, ui, repo, prev, node, text=opts['text'])
955 dodiff(fp, ui, repo, prev, node, text=opts['text'])
953 if fp != sys.stdout:
956 if fp != sys.stdout:
954 fp.close()
957 fp.close()
955
958
956 def export(ui, repo, *changesets, **opts):
959 def export(ui, repo, *changesets, **opts):
957 """dump the header and diffs for one or more changesets"""
960 """dump the header and diffs for one or more changesets"""
958 if not changesets:
961 if not changesets:
959 raise util.Abort("export requires at least one changeset")
962 raise util.Abort("export requires at least one changeset")
960 seqno = 0
963 seqno = 0
961 revs = list(revrange(ui, repo, changesets))
964 revs = list(revrange(ui, repo, changesets))
962 total = len(revs)
965 total = len(revs)
963 revwidth = max(map(len, revs))
966 revwidth = max(map(len, revs))
964 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
967 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
965 for cset in revs:
968 for cset in revs:
966 seqno += 1
969 seqno += 1
967 doexport(ui, repo, cset, seqno, total, revwidth, opts)
970 doexport(ui, repo, cset, seqno, total, revwidth, opts)
968
971
969 def forget(ui, repo, *pats, **opts):
972 def forget(ui, repo, *pats, **opts):
970 """don't add the specified files on the next commit"""
973 """don't add the specified files on the next commit"""
971 forget = []
974 forget = []
972 for src, abs, rel, exact in walk(repo, pats, opts):
975 for src, abs, rel, exact in walk(repo, pats, opts):
973 if repo.dirstate.state(abs) == 'a':
976 if repo.dirstate.state(abs) == 'a':
974 forget.append(abs)
977 forget.append(abs)
975 if ui.verbose or not exact:
978 if ui.verbose or not exact:
976 ui.status('forgetting ', rel, '\n')
979 ui.status('forgetting ', rel, '\n')
977 repo.forget(forget)
980 repo.forget(forget)
978
981
979 def grep(ui, repo, pattern, *pats, **opts):
982 def grep(ui, repo, pattern, *pats, **opts):
980 """search for a pattern in specified files and revisions"""
983 """search for a pattern in specified files and revisions"""
981 reflags = 0
984 reflags = 0
982 if opts['ignore_case']:
985 if opts['ignore_case']:
983 reflags |= re.I
986 reflags |= re.I
984 regexp = re.compile(pattern, reflags)
987 regexp = re.compile(pattern, reflags)
985 sep, eol = ':', '\n'
988 sep, eol = ':', '\n'
986 if opts['print0']:
989 if opts['print0']:
987 sep = eol = '\0'
990 sep = eol = '\0'
988
991
989 fcache = {}
992 fcache = {}
990 def getfile(fn):
993 def getfile(fn):
991 if fn not in fcache:
994 if fn not in fcache:
992 fcache[fn] = repo.file(fn)
995 fcache[fn] = repo.file(fn)
993 return fcache[fn]
996 return fcache[fn]
994
997
995 def matchlines(body):
998 def matchlines(body):
996 begin = 0
999 begin = 0
997 linenum = 0
1000 linenum = 0
998 while True:
1001 while True:
999 match = regexp.search(body, begin)
1002 match = regexp.search(body, begin)
1000 if not match:
1003 if not match:
1001 break
1004 break
1002 mstart, mend = match.span()
1005 mstart, mend = match.span()
1003 linenum += body.count('\n', begin, mstart) + 1
1006 linenum += body.count('\n', begin, mstart) + 1
1004 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1007 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1005 lend = body.find('\n', mend)
1008 lend = body.find('\n', mend)
1006 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1009 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1007 begin = lend + 1
1010 begin = lend + 1
1008
1011
1009 class linestate:
1012 class linestate:
1010 def __init__(self, line, linenum, colstart, colend):
1013 def __init__(self, line, linenum, colstart, colend):
1011 self.line = line
1014 self.line = line
1012 self.linenum = linenum
1015 self.linenum = linenum
1013 self.colstart = colstart
1016 self.colstart = colstart
1014 self.colend = colend
1017 self.colend = colend
1015 def __eq__(self, other):
1018 def __eq__(self, other):
1016 return self.line == other.line
1019 return self.line == other.line
1017 def __hash__(self):
1020 def __hash__(self):
1018 return hash(self.line)
1021 return hash(self.line)
1019
1022
1020 matches = {}
1023 matches = {}
1021 def grepbody(fn, rev, body):
1024 def grepbody(fn, rev, body):
1022 matches[rev].setdefault(fn, {})
1025 matches[rev].setdefault(fn, {})
1023 m = matches[rev][fn]
1026 m = matches[rev][fn]
1024 for lnum, cstart, cend, line in matchlines(body):
1027 for lnum, cstart, cend, line in matchlines(body):
1025 s = linestate(line, lnum, cstart, cend)
1028 s = linestate(line, lnum, cstart, cend)
1026 m[s] = s
1029 m[s] = s
1027
1030
1028 prev = {}
1031 prev = {}
1029 ucache = {}
1032 ucache = {}
1030 def display(fn, rev, states, prevstates):
1033 def display(fn, rev, states, prevstates):
1031 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1034 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1032 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1035 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1033 counts = {'-': 0, '+': 0}
1036 counts = {'-': 0, '+': 0}
1034 filerevmatches = {}
1037 filerevmatches = {}
1035 for l in diff:
1038 for l in diff:
1036 if incrementing or not opts['all']:
1039 if incrementing or not opts['all']:
1037 change = ((l in prevstates) and '-') or '+'
1040 change = ((l in prevstates) and '-') or '+'
1038 r = rev
1041 r = rev
1039 else:
1042 else:
1040 change = ((l in states) and '-') or '+'
1043 change = ((l in states) and '-') or '+'
1041 r = prev[fn]
1044 r = prev[fn]
1042 cols = [fn, str(rev)]
1045 cols = [fn, str(rev)]
1043 if opts['line_number']: cols.append(str(l.linenum))
1046 if opts['line_number']: cols.append(str(l.linenum))
1044 if opts['all']: cols.append(change)
1047 if opts['all']: cols.append(change)
1045 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1048 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1046 ucache))
1049 ucache))
1047 if opts['files_with_matches']:
1050 if opts['files_with_matches']:
1048 c = (fn, rev)
1051 c = (fn, rev)
1049 if c in filerevmatches: continue
1052 if c in filerevmatches: continue
1050 filerevmatches[c] = 1
1053 filerevmatches[c] = 1
1051 else:
1054 else:
1052 cols.append(l.line)
1055 cols.append(l.line)
1053 ui.write(sep.join(cols), eol)
1056 ui.write(sep.join(cols), eol)
1054 counts[change] += 1
1057 counts[change] += 1
1055 return counts['+'], counts['-']
1058 return counts['+'], counts['-']
1056
1059
1057 fstate = {}
1060 fstate = {}
1058 skip = {}
1061 skip = {}
1059 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1062 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1060 count = 0
1063 count = 0
1061 incrementing = False
1064 incrementing = False
1062 for st, rev, fns in changeiter:
1065 for st, rev, fns in changeiter:
1063 if st == 'window':
1066 if st == 'window':
1064 incrementing = rev
1067 incrementing = rev
1065 matches.clear()
1068 matches.clear()
1066 elif st == 'add':
1069 elif st == 'add':
1067 change = repo.changelog.read(repo.lookup(str(rev)))
1070 change = repo.changelog.read(repo.lookup(str(rev)))
1068 mf = repo.manifest.read(change[0])
1071 mf = repo.manifest.read(change[0])
1069 matches[rev] = {}
1072 matches[rev] = {}
1070 for fn in fns:
1073 for fn in fns:
1071 if fn in skip: continue
1074 if fn in skip: continue
1072 fstate.setdefault(fn, {})
1075 fstate.setdefault(fn, {})
1073 try:
1076 try:
1074 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1077 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1075 except KeyError:
1078 except KeyError:
1076 pass
1079 pass
1077 elif st == 'iter':
1080 elif st == 'iter':
1078 states = matches[rev].items()
1081 states = matches[rev].items()
1079 states.sort()
1082 states.sort()
1080 for fn, m in states:
1083 for fn, m in states:
1081 if fn in skip: continue
1084 if fn in skip: continue
1082 if incrementing or not opts['all'] or fstate[fn]:
1085 if incrementing or not opts['all'] or fstate[fn]:
1083 pos, neg = display(fn, rev, m, fstate[fn])
1086 pos, neg = display(fn, rev, m, fstate[fn])
1084 count += pos + neg
1087 count += pos + neg
1085 if pos and not opts['all']:
1088 if pos and not opts['all']:
1086 skip[fn] = True
1089 skip[fn] = True
1087 fstate[fn] = m
1090 fstate[fn] = m
1088 prev[fn] = rev
1091 prev[fn] = rev
1089
1092
1090 if not incrementing:
1093 if not incrementing:
1091 fstate = fstate.items()
1094 fstate = fstate.items()
1092 fstate.sort()
1095 fstate.sort()
1093 for fn, state in fstate:
1096 for fn, state in fstate:
1094 if fn in skip: continue
1097 if fn in skip: continue
1095 display(fn, rev, {}, state)
1098 display(fn, rev, {}, state)
1096 return (count == 0 and 1) or 0
1099 return (count == 0 and 1) or 0
1097
1100
1098 def heads(ui, repo, **opts):
1101 def heads(ui, repo, **opts):
1099 """show current repository heads"""
1102 """show current repository heads"""
1100 heads = repo.changelog.heads()
1103 heads = repo.changelog.heads()
1101 br = None
1104 br = None
1102 if opts['branches']:
1105 if opts['branches']:
1103 br = repo.branchlookup(heads)
1106 br = repo.branchlookup(heads)
1104 for n in repo.changelog.heads():
1107 for n in repo.changelog.heads():
1105 show_changeset(ui, repo, changenode=n, brinfo=br)
1108 show_changeset(ui, repo, changenode=n, brinfo=br)
1106
1109
1107 def identify(ui, repo):
1110 def identify(ui, repo):
1108 """print information about the working copy"""
1111 """print information about the working copy"""
1109 parents = [p for p in repo.dirstate.parents() if p != nullid]
1112 parents = [p for p in repo.dirstate.parents() if p != nullid]
1110 if not parents:
1113 if not parents:
1111 ui.write("unknown\n")
1114 ui.write("unknown\n")
1112 return
1115 return
1113
1116
1114 hexfunc = ui.verbose and hex or short
1117 hexfunc = ui.verbose and hex or short
1115 (c, a, d, u) = repo.changes()
1118 (c, a, d, u) = repo.changes()
1116 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1119 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1117 (c or a or d) and "+" or "")]
1120 (c or a or d) and "+" or "")]
1118
1121
1119 if not ui.quiet:
1122 if not ui.quiet:
1120 # multiple tags for a single parent separated by '/'
1123 # multiple tags for a single parent separated by '/'
1121 parenttags = ['/'.join(tags)
1124 parenttags = ['/'.join(tags)
1122 for tags in map(repo.nodetags, parents) if tags]
1125 for tags in map(repo.nodetags, parents) if tags]
1123 # tags for multiple parents separated by ' + '
1126 # tags for multiple parents separated by ' + '
1124 if parenttags:
1127 if parenttags:
1125 output.append(' + '.join(parenttags))
1128 output.append(' + '.join(parenttags))
1126
1129
1127 ui.write("%s\n" % ' '.join(output))
1130 ui.write("%s\n" % ' '.join(output))
1128
1131
1129 def import_(ui, repo, patch1, *patches, **opts):
1132 def import_(ui, repo, patch1, *patches, **opts):
1130 """import an ordered set of patches"""
1133 """import an ordered set of patches"""
1131 patches = (patch1,) + patches
1134 patches = (patch1,) + patches
1132
1135
1133 if not opts['force']:
1136 if not opts['force']:
1134 (c, a, d, u) = repo.changes()
1137 (c, a, d, u) = repo.changes()
1135 if c or a or d:
1138 if c or a or d:
1136 raise util.Abort("outstanding uncommitted changes")
1139 raise util.Abort("outstanding uncommitted changes")
1137
1140
1138 d = opts["base"]
1141 d = opts["base"]
1139 strip = opts["strip"]
1142 strip = opts["strip"]
1140
1143
1141 mailre = re.compile(r'(?:From |[\w-]+:)')
1144 mailre = re.compile(r'(?:From |[\w-]+:)')
1142 diffre = re.compile(r'(?:diff -|--- .*\s+\w+ \w+ +\d+ \d+:\d+:\d+ \d+)')
1145 diffre = re.compile(r'(?:diff -|--- .*\s+\w+ \w+ +\d+ \d+:\d+:\d+ \d+)')
1143
1146
1144 for patch in patches:
1147 for patch in patches:
1145 ui.status("applying %s\n" % patch)
1148 ui.status("applying %s\n" % patch)
1146 pf = os.path.join(d, patch)
1149 pf = os.path.join(d, patch)
1147
1150
1148 message = []
1151 message = []
1149 user = None
1152 user = None
1150 hgpatch = False
1153 hgpatch = False
1151 for line in file(pf):
1154 for line in file(pf):
1152 line = line.rstrip()
1155 line = line.rstrip()
1153 if (not message and not hgpatch and
1156 if (not message and not hgpatch and
1154 mailre.match(line) and not opts['force']):
1157 mailre.match(line) and not opts['force']):
1155 if len(line) > 35: line = line[:32] + '...'
1158 if len(line) > 35: line = line[:32] + '...'
1156 raise util.Abort('first line looks like a '
1159 raise util.Abort('first line looks like a '
1157 'mail header: ' + line)
1160 'mail header: ' + line)
1158 if diffre.match(line):
1161 if diffre.match(line):
1159 break
1162 break
1160 elif hgpatch:
1163 elif hgpatch:
1161 # parse values when importing the result of an hg export
1164 # parse values when importing the result of an hg export
1162 if line.startswith("# User "):
1165 if line.startswith("# User "):
1163 user = line[7:]
1166 user = line[7:]
1164 ui.debug('User: %s\n' % user)
1167 ui.debug('User: %s\n' % user)
1165 elif not line.startswith("# ") and line:
1168 elif not line.startswith("# ") and line:
1166 message.append(line)
1169 message.append(line)
1167 hgpatch = False
1170 hgpatch = False
1168 elif line == '# HG changeset patch':
1171 elif line == '# HG changeset patch':
1169 hgpatch = True
1172 hgpatch = True
1170 message = [] # We may have collected garbage
1173 message = [] # We may have collected garbage
1171 else:
1174 else:
1172 message.append(line)
1175 message.append(line)
1173
1176
1174 # make sure message isn't empty
1177 # make sure message isn't empty
1175 if not message:
1178 if not message:
1176 message = "imported patch %s\n" % patch
1179 message = "imported patch %s\n" % patch
1177 else:
1180 else:
1178 message = "%s\n" % '\n'.join(message)
1181 message = "%s\n" % '\n'.join(message)
1179 ui.debug('message:\n%s\n' % message)
1182 ui.debug('message:\n%s\n' % message)
1180
1183
1181 files = util.patch(strip, pf, ui)
1184 files = util.patch(strip, pf, ui)
1182
1185
1183 if len(files) > 0:
1186 if len(files) > 0:
1184 addremove(ui, repo, *files)
1187 addremove(ui, repo, *files)
1185 repo.commit(files, message, user)
1188 repo.commit(files, message, user)
1186
1189
1187 def incoming(ui, repo, source="default", **opts):
1190 def incoming(ui, repo, source="default", **opts):
1188 """show new changesets found in source"""
1191 """show new changesets found in source"""
1189 source = ui.expandpath(source)
1192 source = ui.expandpath(source)
1190 other = hg.repository(ui, source)
1193 other = hg.repository(ui, source)
1191 if not other.local():
1194 if not other.local():
1192 raise util.Abort("incoming doesn't work for remote repositories yet")
1195 raise util.Abort("incoming doesn't work for remote repositories yet")
1193 o = repo.findincoming(other)
1196 o = repo.findincoming(other)
1194 if not o:
1197 if not o:
1195 return
1198 return
1196 o = other.changelog.nodesbetween(o)[0]
1199 o = other.changelog.nodesbetween(o)[0]
1197 for n in o:
1200 for n in o:
1198 show_changeset(ui, other, changenode=n)
1201 show_changeset(ui, other, changenode=n)
1199 if opts['patch']:
1202 if opts['patch']:
1200 prev = other.changelog.parents(n)[0]
1203 prev = other.changelog.parents(n)[0]
1201 dodiff(ui, ui, other, prev, n)
1204 dodiff(ui, ui, other, prev, n)
1202 ui.write("\n")
1205 ui.write("\n")
1203
1206
1204 def init(ui, dest="."):
1207 def init(ui, dest="."):
1205 """create a new repository in the given directory"""
1208 """create a new repository in the given directory"""
1206 if not os.path.exists(dest):
1209 if not os.path.exists(dest):
1207 os.mkdir(dest)
1210 os.mkdir(dest)
1208 hg.repository(ui, dest, create=1)
1211 hg.repository(ui, dest, create=1)
1209
1212
1210 def locate(ui, repo, *pats, **opts):
1213 def locate(ui, repo, *pats, **opts):
1211 """locate files matching specific patterns"""
1214 """locate files matching specific patterns"""
1212 end = opts['print0'] and '\0' or '\n'
1215 end = opts['print0'] and '\0' or '\n'
1213
1216
1214 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1217 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1215 if repo.dirstate.state(abs) == '?':
1218 if repo.dirstate.state(abs) == '?':
1216 continue
1219 continue
1217 if opts['fullpath']:
1220 if opts['fullpath']:
1218 ui.write(os.path.join(repo.root, abs), end)
1221 ui.write(os.path.join(repo.root, abs), end)
1219 else:
1222 else:
1220 ui.write(rel, end)
1223 ui.write(rel, end)
1221
1224
1222 def log(ui, repo, *pats, **opts):
1225 def log(ui, repo, *pats, **opts):
1223 """show revision history of entire repository or files"""
1226 """show revision history of entire repository or files"""
1224 class dui:
1227 class dui:
1225 # Implement and delegate some ui protocol. Save hunks of
1228 # Implement and delegate some ui protocol. Save hunks of
1226 # output for later display in the desired order.
1229 # output for later display in the desired order.
1227 def __init__(self, ui):
1230 def __init__(self, ui):
1228 self.ui = ui
1231 self.ui = ui
1229 self.hunk = {}
1232 self.hunk = {}
1230 def bump(self, rev):
1233 def bump(self, rev):
1231 self.rev = rev
1234 self.rev = rev
1232 self.hunk[rev] = []
1235 self.hunk[rev] = []
1233 def note(self, *args):
1236 def note(self, *args):
1234 if self.verbose:
1237 if self.verbose:
1235 self.write(*args)
1238 self.write(*args)
1236 def status(self, *args):
1239 def status(self, *args):
1237 if not self.quiet:
1240 if not self.quiet:
1238 self.write(*args)
1241 self.write(*args)
1239 def write(self, *args):
1242 def write(self, *args):
1240 self.hunk[self.rev].append(args)
1243 self.hunk[self.rev].append(args)
1241 def debug(self, *args):
1244 def debug(self, *args):
1242 if self.debugflag:
1245 if self.debugflag:
1243 self.write(*args)
1246 self.write(*args)
1244 def __getattr__(self, key):
1247 def __getattr__(self, key):
1245 return getattr(self.ui, key)
1248 return getattr(self.ui, key)
1246 cwd = repo.getcwd()
1249 cwd = repo.getcwd()
1247 if not pats and cwd:
1250 if not pats and cwd:
1248 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1251 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1249 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1252 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1250 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1253 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1251 pats, opts)
1254 pats, opts)
1252 for st, rev, fns in changeiter:
1255 for st, rev, fns in changeiter:
1253 if st == 'window':
1256 if st == 'window':
1254 du = dui(ui)
1257 du = dui(ui)
1255 elif st == 'add':
1258 elif st == 'add':
1256 du.bump(rev)
1259 du.bump(rev)
1257 br = None
1260 br = None
1258 if opts['branch']:
1261 if opts['branch']:
1259 br = repo.branchlookup([repo.changelog.node(rev)])
1262 br = repo.branchlookup([repo.changelog.node(rev)])
1260
1263
1261 if opts['keyword']:
1264 if opts['keyword']:
1262 changes = repo.changelog.read(repo.changelog.node(rev))
1265 changes = repo.changelog.read(repo.changelog.node(rev))
1263 miss = 0
1266 miss = 0
1264 for k in opts['keyword']:
1267 for k in opts['keyword']:
1265 if not (k in changes[1].lower() or
1268 if not (k in changes[1].lower() or
1266 k in changes[4].lower() or
1269 k in changes[4].lower() or
1267 k in " ".join(changes[3][:20]).lower()):
1270 k in " ".join(changes[3][:20]).lower()):
1268 miss = 1
1271 miss = 1
1269 break
1272 break
1270 if miss:
1273 if miss:
1271 continue
1274 continue
1272
1275
1273 show_changeset(du, repo, rev, brinfo=br)
1276 show_changeset(du, repo, rev, brinfo=br)
1274 if opts['patch']:
1277 if opts['patch']:
1275 changenode = repo.changelog.node(rev)
1278 changenode = repo.changelog.node(rev)
1276 prev, other = repo.changelog.parents(changenode)
1279 prev, other = repo.changelog.parents(changenode)
1277 dodiff(du, du, repo, prev, changenode, fns)
1280 dodiff(du, du, repo, prev, changenode, fns)
1278 du.write("\n\n")
1281 du.write("\n\n")
1279 elif st == 'iter':
1282 elif st == 'iter':
1280 for args in du.hunk[rev]:
1283 for args in du.hunk[rev]:
1281 ui.write(*args)
1284 ui.write(*args)
1282
1285
1283 def manifest(ui, repo, rev=None):
1286 def manifest(ui, repo, rev=None):
1284 """output the latest or given revision of the project manifest"""
1287 """output the latest or given revision of the project manifest"""
1285 if rev:
1288 if rev:
1286 try:
1289 try:
1287 # assume all revision numbers are for changesets
1290 # assume all revision numbers are for changesets
1288 n = repo.lookup(rev)
1291 n = repo.lookup(rev)
1289 change = repo.changelog.read(n)
1292 change = repo.changelog.read(n)
1290 n = change[0]
1293 n = change[0]
1291 except hg.RepoError:
1294 except hg.RepoError:
1292 n = repo.manifest.lookup(rev)
1295 n = repo.manifest.lookup(rev)
1293 else:
1296 else:
1294 n = repo.manifest.tip()
1297 n = repo.manifest.tip()
1295 m = repo.manifest.read(n)
1298 m = repo.manifest.read(n)
1296 mf = repo.manifest.readflags(n)
1299 mf = repo.manifest.readflags(n)
1297 files = m.keys()
1300 files = m.keys()
1298 files.sort()
1301 files.sort()
1299
1302
1300 for f in files:
1303 for f in files:
1301 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1304 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1302
1305
1303 def outgoing(ui, repo, dest="default-push", **opts):
1306 def outgoing(ui, repo, dest="default-push", **opts):
1304 """show changesets not found in destination"""
1307 """show changesets not found in destination"""
1305 dest = ui.expandpath(dest)
1308 dest = ui.expandpath(dest)
1306 other = hg.repository(ui, dest)
1309 other = hg.repository(ui, dest)
1307 o = repo.findoutgoing(other)
1310 o = repo.findoutgoing(other)
1308 o = repo.changelog.nodesbetween(o)[0]
1311 o = repo.changelog.nodesbetween(o)[0]
1309 for n in o:
1312 for n in o:
1310 show_changeset(ui, repo, changenode=n)
1313 show_changeset(ui, repo, changenode=n)
1311 if opts['patch']:
1314 if opts['patch']:
1312 prev = repo.changelog.parents(n)[0]
1315 prev = repo.changelog.parents(n)[0]
1313 dodiff(ui, ui, repo, prev, n)
1316 dodiff(ui, ui, repo, prev, n)
1314 ui.write("\n")
1317 ui.write("\n")
1315
1318
1316 def parents(ui, repo, rev=None):
1319 def parents(ui, repo, rev=None):
1317 """show the parents of the working dir or revision"""
1320 """show the parents of the working dir or revision"""
1318 if rev:
1321 if rev:
1319 p = repo.changelog.parents(repo.lookup(rev))
1322 p = repo.changelog.parents(repo.lookup(rev))
1320 else:
1323 else:
1321 p = repo.dirstate.parents()
1324 p = repo.dirstate.parents()
1322
1325
1323 for n in p:
1326 for n in p:
1324 if n != nullid:
1327 if n != nullid:
1325 show_changeset(ui, repo, changenode=n)
1328 show_changeset(ui, repo, changenode=n)
1326
1329
1327 def paths(ui, search=None):
1330 def paths(ui, search=None):
1328 """show definition of symbolic path names"""
1331 """show definition of symbolic path names"""
1329 try:
1332 try:
1330 repo = hg.repository(ui=ui)
1333 repo = hg.repository(ui=ui)
1331 except hg.RepoError:
1334 except hg.RepoError:
1332 pass
1335 pass
1333
1336
1334 if search:
1337 if search:
1335 for name, path in ui.configitems("paths"):
1338 for name, path in ui.configitems("paths"):
1336 if name == search:
1339 if name == search:
1337 ui.write("%s\n" % path)
1340 ui.write("%s\n" % path)
1338 return
1341 return
1339 ui.warn("not found!\n")
1342 ui.warn("not found!\n")
1340 return 1
1343 return 1
1341 else:
1344 else:
1342 for name, path in ui.configitems("paths"):
1345 for name, path in ui.configitems("paths"):
1343 ui.write("%s = %s\n" % (name, path))
1346 ui.write("%s = %s\n" % (name, path))
1344
1347
1345 def pull(ui, repo, source="default", **opts):
1348 def pull(ui, repo, source="default", **opts):
1346 """pull changes from the specified source"""
1349 """pull changes from the specified source"""
1347 source = ui.expandpath(source)
1350 source = ui.expandpath(source)
1348 ui.status('pulling from %s\n' % (source))
1351 ui.status('pulling from %s\n' % (source))
1349
1352
1350 if opts['ssh']:
1353 if opts['ssh']:
1351 ui.setconfig("ui", "ssh", opts['ssh'])
1354 ui.setconfig("ui", "ssh", opts['ssh'])
1352 if opts['remotecmd']:
1355 if opts['remotecmd']:
1353 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1356 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1354
1357
1355 other = hg.repository(ui, source)
1358 other = hg.repository(ui, source)
1356 r = repo.pull(other)
1359 r = repo.pull(other)
1357 if not r:
1360 if not r:
1358 if opts['update']:
1361 if opts['update']:
1359 return update(ui, repo)
1362 return update(ui, repo)
1360 else:
1363 else:
1361 ui.status("(run 'hg update' to get a working copy)\n")
1364 ui.status("(run 'hg update' to get a working copy)\n")
1362
1365
1363 return r
1366 return r
1364
1367
1365 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1368 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1366 """push changes to the specified destination"""
1369 """push changes to the specified destination"""
1367 dest = ui.expandpath(dest)
1370 dest = ui.expandpath(dest)
1368 ui.status('pushing to %s\n' % (dest))
1371 ui.status('pushing to %s\n' % (dest))
1369
1372
1370 if ssh:
1373 if ssh:
1371 ui.setconfig("ui", "ssh", ssh)
1374 ui.setconfig("ui", "ssh", ssh)
1372 if remotecmd:
1375 if remotecmd:
1373 ui.setconfig("ui", "remotecmd", remotecmd)
1376 ui.setconfig("ui", "remotecmd", remotecmd)
1374
1377
1375 other = hg.repository(ui, dest)
1378 other = hg.repository(ui, dest)
1376 r = repo.push(other, force)
1379 r = repo.push(other, force)
1377 return r
1380 return r
1378
1381
1379 def rawcommit(ui, repo, *flist, **rc):
1382 def rawcommit(ui, repo, *flist, **rc):
1380 "raw commit interface"
1383 "raw commit interface"
1381 if rc['text']:
1384 if rc['text']:
1382 ui.warn("Warning: -t and --text is deprecated,"
1385 ui.warn("Warning: -t and --text is deprecated,"
1383 " please use -m or --message instead.\n")
1386 " please use -m or --message instead.\n")
1384 message = rc['message'] or rc['text']
1387 message = rc['message'] or rc['text']
1385 if not message and rc['logfile']:
1388 if not message and rc['logfile']:
1386 try:
1389 try:
1387 message = open(rc['logfile']).read()
1390 message = open(rc['logfile']).read()
1388 except IOError:
1391 except IOError:
1389 pass
1392 pass
1390 if not message and not rc['logfile']:
1393 if not message and not rc['logfile']:
1391 raise util.Abort("missing commit message")
1394 raise util.Abort("missing commit message")
1392
1395
1393 files = relpath(repo, list(flist))
1396 files = relpath(repo, list(flist))
1394 if rc['files']:
1397 if rc['files']:
1395 files += open(rc['files']).read().splitlines()
1398 files += open(rc['files']).read().splitlines()
1396
1399
1397 rc['parent'] = map(repo.lookup, rc['parent'])
1400 rc['parent'] = map(repo.lookup, rc['parent'])
1398
1401
1399 try:
1402 try:
1400 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1403 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1401 except ValueError, inst:
1404 except ValueError, inst:
1402 raise util.Abort(str(inst))
1405 raise util.Abort(str(inst))
1403
1406
1404 def recover(ui, repo):
1407 def recover(ui, repo):
1405 """roll back an interrupted transaction"""
1408 """roll back an interrupted transaction"""
1406 repo.recover()
1409 repo.recover()
1407
1410
1408 def remove(ui, repo, pat, *pats, **opts):
1411 def remove(ui, repo, pat, *pats, **opts):
1409 """remove the specified files on the next commit"""
1412 """remove the specified files on the next commit"""
1410 names = []
1413 names = []
1411 def okaytoremove(abs, rel, exact):
1414 def okaytoremove(abs, rel, exact):
1412 c, a, d, u = repo.changes(files = [abs])
1415 c, a, d, u = repo.changes(files = [abs])
1413 reason = None
1416 reason = None
1414 if c: reason = 'is modified'
1417 if c: reason = 'is modified'
1415 elif a: reason = 'has been marked for add'
1418 elif a: reason = 'has been marked for add'
1416 elif u: reason = 'is not managed'
1419 elif u: reason = 'is not managed'
1417 if reason:
1420 if reason:
1418 if exact: ui.warn('not removing %s: file %s\n' % (rel, reason))
1421 if exact: ui.warn('not removing %s: file %s\n' % (rel, reason))
1419 else:
1422 else:
1420 return True
1423 return True
1421 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1424 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1422 if okaytoremove(abs, rel, exact):
1425 if okaytoremove(abs, rel, exact):
1423 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1426 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1424 names.append(abs)
1427 names.append(abs)
1425 for name in names:
1428 for name in names:
1426 try:
1429 try:
1427 os.unlink(name)
1430 os.unlink(name)
1428 except OSError, inst:
1431 except OSError, inst:
1429 if inst.errno != errno.ENOENT: raise
1432 if inst.errno != errno.ENOENT: raise
1430 repo.remove(names)
1433 repo.remove(names)
1431
1434
1432 def rename(ui, repo, *pats, **opts):
1435 def rename(ui, repo, *pats, **opts):
1433 """rename files; equivalent of copy + remove"""
1436 """rename files; equivalent of copy + remove"""
1434 errs, copied = docopy(ui, repo, pats, opts)
1437 errs, copied = docopy(ui, repo, pats, opts)
1435 names = []
1438 names = []
1436 for abs, rel, exact in copied:
1439 for abs, rel, exact in copied:
1437 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1440 if ui.verbose or not exact: ui.status('removing %s\n' % rel)
1438 try:
1441 try:
1439 os.unlink(rel)
1442 os.unlink(rel)
1440 except OSError, inst:
1443 except OSError, inst:
1441 if inst.errno != errno.ENOENT: raise
1444 if inst.errno != errno.ENOENT: raise
1442 names.append(abs)
1445 names.append(abs)
1443 repo.remove(names)
1446 repo.remove(names)
1444 return errs
1447 return errs
1445
1448
1446 def revert(ui, repo, *names, **opts):
1449 def revert(ui, repo, *names, **opts):
1447 """revert modified files or dirs back to their unmodified states"""
1450 """revert modified files or dirs back to their unmodified states"""
1448 node = opts['rev'] and repo.lookup(opts['rev']) or \
1451 node = opts['rev'] and repo.lookup(opts['rev']) or \
1449 repo.dirstate.parents()[0]
1452 repo.dirstate.parents()[0]
1450 root = os.path.realpath(repo.root)
1453 root = os.path.realpath(repo.root)
1451
1454
1452 def trimpath(p):
1455 def trimpath(p):
1453 p = os.path.realpath(p)
1456 p = os.path.realpath(p)
1454 if p.startswith(root):
1457 if p.startswith(root):
1455 rest = p[len(root):]
1458 rest = p[len(root):]
1456 if not rest:
1459 if not rest:
1457 return rest
1460 return rest
1458 if p.startswith(os.sep):
1461 if p.startswith(os.sep):
1459 return rest[1:]
1462 return rest[1:]
1460 return p
1463 return p
1461
1464
1462 relnames = map(trimpath, names or [os.getcwd()])
1465 relnames = map(trimpath, names or [os.getcwd()])
1463 chosen = {}
1466 chosen = {}
1464
1467
1465 def choose(name):
1468 def choose(name):
1466 def body(name):
1469 def body(name):
1467 for r in relnames:
1470 for r in relnames:
1468 if not name.startswith(r):
1471 if not name.startswith(r):
1469 continue
1472 continue
1470 rest = name[len(r):]
1473 rest = name[len(r):]
1471 if not rest:
1474 if not rest:
1472 return r, True
1475 return r, True
1473 depth = rest.count(os.sep)
1476 depth = rest.count(os.sep)
1474 if not r:
1477 if not r:
1475 if depth == 0 or not opts['nonrecursive']:
1478 if depth == 0 or not opts['nonrecursive']:
1476 return r, True
1479 return r, True
1477 elif rest[0] == os.sep:
1480 elif rest[0] == os.sep:
1478 if depth == 1 or not opts['nonrecursive']:
1481 if depth == 1 or not opts['nonrecursive']:
1479 return r, True
1482 return r, True
1480 return None, False
1483 return None, False
1481 relname, ret = body(name)
1484 relname, ret = body(name)
1482 if ret:
1485 if ret:
1483 chosen[relname] = 1
1486 chosen[relname] = 1
1484 return ret
1487 return ret
1485
1488
1486 r = repo.update(node, False, True, choose, False)
1489 r = repo.update(node, False, True, choose, False)
1487 for n in relnames:
1490 for n in relnames:
1488 if n not in chosen:
1491 if n not in chosen:
1489 ui.warn('error: no matches for %s\n' % n)
1492 ui.warn('error: no matches for %s\n' % n)
1490 r = 1
1493 r = 1
1491 sys.stdout.flush()
1494 sys.stdout.flush()
1492 return r
1495 return r
1493
1496
1494 def root(ui, repo):
1497 def root(ui, repo):
1495 """print the root (top) of the current working dir"""
1498 """print the root (top) of the current working dir"""
1496 ui.write(repo.root + "\n")
1499 ui.write(repo.root + "\n")
1497
1500
1498 def serve(ui, repo, **opts):
1501 def serve(ui, repo, **opts):
1499 """export the repository via HTTP"""
1502 """export the repository via HTTP"""
1500
1503
1501 if opts["stdio"]:
1504 if opts["stdio"]:
1502 fin, fout = sys.stdin, sys.stdout
1505 fin, fout = sys.stdin, sys.stdout
1503 sys.stdout = sys.stderr
1506 sys.stdout = sys.stderr
1504
1507
1505 def getarg():
1508 def getarg():
1506 argline = fin.readline()[:-1]
1509 argline = fin.readline()[:-1]
1507 arg, l = argline.split()
1510 arg, l = argline.split()
1508 val = fin.read(int(l))
1511 val = fin.read(int(l))
1509 return arg, val
1512 return arg, val
1510 def respond(v):
1513 def respond(v):
1511 fout.write("%d\n" % len(v))
1514 fout.write("%d\n" % len(v))
1512 fout.write(v)
1515 fout.write(v)
1513 fout.flush()
1516 fout.flush()
1514
1517
1515 lock = None
1518 lock = None
1516
1519
1517 while 1:
1520 while 1:
1518 cmd = fin.readline()[:-1]
1521 cmd = fin.readline()[:-1]
1519 if cmd == '':
1522 if cmd == '':
1520 return
1523 return
1521 if cmd == "heads":
1524 if cmd == "heads":
1522 h = repo.heads()
1525 h = repo.heads()
1523 respond(" ".join(map(hex, h)) + "\n")
1526 respond(" ".join(map(hex, h)) + "\n")
1524 if cmd == "lock":
1527 if cmd == "lock":
1525 lock = repo.lock()
1528 lock = repo.lock()
1526 respond("")
1529 respond("")
1527 if cmd == "unlock":
1530 if cmd == "unlock":
1528 if lock:
1531 if lock:
1529 lock.release()
1532 lock.release()
1530 lock = None
1533 lock = None
1531 respond("")
1534 respond("")
1532 elif cmd == "branches":
1535 elif cmd == "branches":
1533 arg, nodes = getarg()
1536 arg, nodes = getarg()
1534 nodes = map(bin, nodes.split(" "))
1537 nodes = map(bin, nodes.split(" "))
1535 r = []
1538 r = []
1536 for b in repo.branches(nodes):
1539 for b in repo.branches(nodes):
1537 r.append(" ".join(map(hex, b)) + "\n")
1540 r.append(" ".join(map(hex, b)) + "\n")
1538 respond("".join(r))
1541 respond("".join(r))
1539 elif cmd == "between":
1542 elif cmd == "between":
1540 arg, pairs = getarg()
1543 arg, pairs = getarg()
1541 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1544 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1542 r = []
1545 r = []
1543 for b in repo.between(pairs):
1546 for b in repo.between(pairs):
1544 r.append(" ".join(map(hex, b)) + "\n")
1547 r.append(" ".join(map(hex, b)) + "\n")
1545 respond("".join(r))
1548 respond("".join(r))
1546 elif cmd == "changegroup":
1549 elif cmd == "changegroup":
1547 nodes = []
1550 nodes = []
1548 arg, roots = getarg()
1551 arg, roots = getarg()
1549 nodes = map(bin, roots.split(" "))
1552 nodes = map(bin, roots.split(" "))
1550
1553
1551 cg = repo.changegroup(nodes)
1554 cg = repo.changegroup(nodes)
1552 while 1:
1555 while 1:
1553 d = cg.read(4096)
1556 d = cg.read(4096)
1554 if not d:
1557 if not d:
1555 break
1558 break
1556 fout.write(d)
1559 fout.write(d)
1557
1560
1558 fout.flush()
1561 fout.flush()
1559
1562
1560 elif cmd == "addchangegroup":
1563 elif cmd == "addchangegroup":
1561 if not lock:
1564 if not lock:
1562 respond("not locked")
1565 respond("not locked")
1563 continue
1566 continue
1564 respond("")
1567 respond("")
1565
1568
1566 r = repo.addchangegroup(fin)
1569 r = repo.addchangegroup(fin)
1567 respond("")
1570 respond("")
1568
1571
1569 optlist = "name templates style address port ipv6 accesslog errorlog"
1572 optlist = "name templates style address port ipv6 accesslog errorlog"
1570 for o in optlist.split():
1573 for o in optlist.split():
1571 if opts[o]:
1574 if opts[o]:
1572 ui.setconfig("web", o, opts[o])
1575 ui.setconfig("web", o, opts[o])
1573
1576
1574 try:
1577 try:
1575 httpd = hgweb.create_server(repo)
1578 httpd = hgweb.create_server(repo)
1576 except socket.error, inst:
1579 except socket.error, inst:
1577 raise util.Abort('cannot start server: ' + inst.args[1])
1580 raise util.Abort('cannot start server: ' + inst.args[1])
1578
1581
1579 if ui.verbose:
1582 if ui.verbose:
1580 addr, port = httpd.socket.getsockname()
1583 addr, port = httpd.socket.getsockname()
1581 if addr == '0.0.0.0':
1584 if addr == '0.0.0.0':
1582 addr = socket.gethostname()
1585 addr = socket.gethostname()
1583 else:
1586 else:
1584 try:
1587 try:
1585 addr = socket.gethostbyaddr(addr)[0]
1588 addr = socket.gethostbyaddr(addr)[0]
1586 except socket.error:
1589 except socket.error:
1587 pass
1590 pass
1588 if port != 80:
1591 if port != 80:
1589 ui.status('listening at http://%s:%d/\n' % (addr, port))
1592 ui.status('listening at http://%s:%d/\n' % (addr, port))
1590 else:
1593 else:
1591 ui.status('listening at http://%s/\n' % addr)
1594 ui.status('listening at http://%s/\n' % addr)
1592 httpd.serve_forever()
1595 httpd.serve_forever()
1593
1596
1594 def status(ui, repo, *pats, **opts):
1597 def status(ui, repo, *pats, **opts):
1595 '''show changed files in the working directory
1598 '''show changed files in the working directory
1596
1599
1597 M = modified
1600 M = modified
1598 A = added
1601 A = added
1599 R = removed
1602 R = removed
1600 ? = not tracked
1603 ? = not tracked
1601 '''
1604 '''
1602
1605
1603 cwd = repo.getcwd()
1606 cwd = repo.getcwd()
1604 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1607 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1605 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1608 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1606 for n in repo.changes(files=files, match=matchfn)]
1609 for n in repo.changes(files=files, match=matchfn)]
1607
1610
1608 changetypes = [('modified', 'M', c),
1611 changetypes = [('modified', 'M', c),
1609 ('added', 'A', a),
1612 ('added', 'A', a),
1610 ('removed', 'R', d),
1613 ('removed', 'R', d),
1611 ('unknown', '?', u)]
1614 ('unknown', '?', u)]
1612
1615
1613 end = opts['print0'] and '\0' or '\n'
1616 end = opts['print0'] and '\0' or '\n'
1614
1617
1615 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1618 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1616 or changetypes):
1619 or changetypes):
1617 if opts['no_status']:
1620 if opts['no_status']:
1618 format = "%%s%s" % end
1621 format = "%%s%s" % end
1619 else:
1622 else:
1620 format = "%s %%s%s" % (char, end);
1623 format = "%s %%s%s" % (char, end);
1621
1624
1622 for f in changes:
1625 for f in changes:
1623 ui.write(format % f)
1626 ui.write(format % f)
1624
1627
1625 def tag(ui, repo, name, rev=None, **opts):
1628 def tag(ui, repo, name, rev=None, **opts):
1626 """add a tag for the current tip or a given revision"""
1629 """add a tag for the current tip or a given revision"""
1627 if opts['text']:
1630 if opts['text']:
1628 ui.warn("Warning: -t and --text is deprecated,"
1631 ui.warn("Warning: -t and --text is deprecated,"
1629 " please use -m or --message instead.\n")
1632 " please use -m or --message instead.\n")
1630 if name == "tip":
1633 if name == "tip":
1631 raise util.Abort("the name 'tip' is reserved")
1634 raise util.Abort("the name 'tip' is reserved")
1632 if rev:
1635 if rev:
1633 r = hex(repo.lookup(rev))
1636 r = hex(repo.lookup(rev))
1634 else:
1637 else:
1635 r = hex(repo.changelog.tip())
1638 r = hex(repo.changelog.tip())
1636
1639
1637 if name.find(revrangesep) >= 0:
1640 if name.find(revrangesep) >= 0:
1638 raise util.Abort("'%s' cannot be used in a tag name" % revrangesep)
1641 raise util.Abort("'%s' cannot be used in a tag name" % revrangesep)
1639
1642
1640 if opts['local']:
1643 if opts['local']:
1641 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1644 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1642 return
1645 return
1643
1646
1644 (c, a, d, u) = repo.changes()
1647 (c, a, d, u) = repo.changes()
1645 for x in (c, a, d, u):
1648 for x in (c, a, d, u):
1646 if ".hgtags" in x:
1649 if ".hgtags" in x:
1647 raise util.Abort("working copy of .hgtags is changed "
1650 raise util.Abort("working copy of .hgtags is changed "
1648 "(please commit .hgtags manually)")
1651 "(please commit .hgtags manually)")
1649
1652
1650 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1653 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1651 if repo.dirstate.state(".hgtags") == '?':
1654 if repo.dirstate.state(".hgtags") == '?':
1652 repo.add([".hgtags"])
1655 repo.add([".hgtags"])
1653
1656
1654 message = (opts['message'] or opts['text'] or
1657 message = (opts['message'] or opts['text'] or
1655 "Added tag %s for changeset %s" % (name, r))
1658 "Added tag %s for changeset %s" % (name, r))
1656 try:
1659 try:
1657 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1660 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1658 except ValueError, inst:
1661 except ValueError, inst:
1659 raise util.Abort(str(inst))
1662 raise util.Abort(str(inst))
1660
1663
1661 def tags(ui, repo):
1664 def tags(ui, repo):
1662 """list repository tags"""
1665 """list repository tags"""
1663
1666
1664 l = repo.tagslist()
1667 l = repo.tagslist()
1665 l.reverse()
1668 l.reverse()
1666 for t, n in l:
1669 for t, n in l:
1667 try:
1670 try:
1668 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1671 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
1669 except KeyError:
1672 except KeyError:
1670 r = " ?:?"
1673 r = " ?:?"
1671 ui.write("%-30s %s\n" % (t, r))
1674 ui.write("%-30s %s\n" % (t, r))
1672
1675
1673 def tip(ui, repo):
1676 def tip(ui, repo):
1674 """show the tip revision"""
1677 """show the tip revision"""
1675 n = repo.changelog.tip()
1678 n = repo.changelog.tip()
1676 show_changeset(ui, repo, changenode=n)
1679 show_changeset(ui, repo, changenode=n)
1677
1680
1678 def unbundle(ui, repo, fname):
1681 def unbundle(ui, repo, fname):
1679 """apply a changegroup file"""
1682 """apply a changegroup file"""
1680 f = urllib.urlopen(fname)
1683 f = urllib.urlopen(fname)
1681
1684
1682 if f.read(4) != "HG10":
1685 if f.read(4) != "HG10":
1683 raise util.Abort("%s: not a Mercurial bundle file" % fname)
1686 raise util.Abort("%s: not a Mercurial bundle file" % fname)
1684
1687
1685 def bzgenerator(f):
1688 def bzgenerator(f):
1686 zd = bz2.BZ2Decompressor()
1689 zd = bz2.BZ2Decompressor()
1687 for chunk in f:
1690 for chunk in f:
1688 yield zd.decompress(chunk)
1691 yield zd.decompress(chunk)
1689 yield zd.flush()
1692 yield zd.flush()
1690
1693
1691 bzgen = bzgenerator(util.filechunkiter(f, 4096))
1694 bzgen = bzgenerator(util.filechunkiter(f, 4096))
1692 repo.addchangegroup(util.chunkbuffer(bzgen))
1695 repo.addchangegroup(util.chunkbuffer(bzgen))
1693
1696
1694 def undo(ui, repo):
1697 def undo(ui, repo):
1695 """undo the last commit or pull
1698 """undo the last commit or pull
1696
1699
1697 Roll back the last pull or commit transaction on the
1700 Roll back the last pull or commit transaction on the
1698 repository, restoring the project to its earlier state.
1701 repository, restoring the project to its earlier state.
1699
1702
1700 This command should be used with care. There is only one level of
1703 This command should be used with care. There is only one level of
1701 undo and there is no redo.
1704 undo and there is no redo.
1702
1705
1703 This command is not intended for use on public repositories. Once
1706 This command is not intended for use on public repositories. Once
1704 a change is visible for pull by other users, undoing it locally is
1707 a change is visible for pull by other users, undoing it locally is
1705 ineffective.
1708 ineffective.
1706 """
1709 """
1707 repo.undo()
1710 repo.undo()
1708
1711
1709 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1712 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1710 '''update or merge working directory
1713 '''update or merge working directory
1711
1714
1712 If there are no outstanding changes in the working directory and
1715 If there are no outstanding changes in the working directory and
1713 there is a linear relationship between the current version and the
1716 there is a linear relationship between the current version and the
1714 requested version, the result is the requested version.
1717 requested version, the result is the requested version.
1715
1718
1716 Otherwise the result is a merge between the contents of the
1719 Otherwise the result is a merge between the contents of the
1717 current working directory and the requested version. Files that
1720 current working directory and the requested version. Files that
1718 changed between either parent are marked as changed for the next
1721 changed between either parent are marked as changed for the next
1719 commit and a commit must be performed before any further updates
1722 commit and a commit must be performed before any further updates
1720 are allowed.
1723 are allowed.
1721 '''
1724 '''
1722 if branch:
1725 if branch:
1723 br = repo.branchlookup(branch=branch)
1726 br = repo.branchlookup(branch=branch)
1724 found = []
1727 found = []
1725 for x in br:
1728 for x in br:
1726 if branch in br[x]:
1729 if branch in br[x]:
1727 found.append(x)
1730 found.append(x)
1728 if len(found) > 1:
1731 if len(found) > 1:
1729 ui.warn("Found multiple heads for %s\n" % branch)
1732 ui.warn("Found multiple heads for %s\n" % branch)
1730 for x in found:
1733 for x in found:
1731 show_changeset(ui, repo, changenode=x, brinfo=br)
1734 show_changeset(ui, repo, changenode=x, brinfo=br)
1732 return 1
1735 return 1
1733 if len(found) == 1:
1736 if len(found) == 1:
1734 node = found[0]
1737 node = found[0]
1735 ui.warn("Using head %s for branch %s\n" % (short(node), branch))
1738 ui.warn("Using head %s for branch %s\n" % (short(node), branch))
1736 else:
1739 else:
1737 ui.warn("branch %s not found\n" % (branch))
1740 ui.warn("branch %s not found\n" % (branch))
1738 return 1
1741 return 1
1739 else:
1742 else:
1740 node = node and repo.lookup(node) or repo.changelog.tip()
1743 node = node and repo.lookup(node) or repo.changelog.tip()
1741 return repo.update(node, allow=merge, force=clean)
1744 return repo.update(node, allow=merge, force=clean)
1742
1745
1743 def verify(ui, repo):
1746 def verify(ui, repo):
1744 """verify the integrity of the repository"""
1747 """verify the integrity of the repository"""
1745 return repo.verify()
1748 return repo.verify()
1746
1749
1747 # Command options and aliases are listed here, alphabetically
1750 # Command options and aliases are listed here, alphabetically
1748
1751
1749 table = {
1752 table = {
1750 "^add":
1753 "^add":
1751 (add,
1754 (add,
1752 [('I', 'include', [], 'include path in search'),
1755 [('I', 'include', [], 'include path in search'),
1753 ('X', 'exclude', [], 'exclude path from search')],
1756 ('X', 'exclude', [], 'exclude path from search')],
1754 "hg add [OPTION]... [FILE]..."),
1757 "hg add [OPTION]... [FILE]..."),
1755 "addremove":
1758 "addremove":
1756 (addremove,
1759 (addremove,
1757 [('I', 'include', [], 'include path in search'),
1760 [('I', 'include', [], 'include path in search'),
1758 ('X', 'exclude', [], 'exclude path from search')],
1761 ('X', 'exclude', [], 'exclude path from search')],
1759 "hg addremove [OPTION]... [FILE]..."),
1762 "hg addremove [OPTION]... [FILE]..."),
1760 "^annotate":
1763 "^annotate":
1761 (annotate,
1764 (annotate,
1762 [('r', 'rev', '', 'revision'),
1765 [('r', 'rev', '', 'revision'),
1763 ('a', 'text', None, 'treat all files as text'),
1766 ('a', 'text', None, 'treat all files as text'),
1764 ('u', 'user', None, 'show user'),
1767 ('u', 'user', None, 'show user'),
1765 ('n', 'number', None, 'show revision number'),
1768 ('n', 'number', None, 'show revision number'),
1766 ('c', 'changeset', None, 'show changeset'),
1769 ('c', 'changeset', None, 'show changeset'),
1767 ('I', 'include', [], 'include path in search'),
1770 ('I', 'include', [], 'include path in search'),
1768 ('X', 'exclude', [], 'exclude path from search')],
1771 ('X', 'exclude', [], 'exclude path from search')],
1769 'hg annotate [OPTION]... FILE...'),
1772 'hg annotate [OPTION]... FILE...'),
1770 "bundle":
1773 "bundle":
1771 (bundle,
1774 (bundle,
1772 [],
1775 [],
1773 'hg bundle FILE DEST'),
1776 'hg bundle FILE DEST'),
1774 "cat":
1777 "cat":
1775 (cat,
1778 (cat,
1776 [('I', 'include', [], 'include path in search'),
1779 [('I', 'include', [], 'include path in search'),
1777 ('X', 'exclude', [], 'exclude path from search'),
1780 ('X', 'exclude', [], 'exclude path from search'),
1778 ('o', 'output', "", 'output to file'),
1781 ('o', 'output', "", 'output to file'),
1779 ('r', 'rev', '', 'revision')],
1782 ('r', 'rev', '', 'revision')],
1780 'hg cat [OPTION]... FILE...'),
1783 'hg cat [OPTION]... FILE...'),
1781 "^clone":
1784 "^clone":
1782 (clone,
1785 (clone,
1783 [('U', 'noupdate', None, 'skip update after cloning'),
1786 [('U', 'noupdate', None, 'skip update after cloning'),
1784 ('e', 'ssh', "", 'ssh command'),
1787 ('e', 'ssh', "", 'ssh command'),
1788 ('r', 'rev', "", 'only clone changesets needed to create revision'),
1785 ('', 'pull', None, 'use pull protocol to copy metadata'),
1789 ('', 'pull', None, 'use pull protocol to copy metadata'),
1786 ('', 'remotecmd', "", 'remote hg command')],
1790 ('', 'remotecmd', "", 'remote hg command')],
1787 'hg clone [OPTION]... SOURCE [DEST]'),
1791 'hg clone [OPTION]... SOURCE [DEST]'),
1788 "^commit|ci":
1792 "^commit|ci":
1789 (commit,
1793 (commit,
1790 [('A', 'addremove', None, 'run add/remove during commit'),
1794 [('A', 'addremove', None, 'run add/remove during commit'),
1791 ('I', 'include', [], 'include path in search'),
1795 ('I', 'include', [], 'include path in search'),
1792 ('X', 'exclude', [], 'exclude path from search'),
1796 ('X', 'exclude', [], 'exclude path from search'),
1793 ('m', 'message', "", 'commit message'),
1797 ('m', 'message', "", 'commit message'),
1794 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1798 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1795 ('l', 'logfile', "", 'commit message file'),
1799 ('l', 'logfile', "", 'commit message file'),
1796 ('d', 'date', "", 'date code'),
1800 ('d', 'date', "", 'date code'),
1797 ('u', 'user', "", 'user')],
1801 ('u', 'user', "", 'user')],
1798 'hg commit [OPTION]... [FILE]...'),
1802 'hg commit [OPTION]... [FILE]...'),
1799 "copy|cp": (copy,
1803 "copy|cp": (copy,
1800 [('I', 'include', [], 'include path in search'),
1804 [('I', 'include', [], 'include path in search'),
1801 ('X', 'exclude', [], 'exclude path from search'),
1805 ('X', 'exclude', [], 'exclude path from search'),
1802 ('A', 'after', None, 'record a copy after it has happened'),
1806 ('A', 'after', None, 'record a copy after it has happened'),
1803 ('f', 'force', None, 'replace destination if it exists'),
1807 ('f', 'force', None, 'replace destination if it exists'),
1804 ('p', 'parents', None, 'append source path to dest')],
1808 ('p', 'parents', None, 'append source path to dest')],
1805 'hg copy [OPTION]... [SOURCE]... DEST'),
1809 'hg copy [OPTION]... [SOURCE]... DEST'),
1806 "debugancestor": (debugancestor, [], 'debugancestor INDEX REV1 REV2'),
1810 "debugancestor": (debugancestor, [], 'debugancestor INDEX REV1 REV2'),
1807 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1811 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1808 "debugconfig": (debugconfig, [], 'debugconfig'),
1812 "debugconfig": (debugconfig, [], 'debugconfig'),
1809 "debugstate": (debugstate, [], 'debugstate'),
1813 "debugstate": (debugstate, [], 'debugstate'),
1810 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1814 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1811 "debugindex": (debugindex, [], 'debugindex FILE'),
1815 "debugindex": (debugindex, [], 'debugindex FILE'),
1812 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1816 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1813 "debugrename": (debugrename, [], 'debugrename FILE [REV]'),
1817 "debugrename": (debugrename, [], 'debugrename FILE [REV]'),
1814 "debugwalk":
1818 "debugwalk":
1815 (debugwalk,
1819 (debugwalk,
1816 [('I', 'include', [], 'include path in search'),
1820 [('I', 'include', [], 'include path in search'),
1817 ('X', 'exclude', [], 'exclude path from search')],
1821 ('X', 'exclude', [], 'exclude path from search')],
1818 'debugwalk [OPTION]... [FILE]...'),
1822 'debugwalk [OPTION]... [FILE]...'),
1819 "^diff":
1823 "^diff":
1820 (diff,
1824 (diff,
1821 [('r', 'rev', [], 'revision'),
1825 [('r', 'rev', [], 'revision'),
1822 ('a', 'text', None, 'treat all files as text'),
1826 ('a', 'text', None, 'treat all files as text'),
1823 ('I', 'include', [], 'include path in search'),
1827 ('I', 'include', [], 'include path in search'),
1824 ('X', 'exclude', [], 'exclude path from search')],
1828 ('X', 'exclude', [], 'exclude path from search')],
1825 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1829 'hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1826 "^export":
1830 "^export":
1827 (export,
1831 (export,
1828 [('o', 'output', "", 'output to file'),
1832 [('o', 'output', "", 'output to file'),
1829 ('a', 'text', None, 'treat all files as text')],
1833 ('a', 'text', None, 'treat all files as text')],
1830 "hg export [-a] [-o OUTFILE] REV..."),
1834 "hg export [-a] [-o OUTFILE] REV..."),
1831 "forget":
1835 "forget":
1832 (forget,
1836 (forget,
1833 [('I', 'include', [], 'include path in search'),
1837 [('I', 'include', [], 'include path in search'),
1834 ('X', 'exclude', [], 'exclude path from search')],
1838 ('X', 'exclude', [], 'exclude path from search')],
1835 "hg forget [OPTION]... FILE..."),
1839 "hg forget [OPTION]... FILE..."),
1836 "grep":
1840 "grep":
1837 (grep,
1841 (grep,
1838 [('0', 'print0', None, 'end fields with NUL'),
1842 [('0', 'print0', None, 'end fields with NUL'),
1839 ('I', 'include', [], 'include path in search'),
1843 ('I', 'include', [], 'include path in search'),
1840 ('X', 'exclude', [], 'include path in search'),
1844 ('X', 'exclude', [], 'include path in search'),
1841 ('', 'all', None, 'print all revisions with matches'),
1845 ('', 'all', None, 'print all revisions with matches'),
1842 ('i', 'ignore-case', None, 'ignore case when matching'),
1846 ('i', 'ignore-case', None, 'ignore case when matching'),
1843 ('l', 'files-with-matches', None, 'print names of files and revs with matches'),
1847 ('l', 'files-with-matches', None, 'print names of files and revs with matches'),
1844 ('n', 'line-number', None, 'print line numbers'),
1848 ('n', 'line-number', None, 'print line numbers'),
1845 ('r', 'rev', [], 'search in revision rev'),
1849 ('r', 'rev', [], 'search in revision rev'),
1846 ('u', 'user', None, 'print user who made change')],
1850 ('u', 'user', None, 'print user who made change')],
1847 "hg grep [OPTION]... PATTERN [FILE]..."),
1851 "hg grep [OPTION]... PATTERN [FILE]..."),
1848 "heads":
1852 "heads":
1849 (heads,
1853 (heads,
1850 [('b', 'branches', None, 'find branch info')],
1854 [('b', 'branches', None, 'find branch info')],
1851 'hg heads [-b]'),
1855 'hg heads [-b]'),
1852 "help": (help_, [], 'hg help [COMMAND]'),
1856 "help": (help_, [], 'hg help [COMMAND]'),
1853 "identify|id": (identify, [], 'hg identify'),
1857 "identify|id": (identify, [], 'hg identify'),
1854 "import|patch":
1858 "import|patch":
1855 (import_,
1859 (import_,
1856 [('p', 'strip', 1, 'path strip'),
1860 [('p', 'strip', 1, 'path strip'),
1857 ('f', 'force', None, 'skip check for outstanding changes'),
1861 ('f', 'force', None, 'skip check for outstanding changes'),
1858 ('b', 'base', "", 'base path')],
1862 ('b', 'base', "", 'base path')],
1859 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1863 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
1860 "incoming|in": (incoming,
1864 "incoming|in": (incoming,
1861 [('p', 'patch', None, 'show patch')],
1865 [('p', 'patch', None, 'show patch')],
1862 'hg incoming [-p] [SOURCE]'),
1866 'hg incoming [-p] [SOURCE]'),
1863 "^init": (init, [], 'hg init [DEST]'),
1867 "^init": (init, [], 'hg init [DEST]'),
1864 "locate":
1868 "locate":
1865 (locate,
1869 (locate,
1866 [('r', 'rev', '', 'revision'),
1870 [('r', 'rev', '', 'revision'),
1867 ('0', 'print0', None, 'end filenames with NUL'),
1871 ('0', 'print0', None, 'end filenames with NUL'),
1868 ('f', 'fullpath', None, 'print complete paths'),
1872 ('f', 'fullpath', None, 'print complete paths'),
1869 ('I', 'include', [], 'include path in search'),
1873 ('I', 'include', [], 'include path in search'),
1870 ('X', 'exclude', [], 'exclude path from search')],
1874 ('X', 'exclude', [], 'exclude path from search')],
1871 'hg locate [OPTION]... [PATTERN]...'),
1875 'hg locate [OPTION]... [PATTERN]...'),
1872 "^log|history":
1876 "^log|history":
1873 (log,
1877 (log,
1874 [('I', 'include', [], 'include path in search'),
1878 [('I', 'include', [], 'include path in search'),
1875 ('X', 'exclude', [], 'exclude path from search'),
1879 ('X', 'exclude', [], 'exclude path from search'),
1876 ('b', 'branch', None, 'show branches'),
1880 ('b', 'branch', None, 'show branches'),
1877 ('k', 'keyword', [], 'search for a keyword'),
1881 ('k', 'keyword', [], 'search for a keyword'),
1878 ('r', 'rev', [], 'revision'),
1882 ('r', 'rev', [], 'revision'),
1879 ('p', 'patch', None, 'show patch')],
1883 ('p', 'patch', None, 'show patch')],
1880 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1884 'hg log [-I] [-X] [-r REV]... [-p] [FILE]'),
1881 "manifest": (manifest, [], 'hg manifest [REV]'),
1885 "manifest": (manifest, [], 'hg manifest [REV]'),
1882 "outgoing|out": (outgoing,
1886 "outgoing|out": (outgoing,
1883 [('p', 'patch', None, 'show patch')],
1887 [('p', 'patch', None, 'show patch')],
1884 'hg outgoing [-p] [DEST]'),
1888 'hg outgoing [-p] [DEST]'),
1885 "parents": (parents, [], 'hg parents [REV]'),
1889 "parents": (parents, [], 'hg parents [REV]'),
1886 "paths": (paths, [], 'hg paths [NAME]'),
1890 "paths": (paths, [], 'hg paths [NAME]'),
1887 "^pull":
1891 "^pull":
1888 (pull,
1892 (pull,
1889 [('u', 'update', None, 'update working directory'),
1893 [('u', 'update', None, 'update working directory'),
1890 ('e', 'ssh', "", 'ssh command'),
1894 ('e', 'ssh', "", 'ssh command'),
1891 ('', 'remotecmd', "", 'remote hg command')],
1895 ('', 'remotecmd', "", 'remote hg command')],
1892 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1896 'hg pull [-u] [-e FILE] [--remotecmd FILE] [SOURCE]'),
1893 "^push":
1897 "^push":
1894 (push,
1898 (push,
1895 [('f', 'force', None, 'force push'),
1899 [('f', 'force', None, 'force push'),
1896 ('e', 'ssh', "", 'ssh command'),
1900 ('e', 'ssh', "", 'ssh command'),
1897 ('', 'remotecmd', "", 'remote hg command')],
1901 ('', 'remotecmd', "", 'remote hg command')],
1898 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1902 'hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]'),
1899 "rawcommit":
1903 "rawcommit":
1900 (rawcommit,
1904 (rawcommit,
1901 [('p', 'parent', [], 'parent'),
1905 [('p', 'parent', [], 'parent'),
1902 ('d', 'date', "", 'date code'),
1906 ('d', 'date', "", 'date code'),
1903 ('u', 'user', "", 'user'),
1907 ('u', 'user', "", 'user'),
1904 ('F', 'files', "", 'file list'),
1908 ('F', 'files', "", 'file list'),
1905 ('m', 'message', "", 'commit message'),
1909 ('m', 'message', "", 'commit message'),
1906 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1910 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1907 ('l', 'logfile', "", 'commit message file')],
1911 ('l', 'logfile', "", 'commit message file')],
1908 'hg rawcommit [OPTION]... [FILE]...'),
1912 'hg rawcommit [OPTION]... [FILE]...'),
1909 "recover": (recover, [], "hg recover"),
1913 "recover": (recover, [], "hg recover"),
1910 "^remove|rm": (remove,
1914 "^remove|rm": (remove,
1911 [('I', 'include', [], 'include path in search'),
1915 [('I', 'include', [], 'include path in search'),
1912 ('X', 'exclude', [], 'exclude path from search')],
1916 ('X', 'exclude', [], 'exclude path from search')],
1913 "hg remove [OPTION]... FILE..."),
1917 "hg remove [OPTION]... FILE..."),
1914 "rename|mv": (rename,
1918 "rename|mv": (rename,
1915 [('I', 'include', [], 'include path in search'),
1919 [('I', 'include', [], 'include path in search'),
1916 ('X', 'exclude', [], 'exclude path from search'),
1920 ('X', 'exclude', [], 'exclude path from search'),
1917 ('A', 'after', None, 'record a copy after it has happened'),
1921 ('A', 'after', None, 'record a copy after it has happened'),
1918 ('f', 'force', None, 'replace destination if it exists'),
1922 ('f', 'force', None, 'replace destination if it exists'),
1919 ('p', 'parents', None, 'append source path to dest')],
1923 ('p', 'parents', None, 'append source path to dest')],
1920 'hg rename [OPTION]... [SOURCE]... DEST'),
1924 'hg rename [OPTION]... [SOURCE]... DEST'),
1921 "^revert":
1925 "^revert":
1922 (revert,
1926 (revert,
1923 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1927 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1924 ("r", "rev", "", "revision")],
1928 ("r", "rev", "", "revision")],
1925 "hg revert [-n] [-r REV] [NAME]..."),
1929 "hg revert [-n] [-r REV] [NAME]..."),
1926 "root": (root, [], "hg root"),
1930 "root": (root, [], "hg root"),
1927 "^serve":
1931 "^serve":
1928 (serve,
1932 (serve,
1929 [('A', 'accesslog', '', 'access log file'),
1933 [('A', 'accesslog', '', 'access log file'),
1930 ('E', 'errorlog', '', 'error log file'),
1934 ('E', 'errorlog', '', 'error log file'),
1931 ('p', 'port', 0, 'listen port'),
1935 ('p', 'port', 0, 'listen port'),
1932 ('a', 'address', '', 'interface address'),
1936 ('a', 'address', '', 'interface address'),
1933 ('n', 'name', "", 'repository name'),
1937 ('n', 'name', "", 'repository name'),
1934 ('', 'stdio', None, 'for remote clients'),
1938 ('', 'stdio', None, 'for remote clients'),
1935 ('t', 'templates', "", 'template directory'),
1939 ('t', 'templates', "", 'template directory'),
1936 ('', 'style', "", 'template style'),
1940 ('', 'style', "", 'template style'),
1937 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1941 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1938 "hg serve [OPTION]..."),
1942 "hg serve [OPTION]..."),
1939 "^status":
1943 "^status":
1940 (status,
1944 (status,
1941 [('m', 'modified', None, 'show only modified files'),
1945 [('m', 'modified', None, 'show only modified files'),
1942 ('a', 'added', None, 'show only added files'),
1946 ('a', 'added', None, 'show only added files'),
1943 ('r', 'removed', None, 'show only removed files'),
1947 ('r', 'removed', None, 'show only removed files'),
1944 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1948 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1945 ('n', 'no-status', None, 'hide status prefix'),
1949 ('n', 'no-status', None, 'hide status prefix'),
1946 ('0', 'print0', None, 'end filenames with NUL'),
1950 ('0', 'print0', None, 'end filenames with NUL'),
1947 ('I', 'include', [], 'include path in search'),
1951 ('I', 'include', [], 'include path in search'),
1948 ('X', 'exclude', [], 'exclude path from search')],
1952 ('X', 'exclude', [], 'exclude path from search')],
1949 "hg status [OPTION]... [FILE]..."),
1953 "hg status [OPTION]... [FILE]..."),
1950 "tag":
1954 "tag":
1951 (tag,
1955 (tag,
1952 [('l', 'local', None, 'make the tag local'),
1956 [('l', 'local', None, 'make the tag local'),
1953 ('m', 'message', "", 'commit message'),
1957 ('m', 'message', "", 'commit message'),
1954 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1958 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1955 ('d', 'date', "", 'date code'),
1959 ('d', 'date', "", 'date code'),
1956 ('u', 'user', "", 'user')],
1960 ('u', 'user', "", 'user')],
1957 'hg tag [OPTION]... NAME [REV]'),
1961 'hg tag [OPTION]... NAME [REV]'),
1958 "tags": (tags, [], 'hg tags'),
1962 "tags": (tags, [], 'hg tags'),
1959 "tip": (tip, [], 'hg tip'),
1963 "tip": (tip, [], 'hg tip'),
1960 "unbundle":
1964 "unbundle":
1961 (unbundle,
1965 (unbundle,
1962 [],
1966 [],
1963 'hg unbundle FILE'),
1967 'hg unbundle FILE'),
1964 "undo": (undo, [], 'hg undo'),
1968 "undo": (undo, [], 'hg undo'),
1965 "^update|up|checkout|co":
1969 "^update|up|checkout|co":
1966 (update,
1970 (update,
1967 [('b', 'branch', "", 'checkout the head of a specific branch'),
1971 [('b', 'branch', "", 'checkout the head of a specific branch'),
1968 ('m', 'merge', None, 'allow merging of conflicts'),
1972 ('m', 'merge', None, 'allow merging of conflicts'),
1969 ('C', 'clean', None, 'overwrite locally modified files')],
1973 ('C', 'clean', None, 'overwrite locally modified files')],
1970 'hg update [-b TAG] [-m] [-C] [REV]'),
1974 'hg update [-b TAG] [-m] [-C] [REV]'),
1971 "verify": (verify, [], 'hg verify'),
1975 "verify": (verify, [], 'hg verify'),
1972 "version": (show_version, [], 'hg version'),
1976 "version": (show_version, [], 'hg version'),
1973 }
1977 }
1974
1978
1975 globalopts = [
1979 globalopts = [
1976 ('R', 'repository', "", 'repository root directory'),
1980 ('R', 'repository', "", 'repository root directory'),
1977 ('', 'cwd', '', 'change working directory'),
1981 ('', 'cwd', '', 'change working directory'),
1978 ('y', 'noninteractive', None, 'run non-interactively'),
1982 ('y', 'noninteractive', None, 'run non-interactively'),
1979 ('q', 'quiet', None, 'quiet mode'),
1983 ('q', 'quiet', None, 'quiet mode'),
1980 ('v', 'verbose', None, 'verbose mode'),
1984 ('v', 'verbose', None, 'verbose mode'),
1981 ('', 'debug', None, 'debug mode'),
1985 ('', 'debug', None, 'debug mode'),
1982 ('', 'debugger', None, 'start debugger'),
1986 ('', 'debugger', None, 'start debugger'),
1983 ('', 'traceback', None, 'print traceback on exception'),
1987 ('', 'traceback', None, 'print traceback on exception'),
1984 ('', 'time', None, 'time how long the command takes'),
1988 ('', 'time', None, 'time how long the command takes'),
1985 ('', 'profile', None, 'profile'),
1989 ('', 'profile', None, 'profile'),
1986 ('', 'version', None, 'output version information and exit'),
1990 ('', 'version', None, 'output version information and exit'),
1987 ('h', 'help', None, 'display help and exit'),
1991 ('h', 'help', None, 'display help and exit'),
1988 ]
1992 ]
1989
1993
1990 norepo = ("clone init version help debugancestor debugconfig debugdata"
1994 norepo = ("clone init version help debugancestor debugconfig debugdata"
1991 " debugindex debugindexdot paths")
1995 " debugindex debugindexdot paths")
1992
1996
1993 def find(cmd):
1997 def find(cmd):
1994 for e in table.keys():
1998 for e in table.keys():
1995 if re.match("(%s)$" % e, cmd):
1999 if re.match("(%s)$" % e, cmd):
1996 return e, table[e]
2000 return e, table[e]
1997
2001
1998 raise UnknownCommand(cmd)
2002 raise UnknownCommand(cmd)
1999
2003
2000 class SignalInterrupt(Exception):
2004 class SignalInterrupt(Exception):
2001 """Exception raised on SIGTERM and SIGHUP."""
2005 """Exception raised on SIGTERM and SIGHUP."""
2002
2006
2003 def catchterm(*args):
2007 def catchterm(*args):
2004 raise SignalInterrupt
2008 raise SignalInterrupt
2005
2009
2006 def run():
2010 def run():
2007 sys.exit(dispatch(sys.argv[1:]))
2011 sys.exit(dispatch(sys.argv[1:]))
2008
2012
2009 class ParseError(Exception):
2013 class ParseError(Exception):
2010 """Exception raised on errors in parsing the command line."""
2014 """Exception raised on errors in parsing the command line."""
2011
2015
2012 def parse(args):
2016 def parse(args):
2013 options = {}
2017 options = {}
2014 cmdoptions = {}
2018 cmdoptions = {}
2015
2019
2016 try:
2020 try:
2017 args = fancyopts.fancyopts(args, globalopts, options)
2021 args = fancyopts.fancyopts(args, globalopts, options)
2018 except fancyopts.getopt.GetoptError, inst:
2022 except fancyopts.getopt.GetoptError, inst:
2019 raise ParseError(None, inst)
2023 raise ParseError(None, inst)
2020
2024
2021 if args:
2025 if args:
2022 cmd, args = args[0], args[1:]
2026 cmd, args = args[0], args[1:]
2023 i = find(cmd)[1]
2027 i = find(cmd)[1]
2024 c = list(i[1])
2028 c = list(i[1])
2025 else:
2029 else:
2026 cmd = None
2030 cmd = None
2027 c = []
2031 c = []
2028
2032
2029 # combine global options into local
2033 # combine global options into local
2030 for o in globalopts:
2034 for o in globalopts:
2031 c.append((o[0], o[1], options[o[1]], o[3]))
2035 c.append((o[0], o[1], options[o[1]], o[3]))
2032
2036
2033 try:
2037 try:
2034 args = fancyopts.fancyopts(args, c, cmdoptions)
2038 args = fancyopts.fancyopts(args, c, cmdoptions)
2035 except fancyopts.getopt.GetoptError, inst:
2039 except fancyopts.getopt.GetoptError, inst:
2036 raise ParseError(cmd, inst)
2040 raise ParseError(cmd, inst)
2037
2041
2038 # separate global options back out
2042 # separate global options back out
2039 for o in globalopts:
2043 for o in globalopts:
2040 n = o[1]
2044 n = o[1]
2041 options[n] = cmdoptions[n]
2045 options[n] = cmdoptions[n]
2042 del cmdoptions[n]
2046 del cmdoptions[n]
2043
2047
2044 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2048 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2045
2049
2046 def dispatch(args):
2050 def dispatch(args):
2047 signal.signal(signal.SIGTERM, catchterm)
2051 signal.signal(signal.SIGTERM, catchterm)
2048 try:
2052 try:
2049 signal.signal(signal.SIGHUP, catchterm)
2053 signal.signal(signal.SIGHUP, catchterm)
2050 except AttributeError:
2054 except AttributeError:
2051 pass
2055 pass
2052
2056
2053 u = ui.ui()
2057 u = ui.ui()
2054 external = []
2058 external = []
2055 for x in u.extensions():
2059 for x in u.extensions():
2056 if x[1]:
2060 if x[1]:
2057 try:
2061 try:
2058 mod = imp.load_source(x[0], x[1])
2062 mod = imp.load_source(x[0], x[1])
2059 except:
2063 except:
2060 u.warn("*** failed to import extension %s\n" % x[1])
2064 u.warn("*** failed to import extension %s\n" % x[1])
2061 continue
2065 continue
2062 else:
2066 else:
2063 def importh(name):
2067 def importh(name):
2064 mod = __import__(name)
2068 mod = __import__(name)
2065 components = name.split('.')
2069 components = name.split('.')
2066 for comp in components[1:]:
2070 for comp in components[1:]:
2067 mod = getattr(mod, comp)
2071 mod = getattr(mod, comp)
2068 return mod
2072 return mod
2069 try:
2073 try:
2070 mod = importh(x[0])
2074 mod = importh(x[0])
2071 except:
2075 except:
2072 u.warn("failed to import extension %s\n" % x[0])
2076 u.warn("failed to import extension %s\n" % x[0])
2073 continue
2077 continue
2074
2078
2075 external.append(mod)
2079 external.append(mod)
2076 for x in external:
2080 for x in external:
2077 cmdtable = getattr(x, 'cmdtable', {})
2081 cmdtable = getattr(x, 'cmdtable', {})
2078 for t in cmdtable:
2082 for t in cmdtable:
2079 if t in table:
2083 if t in table:
2080 u.warn("module %s overrides %s\n" % (x.__name__, t))
2084 u.warn("module %s overrides %s\n" % (x.__name__, t))
2081 table.update(cmdtable)
2085 table.update(cmdtable)
2082
2086
2083 try:
2087 try:
2084 cmd, func, args, options, cmdoptions = parse(args)
2088 cmd, func, args, options, cmdoptions = parse(args)
2085 except ParseError, inst:
2089 except ParseError, inst:
2086 if inst.args[0]:
2090 if inst.args[0]:
2087 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
2091 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
2088 help_(u, inst.args[0])
2092 help_(u, inst.args[0])
2089 else:
2093 else:
2090 u.warn("hg: %s\n" % inst.args[1])
2094 u.warn("hg: %s\n" % inst.args[1])
2091 help_(u, 'shortlist')
2095 help_(u, 'shortlist')
2092 sys.exit(-1)
2096 sys.exit(-1)
2093 except UnknownCommand, inst:
2097 except UnknownCommand, inst:
2094 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2098 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2095 help_(u, 'shortlist')
2099 help_(u, 'shortlist')
2096 sys.exit(1)
2100 sys.exit(1)
2097
2101
2098 if options["time"]:
2102 if options["time"]:
2099 def get_times():
2103 def get_times():
2100 t = os.times()
2104 t = os.times()
2101 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2105 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2102 t = (t[0], t[1], t[2], t[3], time.clock())
2106 t = (t[0], t[1], t[2], t[3], time.clock())
2103 return t
2107 return t
2104 s = get_times()
2108 s = get_times()
2105 def print_time():
2109 def print_time():
2106 t = get_times()
2110 t = get_times()
2107 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
2111 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
2108 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2112 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2109 atexit.register(print_time)
2113 atexit.register(print_time)
2110
2114
2111 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2115 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2112 not options["noninteractive"])
2116 not options["noninteractive"])
2113
2117
2114 # enter the debugger before command execution
2118 # enter the debugger before command execution
2115 if options['debugger']:
2119 if options['debugger']:
2116 pdb.set_trace()
2120 pdb.set_trace()
2117
2121
2118 try:
2122 try:
2119 try:
2123 try:
2120 if options['help']:
2124 if options['help']:
2121 help_(u, cmd, options['version'])
2125 help_(u, cmd, options['version'])
2122 sys.exit(0)
2126 sys.exit(0)
2123 elif options['version']:
2127 elif options['version']:
2124 show_version(u)
2128 show_version(u)
2125 sys.exit(0)
2129 sys.exit(0)
2126 elif not cmd:
2130 elif not cmd:
2127 help_(u, 'shortlist')
2131 help_(u, 'shortlist')
2128 sys.exit(0)
2132 sys.exit(0)
2129
2133
2130 if options['cwd']:
2134 if options['cwd']:
2131 try:
2135 try:
2132 os.chdir(options['cwd'])
2136 os.chdir(options['cwd'])
2133 except OSError, inst:
2137 except OSError, inst:
2134 raise util.Abort('%s: %s' %
2138 raise util.Abort('%s: %s' %
2135 (options['cwd'], inst.strerror))
2139 (options['cwd'], inst.strerror))
2136
2140
2137 if cmd not in norepo.split():
2141 if cmd not in norepo.split():
2138 path = options["repository"] or ""
2142 path = options["repository"] or ""
2139 repo = hg.repository(ui=u, path=path)
2143 repo = hg.repository(ui=u, path=path)
2140 for x in external:
2144 for x in external:
2141 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2145 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2142 d = lambda: func(u, repo, *args, **cmdoptions)
2146 d = lambda: func(u, repo, *args, **cmdoptions)
2143 else:
2147 else:
2144 d = lambda: func(u, *args, **cmdoptions)
2148 d = lambda: func(u, *args, **cmdoptions)
2145
2149
2146 if options['profile']:
2150 if options['profile']:
2147 import hotshot, hotshot.stats
2151 import hotshot, hotshot.stats
2148 prof = hotshot.Profile("hg.prof")
2152 prof = hotshot.Profile("hg.prof")
2149 r = prof.runcall(d)
2153 r = prof.runcall(d)
2150 prof.close()
2154 prof.close()
2151 stats = hotshot.stats.load("hg.prof")
2155 stats = hotshot.stats.load("hg.prof")
2152 stats.strip_dirs()
2156 stats.strip_dirs()
2153 stats.sort_stats('time', 'calls')
2157 stats.sort_stats('time', 'calls')
2154 stats.print_stats(40)
2158 stats.print_stats(40)
2155 return r
2159 return r
2156 else:
2160 else:
2157 return d()
2161 return d()
2158 except:
2162 except:
2159 # enter the debugger when we hit an exception
2163 # enter the debugger when we hit an exception
2160 if options['debugger']:
2164 if options['debugger']:
2161 pdb.post_mortem(sys.exc_info()[2])
2165 pdb.post_mortem(sys.exc_info()[2])
2162 if options['traceback']:
2166 if options['traceback']:
2163 traceback.print_exc()
2167 traceback.print_exc()
2164 raise
2168 raise
2165 except hg.RepoError, inst:
2169 except hg.RepoError, inst:
2166 u.warn("abort: ", inst, "!\n")
2170 u.warn("abort: ", inst, "!\n")
2167 except revlog.RevlogError, inst:
2171 except revlog.RevlogError, inst:
2168 u.warn("abort: ", inst, "!\n")
2172 u.warn("abort: ", inst, "!\n")
2169 except SignalInterrupt:
2173 except SignalInterrupt:
2170 u.warn("killed!\n")
2174 u.warn("killed!\n")
2171 except KeyboardInterrupt:
2175 except KeyboardInterrupt:
2172 try:
2176 try:
2173 u.warn("interrupted!\n")
2177 u.warn("interrupted!\n")
2174 except IOError, inst:
2178 except IOError, inst:
2175 if inst.errno == errno.EPIPE:
2179 if inst.errno == errno.EPIPE:
2176 if u.debugflag:
2180 if u.debugflag:
2177 u.warn("\nbroken pipe\n")
2181 u.warn("\nbroken pipe\n")
2178 else:
2182 else:
2179 raise
2183 raise
2180 except IOError, inst:
2184 except IOError, inst:
2181 if hasattr(inst, "code"):
2185 if hasattr(inst, "code"):
2182 u.warn("abort: %s\n" % inst)
2186 u.warn("abort: %s\n" % inst)
2183 elif hasattr(inst, "reason"):
2187 elif hasattr(inst, "reason"):
2184 u.warn("abort: error: %s\n" % inst.reason[1])
2188 u.warn("abort: error: %s\n" % inst.reason[1])
2185 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2189 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2186 if u.debugflag:
2190 if u.debugflag:
2187 u.warn("broken pipe\n")
2191 u.warn("broken pipe\n")
2188 elif getattr(inst, "strerror", None):
2192 elif getattr(inst, "strerror", None):
2189 if getattr(inst, "filename", None):
2193 if getattr(inst, "filename", None):
2190 u.warn("abort: %s - %s\n" % (inst.strerror, inst.filename))
2194 u.warn("abort: %s - %s\n" % (inst.strerror, inst.filename))
2191 else:
2195 else:
2192 u.warn("abort: %s\n" % inst.strerror)
2196 u.warn("abort: %s\n" % inst.strerror)
2193 else:
2197 else:
2194 raise
2198 raise
2195 except OSError, inst:
2199 except OSError, inst:
2196 if hasattr(inst, "filename"):
2200 if hasattr(inst, "filename"):
2197 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
2201 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
2198 else:
2202 else:
2199 u.warn("abort: %s\n" % inst.strerror)
2203 u.warn("abort: %s\n" % inst.strerror)
2200 except util.Abort, inst:
2204 except util.Abort, inst:
2201 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
2205 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
2202 sys.exit(1)
2206 sys.exit(1)
2203 except TypeError, inst:
2207 except TypeError, inst:
2204 # was this an argument error?
2208 # was this an argument error?
2205 tb = traceback.extract_tb(sys.exc_info()[2])
2209 tb = traceback.extract_tb(sys.exc_info()[2])
2206 if len(tb) > 2: # no
2210 if len(tb) > 2: # no
2207 raise
2211 raise
2208 u.debug(inst, "\n")
2212 u.debug(inst, "\n")
2209 u.warn("%s: invalid arguments\n" % cmd)
2213 u.warn("%s: invalid arguments\n" % cmd)
2210 help_(u, cmd)
2214 help_(u, cmd)
2211 except UnknownCommand, inst:
2215 except UnknownCommand, inst:
2212 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2216 u.warn("hg: unknown command '%s'\n" % inst.args[0])
2213 help_(u, 'shortlist')
2217 help_(u, 'shortlist')
2214 except SystemExit:
2218 except SystemExit:
2215 # don't catch this in the catch-all below
2219 # don't catch this in the catch-all below
2216 raise
2220 raise
2217 except:
2221 except:
2218 u.warn("** unknown exception encountered, details follow\n")
2222 u.warn("** unknown exception encountered, details follow\n")
2219 u.warn("** report bug details to mercurial@selenic.com\n")
2223 u.warn("** report bug details to mercurial@selenic.com\n")
2220 raise
2224 raise
2221
2225
2222 sys.exit(-1)
2226 sys.exit(-1)
@@ -1,1566 +1,1569 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 demandload import *
11 from demandload import *
12 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
12 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
13
13
14 class localrepository:
14 class localrepository:
15 def __init__(self, ui, path=None, create=0):
15 def __init__(self, ui, path=None, create=0):
16 if not path:
16 if not path:
17 p = os.getcwd()
17 p = os.getcwd()
18 while not os.path.isdir(os.path.join(p, ".hg")):
18 while not os.path.isdir(os.path.join(p, ".hg")):
19 oldp = p
19 oldp = p
20 p = os.path.dirname(p)
20 p = os.path.dirname(p)
21 if p == oldp: raise repo.RepoError("no repo found")
21 if p == oldp: raise repo.RepoError("no repo found")
22 path = p
22 path = p
23 self.path = os.path.join(path, ".hg")
23 self.path = os.path.join(path, ".hg")
24
24
25 if not create and not os.path.isdir(self.path):
25 if not create and not os.path.isdir(self.path):
26 raise repo.RepoError("repository %s not found" % self.path)
26 raise repo.RepoError("repository %s not found" % self.path)
27
27
28 self.root = os.path.abspath(path)
28 self.root = os.path.abspath(path)
29 self.ui = ui
29 self.ui = ui
30 self.opener = util.opener(self.path)
30 self.opener = util.opener(self.path)
31 self.wopener = util.opener(self.root)
31 self.wopener = util.opener(self.root)
32 self.manifest = manifest.manifest(self.opener)
32 self.manifest = manifest.manifest(self.opener)
33 self.changelog = changelog.changelog(self.opener)
33 self.changelog = changelog.changelog(self.opener)
34 self.tagscache = None
34 self.tagscache = None
35 self.nodetagscache = None
35 self.nodetagscache = None
36 self.encodepats = None
36 self.encodepats = None
37 self.decodepats = None
37 self.decodepats = None
38
38
39 if create:
39 if create:
40 os.mkdir(self.path)
40 os.mkdir(self.path)
41 os.mkdir(self.join("data"))
41 os.mkdir(self.join("data"))
42
42
43 self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
43 self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
44 try:
44 try:
45 self.ui.readconfig(self.opener("hgrc"))
45 self.ui.readconfig(self.opener("hgrc"))
46 except IOError: pass
46 except IOError: pass
47
47
48 def hook(self, name, **args):
48 def hook(self, name, **args):
49 s = self.ui.config("hooks", name)
49 s = self.ui.config("hooks", name)
50 if s:
50 if s:
51 self.ui.note("running hook %s: %s\n" % (name, s))
51 self.ui.note("running hook %s: %s\n" % (name, s))
52 old = {}
52 old = {}
53 for k, v in args.items():
53 for k, v in args.items():
54 k = k.upper()
54 k = k.upper()
55 old[k] = os.environ.get(k, None)
55 old[k] = os.environ.get(k, None)
56 os.environ[k] = v
56 os.environ[k] = v
57
57
58 # Hooks run in the repository root
58 # Hooks run in the repository root
59 olddir = os.getcwd()
59 olddir = os.getcwd()
60 os.chdir(self.root)
60 os.chdir(self.root)
61 r = os.system(s)
61 r = os.system(s)
62 os.chdir(olddir)
62 os.chdir(olddir)
63
63
64 for k, v in old.items():
64 for k, v in old.items():
65 if v != None:
65 if v != None:
66 os.environ[k] = v
66 os.environ[k] = v
67 else:
67 else:
68 del os.environ[k]
68 del os.environ[k]
69
69
70 if r:
70 if r:
71 self.ui.warn("abort: %s hook failed with status %d!\n" %
71 self.ui.warn("abort: %s hook failed with status %d!\n" %
72 (name, r))
72 (name, r))
73 return False
73 return False
74 return True
74 return True
75
75
76 def tags(self):
76 def tags(self):
77 '''return a mapping of tag to node'''
77 '''return a mapping of tag to node'''
78 if not self.tagscache:
78 if not self.tagscache:
79 self.tagscache = {}
79 self.tagscache = {}
80 def addtag(self, k, n):
80 def addtag(self, k, n):
81 try:
81 try:
82 bin_n = bin(n)
82 bin_n = bin(n)
83 except TypeError:
83 except TypeError:
84 bin_n = ''
84 bin_n = ''
85 self.tagscache[k.strip()] = bin_n
85 self.tagscache[k.strip()] = bin_n
86
86
87 try:
87 try:
88 # read each head of the tags file, ending with the tip
88 # read each head of the tags file, ending with the tip
89 # and add each tag found to the map, with "newer" ones
89 # and add each tag found to the map, with "newer" ones
90 # taking precedence
90 # taking precedence
91 fl = self.file(".hgtags")
91 fl = self.file(".hgtags")
92 h = fl.heads()
92 h = fl.heads()
93 h.reverse()
93 h.reverse()
94 for r in h:
94 for r in h:
95 for l in fl.read(r).splitlines():
95 for l in fl.read(r).splitlines():
96 if l:
96 if l:
97 n, k = l.split(" ", 1)
97 n, k = l.split(" ", 1)
98 addtag(self, k, n)
98 addtag(self, k, n)
99 except KeyError:
99 except KeyError:
100 pass
100 pass
101
101
102 try:
102 try:
103 f = self.opener("localtags")
103 f = self.opener("localtags")
104 for l in f:
104 for l in f:
105 n, k = l.split(" ", 1)
105 n, k = l.split(" ", 1)
106 addtag(self, k, n)
106 addtag(self, k, n)
107 except IOError:
107 except IOError:
108 pass
108 pass
109
109
110 self.tagscache['tip'] = self.changelog.tip()
110 self.tagscache['tip'] = self.changelog.tip()
111
111
112 return self.tagscache
112 return self.tagscache
113
113
114 def tagslist(self):
114 def tagslist(self):
115 '''return a list of tags ordered by revision'''
115 '''return a list of tags ordered by revision'''
116 l = []
116 l = []
117 for t, n in self.tags().items():
117 for t, n in self.tags().items():
118 try:
118 try:
119 r = self.changelog.rev(n)
119 r = self.changelog.rev(n)
120 except:
120 except:
121 r = -2 # sort to the beginning of the list if unknown
121 r = -2 # sort to the beginning of the list if unknown
122 l.append((r,t,n))
122 l.append((r,t,n))
123 l.sort()
123 l.sort()
124 return [(t,n) for r,t,n in l]
124 return [(t,n) for r,t,n in l]
125
125
126 def nodetags(self, node):
126 def nodetags(self, node):
127 '''return the tags associated with a node'''
127 '''return the tags associated with a node'''
128 if not self.nodetagscache:
128 if not self.nodetagscache:
129 self.nodetagscache = {}
129 self.nodetagscache = {}
130 for t,n in self.tags().items():
130 for t,n in self.tags().items():
131 self.nodetagscache.setdefault(n,[]).append(t)
131 self.nodetagscache.setdefault(n,[]).append(t)
132 return self.nodetagscache.get(node, [])
132 return self.nodetagscache.get(node, [])
133
133
134 def lookup(self, key):
134 def lookup(self, key):
135 try:
135 try:
136 return self.tags()[key]
136 return self.tags()[key]
137 except KeyError:
137 except KeyError:
138 try:
138 try:
139 return self.changelog.lookup(key)
139 return self.changelog.lookup(key)
140 except:
140 except:
141 raise repo.RepoError("unknown revision '%s'" % key)
141 raise repo.RepoError("unknown revision '%s'" % key)
142
142
143 def dev(self):
143 def dev(self):
144 return os.stat(self.path).st_dev
144 return os.stat(self.path).st_dev
145
145
146 def local(self):
146 def local(self):
147 return True
147 return True
148
148
149 def join(self, f):
149 def join(self, f):
150 return os.path.join(self.path, f)
150 return os.path.join(self.path, f)
151
151
152 def wjoin(self, f):
152 def wjoin(self, f):
153 return os.path.join(self.root, f)
153 return os.path.join(self.root, f)
154
154
155 def file(self, f):
155 def file(self, f):
156 if f[0] == '/': f = f[1:]
156 if f[0] == '/': 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 # save dirstate for undo
202 # save dirstate for undo
203 try:
203 try:
204 ds = self.opener("dirstate").read()
204 ds = self.opener("dirstate").read()
205 except IOError:
205 except IOError:
206 ds = ""
206 ds = ""
207 self.opener("journal.dirstate", "w").write(ds)
207 self.opener("journal.dirstate", "w").write(ds)
208
208
209 def after():
209 def after():
210 util.rename(self.join("journal"), self.join("undo"))
210 util.rename(self.join("journal"), self.join("undo"))
211 util.rename(self.join("journal.dirstate"),
211 util.rename(self.join("journal.dirstate"),
212 self.join("undo.dirstate"))
212 self.join("undo.dirstate"))
213
213
214 return transaction.transaction(self.ui.warn, self.opener,
214 return transaction.transaction(self.ui.warn, self.opener,
215 self.join("journal"), after)
215 self.join("journal"), after)
216
216
217 def recover(self):
217 def recover(self):
218 lock = self.lock()
218 lock = self.lock()
219 if os.path.exists(self.join("journal")):
219 if os.path.exists(self.join("journal")):
220 self.ui.status("rolling back interrupted transaction\n")
220 self.ui.status("rolling back interrupted transaction\n")
221 return transaction.rollback(self.opener, self.join("journal"))
221 return transaction.rollback(self.opener, self.join("journal"))
222 else:
222 else:
223 self.ui.warn("no interrupted transaction available\n")
223 self.ui.warn("no interrupted transaction available\n")
224
224
225 def undo(self):
225 def undo(self):
226 lock = self.lock()
226 lock = self.lock()
227 if os.path.exists(self.join("undo")):
227 if os.path.exists(self.join("undo")):
228 self.ui.status("rolling back last transaction\n")
228 self.ui.status("rolling back last transaction\n")
229 transaction.rollback(self.opener, self.join("undo"))
229 transaction.rollback(self.opener, self.join("undo"))
230 self.dirstate = None
230 self.dirstate = None
231 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
231 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
232 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
232 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
233 else:
233 else:
234 self.ui.warn("no undo information available\n")
234 self.ui.warn("no undo information available\n")
235
235
236 def lock(self, wait=1):
236 def lock(self, wait=1):
237 try:
237 try:
238 return lock.lock(self.join("lock"), 0)
238 return lock.lock(self.join("lock"), 0)
239 except lock.LockHeld, inst:
239 except lock.LockHeld, inst:
240 if wait:
240 if wait:
241 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
241 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
242 return lock.lock(self.join("lock"), wait)
242 return lock.lock(self.join("lock"), wait)
243 raise inst
243 raise inst
244
244
245 def rawcommit(self, files, text, user, date, p1=None, p2=None):
245 def rawcommit(self, files, text, user, date, p1=None, p2=None):
246 orig_parent = self.dirstate.parents()[0] or nullid
246 orig_parent = self.dirstate.parents()[0] or nullid
247 p1 = p1 or self.dirstate.parents()[0] or nullid
247 p1 = p1 or self.dirstate.parents()[0] or nullid
248 p2 = p2 or self.dirstate.parents()[1] or nullid
248 p2 = p2 or self.dirstate.parents()[1] or nullid
249 c1 = self.changelog.read(p1)
249 c1 = self.changelog.read(p1)
250 c2 = self.changelog.read(p2)
250 c2 = self.changelog.read(p2)
251 m1 = self.manifest.read(c1[0])
251 m1 = self.manifest.read(c1[0])
252 mf1 = self.manifest.readflags(c1[0])
252 mf1 = self.manifest.readflags(c1[0])
253 m2 = self.manifest.read(c2[0])
253 m2 = self.manifest.read(c2[0])
254 changed = []
254 changed = []
255
255
256 if orig_parent == p1:
256 if orig_parent == p1:
257 update_dirstate = 1
257 update_dirstate = 1
258 else:
258 else:
259 update_dirstate = 0
259 update_dirstate = 0
260
260
261 tr = self.transaction()
261 tr = self.transaction()
262 mm = m1.copy()
262 mm = m1.copy()
263 mfm = mf1.copy()
263 mfm = mf1.copy()
264 linkrev = self.changelog.count()
264 linkrev = self.changelog.count()
265 for f in files:
265 for f in files:
266 try:
266 try:
267 t = self.wread(f)
267 t = self.wread(f)
268 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
268 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
269 r = self.file(f)
269 r = self.file(f)
270 mfm[f] = tm
270 mfm[f] = tm
271
271
272 fp1 = m1.get(f, nullid)
272 fp1 = m1.get(f, nullid)
273 fp2 = m2.get(f, nullid)
273 fp2 = m2.get(f, nullid)
274
274
275 # is the same revision on two branches of a merge?
275 # is the same revision on two branches of a merge?
276 if fp2 == fp1:
276 if fp2 == fp1:
277 fp2 = nullid
277 fp2 = nullid
278
278
279 if fp2 != nullid:
279 if fp2 != nullid:
280 # is one parent an ancestor of the other?
280 # is one parent an ancestor of the other?
281 fpa = r.ancestor(fp1, fp2)
281 fpa = r.ancestor(fp1, fp2)
282 if fpa == fp1:
282 if fpa == fp1:
283 fp1, fp2 = fp2, nullid
283 fp1, fp2 = fp2, nullid
284 elif fpa == fp2:
284 elif fpa == fp2:
285 fp2 = nullid
285 fp2 = nullid
286
286
287 # is the file unmodified from the parent?
287 # is the file unmodified from the parent?
288 if t == r.read(fp1):
288 if t == r.read(fp1):
289 # record the proper existing parent in manifest
289 # record the proper existing parent in manifest
290 # no need to add a revision
290 # no need to add a revision
291 mm[f] = fp1
291 mm[f] = fp1
292 continue
292 continue
293
293
294 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
294 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
295 changed.append(f)
295 changed.append(f)
296 if update_dirstate:
296 if update_dirstate:
297 self.dirstate.update([f], "n")
297 self.dirstate.update([f], "n")
298 except IOError:
298 except IOError:
299 try:
299 try:
300 del mm[f]
300 del mm[f]
301 del mfm[f]
301 del mfm[f]
302 if update_dirstate:
302 if update_dirstate:
303 self.dirstate.forget([f])
303 self.dirstate.forget([f])
304 except:
304 except:
305 # deleted from p2?
305 # deleted from p2?
306 pass
306 pass
307
307
308 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
308 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
309 user = user or self.ui.username()
309 user = user or self.ui.username()
310 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
310 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
311 tr.close()
311 tr.close()
312 if update_dirstate:
312 if update_dirstate:
313 self.dirstate.setparents(n, nullid)
313 self.dirstate.setparents(n, nullid)
314
314
315 def commit(self, files = None, text = "", user = None, date = None,
315 def commit(self, files = None, text = "", user = None, date = None,
316 match = util.always, force=False):
316 match = util.always, force=False):
317 commit = []
317 commit = []
318 remove = []
318 remove = []
319 changed = []
319 changed = []
320
320
321 if files:
321 if files:
322 for f in files:
322 for f in files:
323 s = self.dirstate.state(f)
323 s = self.dirstate.state(f)
324 if s in 'nmai':
324 if s in 'nmai':
325 commit.append(f)
325 commit.append(f)
326 elif s == 'r':
326 elif s == 'r':
327 remove.append(f)
327 remove.append(f)
328 else:
328 else:
329 self.ui.warn("%s not tracked!\n" % f)
329 self.ui.warn("%s not tracked!\n" % f)
330 else:
330 else:
331 (c, a, d, u) = self.changes(match=match)
331 (c, a, d, u) = self.changes(match=match)
332 commit = c + a
332 commit = c + a
333 remove = d
333 remove = d
334
334
335 p1, p2 = self.dirstate.parents()
335 p1, p2 = self.dirstate.parents()
336 c1 = self.changelog.read(p1)
336 c1 = self.changelog.read(p1)
337 c2 = self.changelog.read(p2)
337 c2 = self.changelog.read(p2)
338 m1 = self.manifest.read(c1[0])
338 m1 = self.manifest.read(c1[0])
339 mf1 = self.manifest.readflags(c1[0])
339 mf1 = self.manifest.readflags(c1[0])
340 m2 = self.manifest.read(c2[0])
340 m2 = self.manifest.read(c2[0])
341
341
342 if not commit and not remove and not force and p2 == nullid:
342 if not commit and not remove and not force and p2 == nullid:
343 self.ui.status("nothing changed\n")
343 self.ui.status("nothing changed\n")
344 return None
344 return None
345
345
346 if not self.hook("precommit"):
346 if not self.hook("precommit"):
347 return None
347 return None
348
348
349 lock = self.lock()
349 lock = self.lock()
350 tr = self.transaction()
350 tr = self.transaction()
351
351
352 # check in files
352 # check in files
353 new = {}
353 new = {}
354 linkrev = self.changelog.count()
354 linkrev = self.changelog.count()
355 commit.sort()
355 commit.sort()
356 for f in commit:
356 for f in commit:
357 self.ui.note(f + "\n")
357 self.ui.note(f + "\n")
358 try:
358 try:
359 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
359 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
360 t = self.wread(f)
360 t = self.wread(f)
361 except IOError:
361 except IOError:
362 self.ui.warn("trouble committing %s!\n" % f)
362 self.ui.warn("trouble committing %s!\n" % f)
363 raise
363 raise
364
364
365 r = self.file(f)
365 r = self.file(f)
366
366
367 meta = {}
367 meta = {}
368 cp = self.dirstate.copied(f)
368 cp = self.dirstate.copied(f)
369 if cp:
369 if cp:
370 meta["copy"] = cp
370 meta["copy"] = cp
371 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
371 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
372 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
372 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
373 fp1, fp2 = nullid, nullid
373 fp1, fp2 = nullid, nullid
374 else:
374 else:
375 fp1 = m1.get(f, nullid)
375 fp1 = m1.get(f, nullid)
376 fp2 = m2.get(f, nullid)
376 fp2 = m2.get(f, nullid)
377
377
378 # is the same revision on two branches of a merge?
378 # is the same revision on two branches of a merge?
379 if fp2 == fp1:
379 if fp2 == fp1:
380 fp2 = nullid
380 fp2 = nullid
381
381
382 if fp2 != nullid:
382 if fp2 != nullid:
383 # is one parent an ancestor of the other?
383 # is one parent an ancestor of the other?
384 fpa = r.ancestor(fp1, fp2)
384 fpa = r.ancestor(fp1, fp2)
385 if fpa == fp1:
385 if fpa == fp1:
386 fp1, fp2 = fp2, nullid
386 fp1, fp2 = fp2, nullid
387 elif fpa == fp2:
387 elif fpa == fp2:
388 fp2 = nullid
388 fp2 = nullid
389
389
390 # is the file unmodified from the parent?
390 # is the file unmodified from the parent?
391 if not meta and t == r.read(fp1):
391 if not meta and t == r.read(fp1):
392 # record the proper existing parent in manifest
392 # record the proper existing parent in manifest
393 # no need to add a revision
393 # no need to add a revision
394 new[f] = fp1
394 new[f] = fp1
395 continue
395 continue
396
396
397 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
397 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
398 # remember what we've added so that we can later calculate
398 # remember what we've added so that we can later calculate
399 # the files to pull from a set of changesets
399 # the files to pull from a set of changesets
400 changed.append(f)
400 changed.append(f)
401
401
402 # update manifest
402 # update manifest
403 m1.update(new)
403 m1.update(new)
404 for f in remove:
404 for f in remove:
405 if f in m1:
405 if f in m1:
406 del m1[f]
406 del m1[f]
407 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
407 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
408 (new, remove))
408 (new, remove))
409
409
410 # add changeset
410 # add changeset
411 new = new.keys()
411 new = new.keys()
412 new.sort()
412 new.sort()
413
413
414 if not text:
414 if not text:
415 edittext = ""
415 edittext = ""
416 if p2 != nullid:
416 if p2 != nullid:
417 edittext += "HG: branch merge\n"
417 edittext += "HG: branch merge\n"
418 edittext += "\n" + "HG: manifest hash %s\n" % hex(mn)
418 edittext += "\n" + "HG: manifest hash %s\n" % hex(mn)
419 edittext += "".join(["HG: changed %s\n" % f for f in changed])
419 edittext += "".join(["HG: changed %s\n" % f for f in changed])
420 edittext += "".join(["HG: removed %s\n" % f for f in remove])
420 edittext += "".join(["HG: removed %s\n" % f for f in remove])
421 if not changed and not remove:
421 if not changed and not remove:
422 edittext += "HG: no files changed\n"
422 edittext += "HG: no files changed\n"
423 edittext = self.ui.edit(edittext)
423 edittext = self.ui.edit(edittext)
424 if not edittext.rstrip():
424 if not edittext.rstrip():
425 return None
425 return None
426 text = edittext
426 text = edittext
427
427
428 user = user or self.ui.username()
428 user = user or self.ui.username()
429 n = self.changelog.add(mn, changed, text, tr, p1, p2, user, date)
429 n = self.changelog.add(mn, changed, text, tr, p1, p2, user, date)
430 tr.close()
430 tr.close()
431
431
432 self.dirstate.setparents(n)
432 self.dirstate.setparents(n)
433 self.dirstate.update(new, "n")
433 self.dirstate.update(new, "n")
434 self.dirstate.forget(remove)
434 self.dirstate.forget(remove)
435
435
436 if not self.hook("commit", node=hex(n)):
436 if not self.hook("commit", node=hex(n)):
437 return None
437 return None
438 return n
438 return n
439
439
440 def walk(self, node=None, files=[], match=util.always):
440 def walk(self, node=None, files=[], match=util.always):
441 if node:
441 if node:
442 for fn in self.manifest.read(self.changelog.read(node)[0]):
442 for fn in self.manifest.read(self.changelog.read(node)[0]):
443 if match(fn): yield 'm', fn
443 if match(fn): yield 'm', fn
444 else:
444 else:
445 for src, fn in self.dirstate.walk(files, match):
445 for src, fn in self.dirstate.walk(files, match):
446 yield src, fn
446 yield src, fn
447
447
448 def changes(self, node1 = None, node2 = None, files = [],
448 def changes(self, node1 = None, node2 = None, files = [],
449 match = util.always):
449 match = util.always):
450 mf2, u = None, []
450 mf2, u = None, []
451
451
452 def fcmp(fn, mf):
452 def fcmp(fn, mf):
453 t1 = self.wread(fn)
453 t1 = self.wread(fn)
454 t2 = self.file(fn).read(mf.get(fn, nullid))
454 t2 = self.file(fn).read(mf.get(fn, nullid))
455 return cmp(t1, t2)
455 return cmp(t1, t2)
456
456
457 def mfmatches(node):
457 def mfmatches(node):
458 mf = dict(self.manifest.read(node))
458 mf = dict(self.manifest.read(node))
459 for fn in mf.keys():
459 for fn in mf.keys():
460 if not match(fn):
460 if not match(fn):
461 del mf[fn]
461 del mf[fn]
462 return mf
462 return mf
463
463
464 # are we comparing the working directory?
464 # are we comparing the working directory?
465 if not node2:
465 if not node2:
466 l, c, a, d, u = self.dirstate.changes(files, match)
466 l, c, a, d, u = self.dirstate.changes(files, match)
467
467
468 # are we comparing working dir against its parent?
468 # are we comparing working dir against its parent?
469 if not node1:
469 if not node1:
470 if l:
470 if l:
471 # do a full compare of any files that might have changed
471 # do a full compare of any files that might have changed
472 change = self.changelog.read(self.dirstate.parents()[0])
472 change = self.changelog.read(self.dirstate.parents()[0])
473 mf2 = mfmatches(change[0])
473 mf2 = mfmatches(change[0])
474 for f in l:
474 for f in l:
475 if fcmp(f, mf2):
475 if fcmp(f, mf2):
476 c.append(f)
476 c.append(f)
477
477
478 for l in c, a, d, u:
478 for l in c, a, d, u:
479 l.sort()
479 l.sort()
480
480
481 return (c, a, d, u)
481 return (c, a, d, u)
482
482
483 # are we comparing working dir against non-tip?
483 # are we comparing working dir against non-tip?
484 # generate a pseudo-manifest for the working dir
484 # generate a pseudo-manifest for the working dir
485 if not node2:
485 if not node2:
486 if not mf2:
486 if not mf2:
487 change = self.changelog.read(self.dirstate.parents()[0])
487 change = self.changelog.read(self.dirstate.parents()[0])
488 mf2 = mfmatches(change[0])
488 mf2 = mfmatches(change[0])
489 for f in a + c + l:
489 for f in a + c + l:
490 mf2[f] = ""
490 mf2[f] = ""
491 for f in d:
491 for f in d:
492 if f in mf2: del mf2[f]
492 if f in mf2: del mf2[f]
493 else:
493 else:
494 change = self.changelog.read(node2)
494 change = self.changelog.read(node2)
495 mf2 = mfmatches(change[0])
495 mf2 = mfmatches(change[0])
496
496
497 # flush lists from dirstate before comparing manifests
497 # flush lists from dirstate before comparing manifests
498 c, a = [], []
498 c, a = [], []
499
499
500 change = self.changelog.read(node1)
500 change = self.changelog.read(node1)
501 mf1 = mfmatches(change[0])
501 mf1 = mfmatches(change[0])
502
502
503 for fn in mf2:
503 for fn in mf2:
504 if mf1.has_key(fn):
504 if mf1.has_key(fn):
505 if mf1[fn] != mf2[fn]:
505 if mf1[fn] != mf2[fn]:
506 if mf2[fn] != "" or fcmp(fn, mf1):
506 if mf2[fn] != "" or fcmp(fn, mf1):
507 c.append(fn)
507 c.append(fn)
508 del mf1[fn]
508 del mf1[fn]
509 else:
509 else:
510 a.append(fn)
510 a.append(fn)
511
511
512 d = mf1.keys()
512 d = mf1.keys()
513
513
514 for l in c, a, d, u:
514 for l in c, a, d, u:
515 l.sort()
515 l.sort()
516
516
517 return (c, a, d, u)
517 return (c, a, d, u)
518
518
519 def add(self, list):
519 def add(self, list):
520 for f in list:
520 for f in list:
521 p = self.wjoin(f)
521 p = self.wjoin(f)
522 if not os.path.exists(p):
522 if not os.path.exists(p):
523 self.ui.warn("%s does not exist!\n" % f)
523 self.ui.warn("%s does not exist!\n" % f)
524 elif not os.path.isfile(p):
524 elif not os.path.isfile(p):
525 self.ui.warn("%s not added: only files supported currently\n" % f)
525 self.ui.warn("%s not added: only files supported currently\n" % f)
526 elif self.dirstate.state(f) in 'an':
526 elif self.dirstate.state(f) in 'an':
527 self.ui.warn("%s already tracked!\n" % f)
527 self.ui.warn("%s already tracked!\n" % f)
528 else:
528 else:
529 self.dirstate.update([f], "a")
529 self.dirstate.update([f], "a")
530
530
531 def forget(self, list):
531 def forget(self, list):
532 for f in list:
532 for f in list:
533 if self.dirstate.state(f) not in 'ai':
533 if self.dirstate.state(f) not in 'ai':
534 self.ui.warn("%s not added!\n" % f)
534 self.ui.warn("%s not added!\n" % f)
535 else:
535 else:
536 self.dirstate.forget([f])
536 self.dirstate.forget([f])
537
537
538 def remove(self, list):
538 def remove(self, list):
539 for f in list:
539 for f in list:
540 p = self.wjoin(f)
540 p = self.wjoin(f)
541 if os.path.exists(p):
541 if os.path.exists(p):
542 self.ui.warn("%s still exists!\n" % f)
542 self.ui.warn("%s still exists!\n" % f)
543 elif self.dirstate.state(f) == 'a':
543 elif self.dirstate.state(f) == 'a':
544 self.ui.warn("%s never committed!\n" % f)
544 self.ui.warn("%s never committed!\n" % f)
545 self.dirstate.forget([f])
545 self.dirstate.forget([f])
546 elif f not in self.dirstate:
546 elif f not in self.dirstate:
547 self.ui.warn("%s not tracked!\n" % f)
547 self.ui.warn("%s not tracked!\n" % f)
548 else:
548 else:
549 self.dirstate.update([f], "r")
549 self.dirstate.update([f], "r")
550
550
551 def copy(self, source, dest):
551 def copy(self, source, dest):
552 p = self.wjoin(dest)
552 p = self.wjoin(dest)
553 if not os.path.exists(p):
553 if not os.path.exists(p):
554 self.ui.warn("%s does not exist!\n" % dest)
554 self.ui.warn("%s does not exist!\n" % dest)
555 elif not os.path.isfile(p):
555 elif not os.path.isfile(p):
556 self.ui.warn("copy failed: %s is not a file\n" % dest)
556 self.ui.warn("copy failed: %s is not a file\n" % dest)
557 else:
557 else:
558 if self.dirstate.state(dest) == '?':
558 if self.dirstate.state(dest) == '?':
559 self.dirstate.update([dest], "a")
559 self.dirstate.update([dest], "a")
560 self.dirstate.copy(source, dest)
560 self.dirstate.copy(source, dest)
561
561
562 def heads(self):
562 def heads(self):
563 return self.changelog.heads()
563 return self.changelog.heads()
564
564
565 # branchlookup returns a dict giving a list of branches for
565 # branchlookup returns a dict giving a list of branches for
566 # each head. A branch is defined as the tag of a node or
566 # each head. A branch is defined as the tag of a node or
567 # the branch of the node's parents. If a node has multiple
567 # the branch of the node's parents. If a node has multiple
568 # branch tags, tags are eliminated if they are visible from other
568 # branch tags, tags are eliminated if they are visible from other
569 # branch tags.
569 # branch tags.
570 #
570 #
571 # So, for this graph: a->b->c->d->e
571 # So, for this graph: a->b->c->d->e
572 # \ /
572 # \ /
573 # aa -----/
573 # aa -----/
574 # a has tag 2.6.12
574 # a has tag 2.6.12
575 # d has tag 2.6.13
575 # d has tag 2.6.13
576 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
576 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
577 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
577 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
578 # from the list.
578 # from the list.
579 #
579 #
580 # It is possible that more than one head will have the same branch tag.
580 # It is possible that more than one head will have the same branch tag.
581 # callers need to check the result for multiple heads under the same
581 # callers need to check the result for multiple heads under the same
582 # branch tag if that is a problem for them (ie checkout of a specific
582 # branch tag if that is a problem for them (ie checkout of a specific
583 # branch).
583 # branch).
584 #
584 #
585 # passing in a specific branch will limit the depth of the search
585 # passing in a specific branch will limit the depth of the search
586 # through the parents. It won't limit the branches returned in the
586 # through the parents. It won't limit the branches returned in the
587 # result though.
587 # result though.
588 def branchlookup(self, heads=None, branch=None):
588 def branchlookup(self, heads=None, branch=None):
589 if not heads:
589 if not heads:
590 heads = self.heads()
590 heads = self.heads()
591 headt = [ h for h in heads ]
591 headt = [ h for h in heads ]
592 chlog = self.changelog
592 chlog = self.changelog
593 branches = {}
593 branches = {}
594 merges = []
594 merges = []
595 seenmerge = {}
595 seenmerge = {}
596
596
597 # traverse the tree once for each head, recording in the branches
597 # traverse the tree once for each head, recording in the branches
598 # dict which tags are visible from this head. The branches
598 # dict which tags are visible from this head. The branches
599 # dict also records which tags are visible from each tag
599 # dict also records which tags are visible from each tag
600 # while we traverse.
600 # while we traverse.
601 while headt or merges:
601 while headt or merges:
602 if merges:
602 if merges:
603 n, found = merges.pop()
603 n, found = merges.pop()
604 visit = [n]
604 visit = [n]
605 else:
605 else:
606 h = headt.pop()
606 h = headt.pop()
607 visit = [h]
607 visit = [h]
608 found = [h]
608 found = [h]
609 seen = {}
609 seen = {}
610 while visit:
610 while visit:
611 n = visit.pop()
611 n = visit.pop()
612 if n in seen:
612 if n in seen:
613 continue
613 continue
614 pp = chlog.parents(n)
614 pp = chlog.parents(n)
615 tags = self.nodetags(n)
615 tags = self.nodetags(n)
616 if tags:
616 if tags:
617 for x in tags:
617 for x in tags:
618 if x == 'tip':
618 if x == 'tip':
619 continue
619 continue
620 for f in found:
620 for f in found:
621 branches.setdefault(f, {})[n] = 1
621 branches.setdefault(f, {})[n] = 1
622 branches.setdefault(n, {})[n] = 1
622 branches.setdefault(n, {})[n] = 1
623 break
623 break
624 if n not in found:
624 if n not in found:
625 found.append(n)
625 found.append(n)
626 if branch in tags:
626 if branch in tags:
627 continue
627 continue
628 seen[n] = 1
628 seen[n] = 1
629 if pp[1] != nullid and n not in seenmerge:
629 if pp[1] != nullid and n not in seenmerge:
630 merges.append((pp[1], [x for x in found]))
630 merges.append((pp[1], [x for x in found]))
631 seenmerge[n] = 1
631 seenmerge[n] = 1
632 if pp[0] != nullid:
632 if pp[0] != nullid:
633 visit.append(pp[0])
633 visit.append(pp[0])
634 # traverse the branches dict, eliminating branch tags from each
634 # traverse the branches dict, eliminating branch tags from each
635 # head that are visible from another branch tag for that head.
635 # head that are visible from another branch tag for that head.
636 out = {}
636 out = {}
637 viscache = {}
637 viscache = {}
638 for h in heads:
638 for h in heads:
639 def visible(node):
639 def visible(node):
640 if node in viscache:
640 if node in viscache:
641 return viscache[node]
641 return viscache[node]
642 ret = {}
642 ret = {}
643 visit = [node]
643 visit = [node]
644 while visit:
644 while visit:
645 x = visit.pop()
645 x = visit.pop()
646 if x in viscache:
646 if x in viscache:
647 ret.update(viscache[x])
647 ret.update(viscache[x])
648 elif x not in ret:
648 elif x not in ret:
649 ret[x] = 1
649 ret[x] = 1
650 if x in branches:
650 if x in branches:
651 visit[len(visit):] = branches[x].keys()
651 visit[len(visit):] = branches[x].keys()
652 viscache[node] = ret
652 viscache[node] = ret
653 return ret
653 return ret
654 if h not in branches:
654 if h not in branches:
655 continue
655 continue
656 # O(n^2), but somewhat limited. This only searches the
656 # O(n^2), but somewhat limited. This only searches the
657 # tags visible from a specific head, not all the tags in the
657 # tags visible from a specific head, not all the tags in the
658 # whole repo.
658 # whole repo.
659 for b in branches[h]:
659 for b in branches[h]:
660 vis = False
660 vis = False
661 for bb in branches[h].keys():
661 for bb in branches[h].keys():
662 if b != bb:
662 if b != bb:
663 if b in visible(bb):
663 if b in visible(bb):
664 vis = True
664 vis = True
665 break
665 break
666 if not vis:
666 if not vis:
667 l = out.setdefault(h, [])
667 l = out.setdefault(h, [])
668 l[len(l):] = self.nodetags(b)
668 l[len(l):] = self.nodetags(b)
669 return out
669 return out
670
670
671 def branches(self, nodes):
671 def branches(self, nodes):
672 if not nodes: nodes = [self.changelog.tip()]
672 if not nodes: nodes = [self.changelog.tip()]
673 b = []
673 b = []
674 for n in nodes:
674 for n in nodes:
675 t = n
675 t = n
676 while n:
676 while n:
677 p = self.changelog.parents(n)
677 p = self.changelog.parents(n)
678 if p[1] != nullid or p[0] == nullid:
678 if p[1] != nullid or p[0] == nullid:
679 b.append((t, n, p[0], p[1]))
679 b.append((t, n, p[0], p[1]))
680 break
680 break
681 n = p[0]
681 n = p[0]
682 return b
682 return b
683
683
684 def between(self, pairs):
684 def between(self, pairs):
685 r = []
685 r = []
686
686
687 for top, bottom in pairs:
687 for top, bottom in pairs:
688 n, l, i = top, [], 0
688 n, l, i = top, [], 0
689 f = 1
689 f = 1
690
690
691 while n != bottom:
691 while n != bottom:
692 p = self.changelog.parents(n)[0]
692 p = self.changelog.parents(n)[0]
693 if i == f:
693 if i == f:
694 l.append(n)
694 l.append(n)
695 f = f * 2
695 f = f * 2
696 n = p
696 n = p
697 i += 1
697 i += 1
698
698
699 r.append(l)
699 r.append(l)
700
700
701 return r
701 return r
702
702
703 def findincoming(self, remote, base=None, heads=None):
703 def findincoming(self, remote, base=None, heads=None):
704 m = self.changelog.nodemap
704 m = self.changelog.nodemap
705 search = []
705 search = []
706 fetch = {}
706 fetch = {}
707 seen = {}
707 seen = {}
708 seenbranch = {}
708 seenbranch = {}
709 if base == None:
709 if base == None:
710 base = {}
710 base = {}
711
711
712 # assume we're closer to the tip than the root
712 # assume we're closer to the tip than the root
713 # and start by examining the heads
713 # and start by examining the heads
714 self.ui.status("searching for changes\n")
714 self.ui.status("searching for changes\n")
715
715
716 if not heads:
716 if not heads:
717 heads = remote.heads()
717 heads = remote.heads()
718
718
719 unknown = []
719 unknown = []
720 for h in heads:
720 for h in heads:
721 if h not in m:
721 if h not in m:
722 unknown.append(h)
722 unknown.append(h)
723 else:
723 else:
724 base[h] = 1
724 base[h] = 1
725
725
726 if not unknown:
726 if not unknown:
727 return None
727 return None
728
728
729 rep = {}
729 rep = {}
730 reqcnt = 0
730 reqcnt = 0
731
731
732 # search through remote branches
732 # search through remote branches
733 # a 'branch' here is a linear segment of history, with four parts:
733 # a 'branch' here is a linear segment of history, with four parts:
734 # head, root, first parent, second parent
734 # head, root, first parent, second parent
735 # (a branch always has two parents (or none) by definition)
735 # (a branch always has two parents (or none) by definition)
736 unknown = remote.branches(unknown)
736 unknown = remote.branches(unknown)
737 while unknown:
737 while unknown:
738 r = []
738 r = []
739 while unknown:
739 while unknown:
740 n = unknown.pop(0)
740 n = unknown.pop(0)
741 if n[0] in seen:
741 if n[0] in seen:
742 continue
742 continue
743
743
744 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
744 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
745 if n[0] == nullid:
745 if n[0] == nullid:
746 break
746 break
747 if n in seenbranch:
747 if n in seenbranch:
748 self.ui.debug("branch already found\n")
748 self.ui.debug("branch already found\n")
749 continue
749 continue
750 if n[1] and n[1] in m: # do we know the base?
750 if n[1] and n[1] in m: # do we know the base?
751 self.ui.debug("found incomplete branch %s:%s\n"
751 self.ui.debug("found incomplete branch %s:%s\n"
752 % (short(n[0]), short(n[1])))
752 % (short(n[0]), short(n[1])))
753 search.append(n) # schedule branch range for scanning
753 search.append(n) # schedule branch range for scanning
754 seenbranch[n] = 1
754 seenbranch[n] = 1
755 else:
755 else:
756 if n[1] not in seen and n[1] not in fetch:
756 if n[1] not in seen and n[1] not in fetch:
757 if n[2] in m and n[3] in m:
757 if n[2] in m and n[3] in m:
758 self.ui.debug("found new changeset %s\n" %
758 self.ui.debug("found new changeset %s\n" %
759 short(n[1]))
759 short(n[1]))
760 fetch[n[1]] = 1 # earliest unknown
760 fetch[n[1]] = 1 # earliest unknown
761 base[n[2]] = 1 # latest known
761 base[n[2]] = 1 # latest known
762 continue
762 continue
763
763
764 for a in n[2:4]:
764 for a in n[2:4]:
765 if a not in rep:
765 if a not in rep:
766 r.append(a)
766 r.append(a)
767 rep[a] = 1
767 rep[a] = 1
768
768
769 seen[n[0]] = 1
769 seen[n[0]] = 1
770
770
771 if r:
771 if r:
772 reqcnt += 1
772 reqcnt += 1
773 self.ui.debug("request %d: %s\n" %
773 self.ui.debug("request %d: %s\n" %
774 (reqcnt, " ".join(map(short, r))))
774 (reqcnt, " ".join(map(short, r))))
775 for p in range(0, len(r), 10):
775 for p in range(0, len(r), 10):
776 for b in remote.branches(r[p:p+10]):
776 for b in remote.branches(r[p:p+10]):
777 self.ui.debug("received %s:%s\n" %
777 self.ui.debug("received %s:%s\n" %
778 (short(b[0]), short(b[1])))
778 (short(b[0]), short(b[1])))
779 if b[0] in m:
779 if b[0] in m:
780 self.ui.debug("found base node %s\n" % short(b[0]))
780 self.ui.debug("found base node %s\n" % short(b[0]))
781 base[b[0]] = 1
781 base[b[0]] = 1
782 elif b[0] not in seen:
782 elif b[0] not in seen:
783 unknown.append(b)
783 unknown.append(b)
784
784
785 # do binary search on the branches we found
785 # do binary search on the branches we found
786 while search:
786 while search:
787 n = search.pop(0)
787 n = search.pop(0)
788 reqcnt += 1
788 reqcnt += 1
789 l = remote.between([(n[0], n[1])])[0]
789 l = remote.between([(n[0], n[1])])[0]
790 l.append(n[1])
790 l.append(n[1])
791 p = n[0]
791 p = n[0]
792 f = 1
792 f = 1
793 for i in l:
793 for i in l:
794 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
794 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
795 if i in m:
795 if i in m:
796 if f <= 2:
796 if f <= 2:
797 self.ui.debug("found new branch changeset %s\n" %
797 self.ui.debug("found new branch changeset %s\n" %
798 short(p))
798 short(p))
799 fetch[p] = 1
799 fetch[p] = 1
800 base[i] = 1
800 base[i] = 1
801 else:
801 else:
802 self.ui.debug("narrowed branch search to %s:%s\n"
802 self.ui.debug("narrowed branch search to %s:%s\n"
803 % (short(p), short(i)))
803 % (short(p), short(i)))
804 search.append((p, i))
804 search.append((p, i))
805 break
805 break
806 p, f = i, f * 2
806 p, f = i, f * 2
807
807
808 # sanity check our fetch list
808 # sanity check our fetch list
809 for f in fetch.keys():
809 for f in fetch.keys():
810 if f in m:
810 if f in m:
811 raise repo.RepoError("already have changeset " + short(f[:4]))
811 raise repo.RepoError("already have changeset " + short(f[:4]))
812
812
813 if base.keys() == [nullid]:
813 if base.keys() == [nullid]:
814 self.ui.warn("warning: pulling from an unrelated repository!\n")
814 self.ui.warn("warning: pulling from an unrelated repository!\n")
815
815
816 self.ui.note("found new changesets starting at " +
816 self.ui.note("found new changesets starting at " +
817 " ".join([short(f) for f in fetch]) + "\n")
817 " ".join([short(f) for f in fetch]) + "\n")
818
818
819 self.ui.debug("%d total queries\n" % reqcnt)
819 self.ui.debug("%d total queries\n" % reqcnt)
820
820
821 return fetch.keys()
821 return fetch.keys()
822
822
823 def findoutgoing(self, remote, base=None, heads=None):
823 def findoutgoing(self, remote, base=None, heads=None):
824 if base == None:
824 if base == None:
825 base = {}
825 base = {}
826 self.findincoming(remote, base, heads)
826 self.findincoming(remote, base, heads)
827
827
828 self.ui.debug("common changesets up to "
828 self.ui.debug("common changesets up to "
829 + " ".join(map(short, base.keys())) + "\n")
829 + " ".join(map(short, base.keys())) + "\n")
830
830
831 remain = dict.fromkeys(self.changelog.nodemap)
831 remain = dict.fromkeys(self.changelog.nodemap)
832
832
833 # prune everything remote has from the tree
833 # prune everything remote has from the tree
834 del remain[nullid]
834 del remain[nullid]
835 remove = base.keys()
835 remove = base.keys()
836 while remove:
836 while remove:
837 n = remove.pop(0)
837 n = remove.pop(0)
838 if n in remain:
838 if n in remain:
839 del remain[n]
839 del remain[n]
840 for p in self.changelog.parents(n):
840 for p in self.changelog.parents(n):
841 remove.append(p)
841 remove.append(p)
842
842
843 # find every node whose parents have been pruned
843 # find every node whose parents have been pruned
844 subset = []
844 subset = []
845 for n in remain:
845 for n in remain:
846 p1, p2 = self.changelog.parents(n)
846 p1, p2 = self.changelog.parents(n)
847 if p1 not in remain and p2 not in remain:
847 if p1 not in remain and p2 not in remain:
848 subset.append(n)
848 subset.append(n)
849
849
850 # this is the set of all roots we have to push
850 # this is the set of all roots we have to push
851 return subset
851 return subset
852
852
853 def pull(self, remote):
853 def pull(self, remote, heads = None):
854 lock = self.lock()
854 lock = self.lock()
855
855
856 # if we have an empty repo, fetch everything
856 # if we have an empty repo, fetch everything
857 if self.changelog.tip() == nullid:
857 if self.changelog.tip() == nullid:
858 self.ui.status("requesting all changes\n")
858 self.ui.status("requesting all changes\n")
859 fetch = [nullid]
859 fetch = [nullid]
860 else:
860 else:
861 fetch = self.findincoming(remote)
861 fetch = self.findincoming(remote)
862
862
863 if not fetch:
863 if not fetch:
864 self.ui.status("no changes found\n")
864 self.ui.status("no changes found\n")
865 return 1
865 return 1
866
866
867 cg = remote.changegroup(fetch)
867 if heads is None:
868 cg = remote.changegroup(fetch)
869 else:
870 cg = remote.changegroupsubset(fetch, heads)
868 return self.addchangegroup(cg)
871 return self.addchangegroup(cg)
869
872
870 def push(self, remote, force=False):
873 def push(self, remote, force=False):
871 lock = remote.lock()
874 lock = remote.lock()
872
875
873 base = {}
876 base = {}
874 heads = remote.heads()
877 heads = remote.heads()
875 inc = self.findincoming(remote, base, heads)
878 inc = self.findincoming(remote, base, heads)
876 if not force and inc:
879 if not force and inc:
877 self.ui.warn("abort: unsynced remote changes!\n")
880 self.ui.warn("abort: unsynced remote changes!\n")
878 self.ui.status("(did you forget to sync? use push -f to force)\n")
881 self.ui.status("(did you forget to sync? use push -f to force)\n")
879 return 1
882 return 1
880
883
881 update = self.findoutgoing(remote, base)
884 update = self.findoutgoing(remote, base)
882 if not update:
885 if not update:
883 self.ui.status("no changes found\n")
886 self.ui.status("no changes found\n")
884 return 1
887 return 1
885 elif not force:
888 elif not force:
886 if len(heads) < len(self.changelog.heads()):
889 if len(heads) < len(self.changelog.heads()):
887 self.ui.warn("abort: push creates new remote branches!\n")
890 self.ui.warn("abort: push creates new remote branches!\n")
888 self.ui.status("(did you forget to merge?" +
891 self.ui.status("(did you forget to merge?" +
889 " use push -f to force)\n")
892 " use push -f to force)\n")
890 return 1
893 return 1
891
894
892 cg = self.changegroup(update)
895 cg = self.changegroup(update)
893 return remote.addchangegroup(cg)
896 return remote.addchangegroup(cg)
894
897
895 def changegroupsubset(self, bases, heads):
898 def changegroupsubset(self, bases, heads):
896 cl = self.changelog
899 cl = self.changelog
897 # msng = missing
900 # msng = missing
898 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
901 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
899 junk = None
902 junk = None
900 knownheads = {}
903 knownheads = {}
901 for n in bases:
904 for n in bases:
902 for p in cl.parents(n):
905 for p in cl.parents(n):
903 if p != nullid:
906 if p != nullid:
904 knownheads[p] = 1
907 knownheads[p] = 1
905 knownheads = knownheads.keys()
908 knownheads = knownheads.keys()
906 if knownheads:
909 if knownheads:
907 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
910 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
908 has_cl_set = dict.fromkeys(hasnodeset)
911 has_cl_set = dict.fromkeys(hasnodeset)
909 else:
912 else:
910 has_cl_set = {}
913 has_cl_set = {}
911
914
912 mnfst = self.manifest
915 mnfst = self.manifest
913 msng_mnfst_set = {}
916 msng_mnfst_set = {}
914 msng_filenode_set = {}
917 msng_filenode_set = {}
915
918
916 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
919 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
917 junk = None
920 junk = None
918
921
919 def identity(x):
922 def identity(x):
920 return x
923 return x
921
924
922 def cmp_by_rev_func(revlog):
925 def cmp_by_rev_func(revlog):
923 def cmpfunc(a, b):
926 def cmpfunc(a, b):
924 return cmp(revlog.rev(a), revlog.rev(b))
927 return cmp(revlog.rev(a), revlog.rev(b))
925 return cmpfunc
928 return cmpfunc
926
929
927 def prune_parents(revlog, hasset, msngset):
930 def prune_parents(revlog, hasset, msngset):
928 haslst = hasset.keys()
931 haslst = hasset.keys()
929 haslst.sort(cmp_by_rev_func(revlog))
932 haslst.sort(cmp_by_rev_func(revlog))
930 for node in haslst:
933 for node in haslst:
931 parentlst = [p for p in revlog.parents(node) if p != nullid]
934 parentlst = [p for p in revlog.parents(node) if p != nullid]
932 while parentlst:
935 while parentlst:
933 n = parentlst.pop()
936 n = parentlst.pop()
934 if n not in hasset:
937 if n not in hasset:
935 hasset[n] = 1
938 hasset[n] = 1
936 p = [p for p in revlog.parents(n) if p != nullid]
939 p = [p for p in revlog.parents(n) if p != nullid]
937 parentlst.extend(p)
940 parentlst.extend(p)
938 for n in hasset:
941 for n in hasset:
939 msngset.pop(n, None)
942 msngset.pop(n, None)
940
943
941 def manifest_and_file_collector(changedfileset):
944 def manifest_and_file_collector(changedfileset):
942 def collect_manifests_and_files(clnode):
945 def collect_manifests_and_files(clnode):
943 c = cl.read(clnode)
946 c = cl.read(clnode)
944 for f in c[3]:
947 for f in c[3]:
945 # This is to make sure we only have one instance of each
948 # This is to make sure we only have one instance of each
946 # filename string for each filename.
949 # filename string for each filename.
947 changedfileset.setdefault(f, f)
950 changedfileset.setdefault(f, f)
948 msng_mnfst_set.setdefault(c[0], clnode)
951 msng_mnfst_set.setdefault(c[0], clnode)
949 return collect_manifests_and_files
952 return collect_manifests_and_files
950
953
951 def prune_manifests():
954 def prune_manifests():
952 has_mnfst_set = {}
955 has_mnfst_set = {}
953 for n in msng_mnfst_set:
956 for n in msng_mnfst_set:
954 linknode = cl.node(mnfst.linkrev(n))
957 linknode = cl.node(mnfst.linkrev(n))
955 if linknode in has_cl_set:
958 if linknode in has_cl_set:
956 has_mnfst_set[n] = 1
959 has_mnfst_set[n] = 1
957 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
960 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
958
961
959 def lookup_manifest_link(mnfstnode):
962 def lookup_manifest_link(mnfstnode):
960 return msng_mnfst_set[mnfstnode]
963 return msng_mnfst_set[mnfstnode]
961
964
962 def filenode_collector(changedfiles):
965 def filenode_collector(changedfiles):
963 def collect_msng_filenodes(mnfstnode):
966 def collect_msng_filenodes(mnfstnode):
964 m = mnfst.read(mnfstnode)
967 m = mnfst.read(mnfstnode)
965 for f in changedfiles:
968 for f in changedfiles:
966 fnode = m.get(f, None)
969 fnode = m.get(f, None)
967 if fnode is not None:
970 if fnode is not None:
968 clnode = msng_mnfst_set[mnfstnode]
971 clnode = msng_mnfst_set[mnfstnode]
969 ndset = msng_filenode_set.setdefault(f, {})
972 ndset = msng_filenode_set.setdefault(f, {})
970 ndset.setdefault(fnode, clnode)
973 ndset.setdefault(fnode, clnode)
971 return collect_msng_filenodes
974 return collect_msng_filenodes
972
975
973 def prune_filenodes(f, filerevlog):
976 def prune_filenodes(f, filerevlog):
974 msngset = msng_filenode_set[f]
977 msngset = msng_filenode_set[f]
975 hasset = {}
978 hasset = {}
976 for n in msngset:
979 for n in msngset:
977 clnode = cl.node(filerevlog.linkrev(n))
980 clnode = cl.node(filerevlog.linkrev(n))
978 if clnode in has_cl_set:
981 if clnode in has_cl_set:
979 hasset[n] = 1
982 hasset[n] = 1
980 prune_parents(filerevlog, hasset, msngset)
983 prune_parents(filerevlog, hasset, msngset)
981
984
982 def lookup_filenode_link_func(fname):
985 def lookup_filenode_link_func(fname):
983 msngset = msng_filenode_set[fname]
986 msngset = msng_filenode_set[fname]
984 def lookup_filenode_link(fnode):
987 def lookup_filenode_link(fnode):
985 return msngset[fnode]
988 return msngset[fnode]
986 return lookup_filenode_link
989 return lookup_filenode_link
987
990
988 def gengroup():
991 def gengroup():
989 changedfiles = {}
992 changedfiles = {}
990 group = cl.group(msng_cl_lst, identity,
993 group = cl.group(msng_cl_lst, identity,
991 manifest_and_file_collector(changedfiles))
994 manifest_and_file_collector(changedfiles))
992 for chnk in group:
995 for chnk in group:
993 yield chnk
996 yield chnk
994 prune_manifests()
997 prune_manifests()
995 msng_mnfst_lst = msng_mnfst_set.keys()
998 msng_mnfst_lst = msng_mnfst_set.keys()
996 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
999 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
997 changedfiles = changedfiles.keys()
1000 changedfiles = changedfiles.keys()
998 changedfiles.sort()
1001 changedfiles.sort()
999 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1002 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1000 filenode_collector(changedfiles))
1003 filenode_collector(changedfiles))
1001 for chnk in group:
1004 for chnk in group:
1002 yield chnk
1005 yield chnk
1003 msng_mnfst_lst = None
1006 msng_mnfst_lst = None
1004 msng_mnfst_set.clear()
1007 msng_mnfst_set.clear()
1005 for fname in changedfiles:
1008 for fname in changedfiles:
1006 filerevlog = self.file(fname)
1009 filerevlog = self.file(fname)
1007 prune_filenodes(fname, filerevlog)
1010 prune_filenodes(fname, filerevlog)
1008 msng_filenode_lst = msng_filenode_set[fname].keys()
1011 msng_filenode_lst = msng_filenode_set[fname].keys()
1009 if len(msng_filenode_lst) > 0:
1012 if len(msng_filenode_lst) > 0:
1010 yield struct.pack(">l", len(fname) + 4) + fname
1013 yield struct.pack(">l", len(fname) + 4) + fname
1011 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1014 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1012 group = filerevlog.group(msng_filenode_lst,
1015 group = filerevlog.group(msng_filenode_lst,
1013 lookup_filenode_link_func(fname))
1016 lookup_filenode_link_func(fname))
1014 for chnk in group:
1017 for chnk in group:
1015 yield chnk
1018 yield chnk
1016 del msng_filenode_set[fname]
1019 del msng_filenode_set[fname]
1017 yield struct.pack(">l", 0)
1020 yield struct.pack(">l", 0)
1018
1021
1019 return util.chunkbuffer(gengroup())
1022 return util.chunkbuffer(gengroup())
1020
1023
1021 def changegroup(self, basenodes):
1024 def changegroup(self, basenodes):
1022 cl = self.changelog
1025 cl = self.changelog
1023 nodes = cl.nodesbetween(basenodes, None)[0]
1026 nodes = cl.nodesbetween(basenodes, None)[0]
1024 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1027 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1025
1028
1026 def identity(x):
1029 def identity(x):
1027 return x
1030 return x
1028
1031
1029 def gennodelst(revlog):
1032 def gennodelst(revlog):
1030 for r in xrange(0, revlog.count()):
1033 for r in xrange(0, revlog.count()):
1031 n = revlog.node(r)
1034 n = revlog.node(r)
1032 if revlog.linkrev(n) in revset:
1035 if revlog.linkrev(n) in revset:
1033 yield n
1036 yield n
1034
1037
1035 def changed_file_collector(changedfileset):
1038 def changed_file_collector(changedfileset):
1036 def collect_changed_files(clnode):
1039 def collect_changed_files(clnode):
1037 c = cl.read(clnode)
1040 c = cl.read(clnode)
1038 for fname in c[3]:
1041 for fname in c[3]:
1039 changedfileset[fname] = 1
1042 changedfileset[fname] = 1
1040 return collect_changed_files
1043 return collect_changed_files
1041
1044
1042 def lookuprevlink_func(revlog):
1045 def lookuprevlink_func(revlog):
1043 def lookuprevlink(n):
1046 def lookuprevlink(n):
1044 return cl.node(revlog.linkrev(n))
1047 return cl.node(revlog.linkrev(n))
1045 return lookuprevlink
1048 return lookuprevlink
1046
1049
1047 def gengroup():
1050 def gengroup():
1048 # construct a list of all changed files
1051 # construct a list of all changed files
1049 changedfiles = {}
1052 changedfiles = {}
1050
1053
1051 for chnk in cl.group(nodes, identity,
1054 for chnk in cl.group(nodes, identity,
1052 changed_file_collector(changedfiles)):
1055 changed_file_collector(changedfiles)):
1053 yield chnk
1056 yield chnk
1054 changedfiles = changedfiles.keys()
1057 changedfiles = changedfiles.keys()
1055 changedfiles.sort()
1058 changedfiles.sort()
1056
1059
1057 mnfst = self.manifest
1060 mnfst = self.manifest
1058 nodeiter = gennodelst(mnfst)
1061 nodeiter = gennodelst(mnfst)
1059 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1062 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1060 yield chnk
1063 yield chnk
1061
1064
1062 for fname in changedfiles:
1065 for fname in changedfiles:
1063 filerevlog = self.file(fname)
1066 filerevlog = self.file(fname)
1064 nodeiter = gennodelst(filerevlog)
1067 nodeiter = gennodelst(filerevlog)
1065 nodeiter = list(nodeiter)
1068 nodeiter = list(nodeiter)
1066 if nodeiter:
1069 if nodeiter:
1067 yield struct.pack(">l", len(fname) + 4) + fname
1070 yield struct.pack(">l", len(fname) + 4) + fname
1068 lookup = lookuprevlink_func(filerevlog)
1071 lookup = lookuprevlink_func(filerevlog)
1069 for chnk in filerevlog.group(nodeiter, lookup):
1072 for chnk in filerevlog.group(nodeiter, lookup):
1070 yield chnk
1073 yield chnk
1071
1074
1072 yield struct.pack(">l", 0)
1075 yield struct.pack(">l", 0)
1073
1076
1074 return util.chunkbuffer(gengroup())
1077 return util.chunkbuffer(gengroup())
1075
1078
1076 def addchangegroup(self, source):
1079 def addchangegroup(self, source):
1077
1080
1078 def getchunk():
1081 def getchunk():
1079 d = source.read(4)
1082 d = source.read(4)
1080 if not d: return ""
1083 if not d: return ""
1081 l = struct.unpack(">l", d)[0]
1084 l = struct.unpack(">l", d)[0]
1082 if l <= 4: return ""
1085 if l <= 4: return ""
1083 d = source.read(l - 4)
1086 d = source.read(l - 4)
1084 if len(d) < l - 4:
1087 if len(d) < l - 4:
1085 raise repo.RepoError("premature EOF reading chunk" +
1088 raise repo.RepoError("premature EOF reading chunk" +
1086 " (got %d bytes, expected %d)"
1089 " (got %d bytes, expected %d)"
1087 % (len(d), l - 4))
1090 % (len(d), l - 4))
1088 return d
1091 return d
1089
1092
1090 def getgroup():
1093 def getgroup():
1091 while 1:
1094 while 1:
1092 c = getchunk()
1095 c = getchunk()
1093 if not c: break
1096 if not c: break
1094 yield c
1097 yield c
1095
1098
1096 def csmap(x):
1099 def csmap(x):
1097 self.ui.debug("add changeset %s\n" % short(x))
1100 self.ui.debug("add changeset %s\n" % short(x))
1098 return self.changelog.count()
1101 return self.changelog.count()
1099
1102
1100 def revmap(x):
1103 def revmap(x):
1101 return self.changelog.rev(x)
1104 return self.changelog.rev(x)
1102
1105
1103 if not source: return
1106 if not source: return
1104 changesets = files = revisions = 0
1107 changesets = files = revisions = 0
1105
1108
1106 tr = self.transaction()
1109 tr = self.transaction()
1107
1110
1108 oldheads = len(self.changelog.heads())
1111 oldheads = len(self.changelog.heads())
1109
1112
1110 # pull off the changeset group
1113 # pull off the changeset group
1111 self.ui.status("adding changesets\n")
1114 self.ui.status("adding changesets\n")
1112 co = self.changelog.tip()
1115 co = self.changelog.tip()
1113 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1116 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1114 cnr, cor = map(self.changelog.rev, (cn, co))
1117 cnr, cor = map(self.changelog.rev, (cn, co))
1115 if cn == nullid:
1118 if cn == nullid:
1116 cnr = cor
1119 cnr = cor
1117 changesets = cnr - cor
1120 changesets = cnr - cor
1118
1121
1119 # pull off the manifest group
1122 # pull off the manifest group
1120 self.ui.status("adding manifests\n")
1123 self.ui.status("adding manifests\n")
1121 mm = self.manifest.tip()
1124 mm = self.manifest.tip()
1122 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1125 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1123
1126
1124 # process the files
1127 # process the files
1125 self.ui.status("adding file changes\n")
1128 self.ui.status("adding file changes\n")
1126 while 1:
1129 while 1:
1127 f = getchunk()
1130 f = getchunk()
1128 if not f: break
1131 if not f: break
1129 self.ui.debug("adding %s revisions\n" % f)
1132 self.ui.debug("adding %s revisions\n" % f)
1130 fl = self.file(f)
1133 fl = self.file(f)
1131 o = fl.count()
1134 o = fl.count()
1132 n = fl.addgroup(getgroup(), revmap, tr)
1135 n = fl.addgroup(getgroup(), revmap, tr)
1133 revisions += fl.count() - o
1136 revisions += fl.count() - o
1134 files += 1
1137 files += 1
1135
1138
1136 newheads = len(self.changelog.heads())
1139 newheads = len(self.changelog.heads())
1137 heads = ""
1140 heads = ""
1138 if oldheads and newheads > oldheads:
1141 if oldheads and newheads > oldheads:
1139 heads = " (+%d heads)" % (newheads - oldheads)
1142 heads = " (+%d heads)" % (newheads - oldheads)
1140
1143
1141 self.ui.status(("added %d changesets" +
1144 self.ui.status(("added %d changesets" +
1142 " with %d changes to %d files%s\n")
1145 " with %d changes to %d files%s\n")
1143 % (changesets, revisions, files, heads))
1146 % (changesets, revisions, files, heads))
1144
1147
1145 tr.close()
1148 tr.close()
1146
1149
1147 if changesets > 0:
1150 if changesets > 0:
1148 if not self.hook("changegroup",
1151 if not self.hook("changegroup",
1149 node=hex(self.changelog.node(cor+1))):
1152 node=hex(self.changelog.node(cor+1))):
1150 self.ui.warn("abort: changegroup hook returned failure!\n")
1153 self.ui.warn("abort: changegroup hook returned failure!\n")
1151 return 1
1154 return 1
1152
1155
1153 for i in range(cor + 1, cnr + 1):
1156 for i in range(cor + 1, cnr + 1):
1154 self.hook("commit", node=hex(self.changelog.node(i)))
1157 self.hook("commit", node=hex(self.changelog.node(i)))
1155
1158
1156 return
1159 return
1157
1160
1158 def update(self, node, allow=False, force=False, choose=None,
1161 def update(self, node, allow=False, force=False, choose=None,
1159 moddirstate=True):
1162 moddirstate=True):
1160 pl = self.dirstate.parents()
1163 pl = self.dirstate.parents()
1161 if not force and pl[1] != nullid:
1164 if not force and pl[1] != nullid:
1162 self.ui.warn("aborting: outstanding uncommitted merges\n")
1165 self.ui.warn("aborting: outstanding uncommitted merges\n")
1163 return 1
1166 return 1
1164
1167
1165 p1, p2 = pl[0], node
1168 p1, p2 = pl[0], node
1166 pa = self.changelog.ancestor(p1, p2)
1169 pa = self.changelog.ancestor(p1, p2)
1167 m1n = self.changelog.read(p1)[0]
1170 m1n = self.changelog.read(p1)[0]
1168 m2n = self.changelog.read(p2)[0]
1171 m2n = self.changelog.read(p2)[0]
1169 man = self.manifest.ancestor(m1n, m2n)
1172 man = self.manifest.ancestor(m1n, m2n)
1170 m1 = self.manifest.read(m1n)
1173 m1 = self.manifest.read(m1n)
1171 mf1 = self.manifest.readflags(m1n)
1174 mf1 = self.manifest.readflags(m1n)
1172 m2 = self.manifest.read(m2n)
1175 m2 = self.manifest.read(m2n)
1173 mf2 = self.manifest.readflags(m2n)
1176 mf2 = self.manifest.readflags(m2n)
1174 ma = self.manifest.read(man)
1177 ma = self.manifest.read(man)
1175 mfa = self.manifest.readflags(man)
1178 mfa = self.manifest.readflags(man)
1176
1179
1177 (c, a, d, u) = self.changes()
1180 (c, a, d, u) = self.changes()
1178
1181
1179 # is this a jump, or a merge? i.e. is there a linear path
1182 # is this a jump, or a merge? i.e. is there a linear path
1180 # from p1 to p2?
1183 # from p1 to p2?
1181 linear_path = (pa == p1 or pa == p2)
1184 linear_path = (pa == p1 or pa == p2)
1182
1185
1183 # resolve the manifest to determine which files
1186 # resolve the manifest to determine which files
1184 # we care about merging
1187 # we care about merging
1185 self.ui.note("resolving manifests\n")
1188 self.ui.note("resolving manifests\n")
1186 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1189 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1187 (force, allow, moddirstate, linear_path))
1190 (force, allow, moddirstate, linear_path))
1188 self.ui.debug(" ancestor %s local %s remote %s\n" %
1191 self.ui.debug(" ancestor %s local %s remote %s\n" %
1189 (short(man), short(m1n), short(m2n)))
1192 (short(man), short(m1n), short(m2n)))
1190
1193
1191 merge = {}
1194 merge = {}
1192 get = {}
1195 get = {}
1193 remove = []
1196 remove = []
1194
1197
1195 # construct a working dir manifest
1198 # construct a working dir manifest
1196 mw = m1.copy()
1199 mw = m1.copy()
1197 mfw = mf1.copy()
1200 mfw = mf1.copy()
1198 umap = dict.fromkeys(u)
1201 umap = dict.fromkeys(u)
1199
1202
1200 for f in a + c + u:
1203 for f in a + c + u:
1201 mw[f] = ""
1204 mw[f] = ""
1202 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1205 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1203
1206
1204 for f in d:
1207 for f in d:
1205 if f in mw: del mw[f]
1208 if f in mw: del mw[f]
1206
1209
1207 # If we're jumping between revisions (as opposed to merging),
1210 # If we're jumping between revisions (as opposed to merging),
1208 # and if neither the working directory nor the target rev has
1211 # and if neither the working directory nor the target rev has
1209 # the file, then we need to remove it from the dirstate, to
1212 # the file, then we need to remove it from the dirstate, to
1210 # prevent the dirstate from listing the file when it is no
1213 # prevent the dirstate from listing the file when it is no
1211 # longer in the manifest.
1214 # longer in the manifest.
1212 if moddirstate and linear_path and f not in m2:
1215 if moddirstate and linear_path and f not in m2:
1213 self.dirstate.forget((f,))
1216 self.dirstate.forget((f,))
1214
1217
1215 # Compare manifests
1218 # Compare manifests
1216 for f, n in mw.iteritems():
1219 for f, n in mw.iteritems():
1217 if choose and not choose(f): continue
1220 if choose and not choose(f): continue
1218 if f in m2:
1221 if f in m2:
1219 s = 0
1222 s = 0
1220
1223
1221 # is the wfile new since m1, and match m2?
1224 # is the wfile new since m1, and match m2?
1222 if f not in m1:
1225 if f not in m1:
1223 t1 = self.wread(f)
1226 t1 = self.wread(f)
1224 t2 = self.file(f).read(m2[f])
1227 t2 = self.file(f).read(m2[f])
1225 if cmp(t1, t2) == 0:
1228 if cmp(t1, t2) == 0:
1226 n = m2[f]
1229 n = m2[f]
1227 del t1, t2
1230 del t1, t2
1228
1231
1229 # are files different?
1232 # are files different?
1230 if n != m2[f]:
1233 if n != m2[f]:
1231 a = ma.get(f, nullid)
1234 a = ma.get(f, nullid)
1232 # are both different from the ancestor?
1235 # are both different from the ancestor?
1233 if n != a and m2[f] != a:
1236 if n != a and m2[f] != a:
1234 self.ui.debug(" %s versions differ, resolve\n" % f)
1237 self.ui.debug(" %s versions differ, resolve\n" % f)
1235 # merge executable bits
1238 # merge executable bits
1236 # "if we changed or they changed, change in merge"
1239 # "if we changed or they changed, change in merge"
1237 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1240 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1238 mode = ((a^b) | (a^c)) ^ a
1241 mode = ((a^b) | (a^c)) ^ a
1239 merge[f] = (m1.get(f, nullid), m2[f], mode)
1242 merge[f] = (m1.get(f, nullid), m2[f], mode)
1240 s = 1
1243 s = 1
1241 # are we clobbering?
1244 # are we clobbering?
1242 # is remote's version newer?
1245 # is remote's version newer?
1243 # or are we going back in time?
1246 # or are we going back in time?
1244 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1247 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1245 self.ui.debug(" remote %s is newer, get\n" % f)
1248 self.ui.debug(" remote %s is newer, get\n" % f)
1246 get[f] = m2[f]
1249 get[f] = m2[f]
1247 s = 1
1250 s = 1
1248 elif f in umap:
1251 elif f in umap:
1249 # this unknown file is the same as the checkout
1252 # this unknown file is the same as the checkout
1250 get[f] = m2[f]
1253 get[f] = m2[f]
1251
1254
1252 if not s and mfw[f] != mf2[f]:
1255 if not s and mfw[f] != mf2[f]:
1253 if force:
1256 if force:
1254 self.ui.debug(" updating permissions for %s\n" % f)
1257 self.ui.debug(" updating permissions for %s\n" % f)
1255 util.set_exec(self.wjoin(f), mf2[f])
1258 util.set_exec(self.wjoin(f), mf2[f])
1256 else:
1259 else:
1257 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1260 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1258 mode = ((a^b) | (a^c)) ^ a
1261 mode = ((a^b) | (a^c)) ^ a
1259 if mode != b:
1262 if mode != b:
1260 self.ui.debug(" updating permissions for %s\n" % f)
1263 self.ui.debug(" updating permissions for %s\n" % f)
1261 util.set_exec(self.wjoin(f), mode)
1264 util.set_exec(self.wjoin(f), mode)
1262 del m2[f]
1265 del m2[f]
1263 elif f in ma:
1266 elif f in ma:
1264 if n != ma[f]:
1267 if n != ma[f]:
1265 r = "d"
1268 r = "d"
1266 if not force and (linear_path or allow):
1269 if not force and (linear_path or allow):
1267 r = self.ui.prompt(
1270 r = self.ui.prompt(
1268 (" local changed %s which remote deleted\n" % f) +
1271 (" local changed %s which remote deleted\n" % f) +
1269 "(k)eep or (d)elete?", "[kd]", "k")
1272 "(k)eep or (d)elete?", "[kd]", "k")
1270 if r == "d":
1273 if r == "d":
1271 remove.append(f)
1274 remove.append(f)
1272 else:
1275 else:
1273 self.ui.debug("other deleted %s\n" % f)
1276 self.ui.debug("other deleted %s\n" % f)
1274 remove.append(f) # other deleted it
1277 remove.append(f) # other deleted it
1275 else:
1278 else:
1276 # file is created on branch or in working directory
1279 # file is created on branch or in working directory
1277 if force and f not in umap:
1280 if force and f not in umap:
1278 self.ui.debug("remote deleted %s, clobbering\n" % f)
1281 self.ui.debug("remote deleted %s, clobbering\n" % f)
1279 remove.append(f)
1282 remove.append(f)
1280 elif n == m1.get(f, nullid): # same as parent
1283 elif n == m1.get(f, nullid): # same as parent
1281 if p2 == pa: # going backwards?
1284 if p2 == pa: # going backwards?
1282 self.ui.debug("remote deleted %s\n" % f)
1285 self.ui.debug("remote deleted %s\n" % f)
1283 remove.append(f)
1286 remove.append(f)
1284 else:
1287 else:
1285 self.ui.debug("local modified %s, keeping\n" % f)
1288 self.ui.debug("local modified %s, keeping\n" % f)
1286 else:
1289 else:
1287 self.ui.debug("working dir created %s, keeping\n" % f)
1290 self.ui.debug("working dir created %s, keeping\n" % f)
1288
1291
1289 for f, n in m2.iteritems():
1292 for f, n in m2.iteritems():
1290 if choose and not choose(f): continue
1293 if choose and not choose(f): continue
1291 if f[0] == "/": continue
1294 if f[0] == "/": continue
1292 if f in ma and n != ma[f]:
1295 if f in ma and n != ma[f]:
1293 r = "k"
1296 r = "k"
1294 if not force and (linear_path or allow):
1297 if not force and (linear_path or allow):
1295 r = self.ui.prompt(
1298 r = self.ui.prompt(
1296 ("remote changed %s which local deleted\n" % f) +
1299 ("remote changed %s which local deleted\n" % f) +
1297 "(k)eep or (d)elete?", "[kd]", "k")
1300 "(k)eep or (d)elete?", "[kd]", "k")
1298 if r == "k": get[f] = n
1301 if r == "k": get[f] = n
1299 elif f not in ma:
1302 elif f not in ma:
1300 self.ui.debug("remote created %s\n" % f)
1303 self.ui.debug("remote created %s\n" % f)
1301 get[f] = n
1304 get[f] = n
1302 else:
1305 else:
1303 if force or p2 == pa: # going backwards?
1306 if force or p2 == pa: # going backwards?
1304 self.ui.debug("local deleted %s, recreating\n" % f)
1307 self.ui.debug("local deleted %s, recreating\n" % f)
1305 get[f] = n
1308 get[f] = n
1306 else:
1309 else:
1307 self.ui.debug("local deleted %s\n" % f)
1310 self.ui.debug("local deleted %s\n" % f)
1308
1311
1309 del mw, m1, m2, ma
1312 del mw, m1, m2, ma
1310
1313
1311 if force:
1314 if force:
1312 for f in merge:
1315 for f in merge:
1313 get[f] = merge[f][1]
1316 get[f] = merge[f][1]
1314 merge = {}
1317 merge = {}
1315
1318
1316 if linear_path or force:
1319 if linear_path or force:
1317 # we don't need to do any magic, just jump to the new rev
1320 # we don't need to do any magic, just jump to the new rev
1318 branch_merge = False
1321 branch_merge = False
1319 p1, p2 = p2, nullid
1322 p1, p2 = p2, nullid
1320 else:
1323 else:
1321 if not allow:
1324 if not allow:
1322 self.ui.status("this update spans a branch" +
1325 self.ui.status("this update spans a branch" +
1323 " affecting the following files:\n")
1326 " affecting the following files:\n")
1324 fl = merge.keys() + get.keys()
1327 fl = merge.keys() + get.keys()
1325 fl.sort()
1328 fl.sort()
1326 for f in fl:
1329 for f in fl:
1327 cf = ""
1330 cf = ""
1328 if f in merge: cf = " (resolve)"
1331 if f in merge: cf = " (resolve)"
1329 self.ui.status(" %s%s\n" % (f, cf))
1332 self.ui.status(" %s%s\n" % (f, cf))
1330 self.ui.warn("aborting update spanning branches!\n")
1333 self.ui.warn("aborting update spanning branches!\n")
1331 self.ui.status("(use update -m to merge across branches" +
1334 self.ui.status("(use update -m to merge across branches" +
1332 " or -C to lose changes)\n")
1335 " or -C to lose changes)\n")
1333 return 1
1336 return 1
1334 branch_merge = True
1337 branch_merge = True
1335
1338
1336 if moddirstate:
1339 if moddirstate:
1337 self.dirstate.setparents(p1, p2)
1340 self.dirstate.setparents(p1, p2)
1338
1341
1339 # get the files we don't need to change
1342 # get the files we don't need to change
1340 files = get.keys()
1343 files = get.keys()
1341 files.sort()
1344 files.sort()
1342 for f in files:
1345 for f in files:
1343 if f[0] == "/": continue
1346 if f[0] == "/": continue
1344 self.ui.note("getting %s\n" % f)
1347 self.ui.note("getting %s\n" % f)
1345 t = self.file(f).read(get[f])
1348 t = self.file(f).read(get[f])
1346 try:
1349 try:
1347 self.wwrite(f, t)
1350 self.wwrite(f, t)
1348 except IOError, e:
1351 except IOError, e:
1349 if e.errno != errno.ENOENT:
1352 if e.errno != errno.ENOENT:
1350 raise
1353 raise
1351 os.makedirs(os.path.dirname(self.wjoin(f)))
1354 os.makedirs(os.path.dirname(self.wjoin(f)))
1352 self.wwrite(f, t)
1355 self.wwrite(f, t)
1353 util.set_exec(self.wjoin(f), mf2[f])
1356 util.set_exec(self.wjoin(f), mf2[f])
1354 if moddirstate:
1357 if moddirstate:
1355 if branch_merge:
1358 if branch_merge:
1356 self.dirstate.update([f], 'n', st_mtime=-1)
1359 self.dirstate.update([f], 'n', st_mtime=-1)
1357 else:
1360 else:
1358 self.dirstate.update([f], 'n')
1361 self.dirstate.update([f], 'n')
1359
1362
1360 # merge the tricky bits
1363 # merge the tricky bits
1361 files = merge.keys()
1364 files = merge.keys()
1362 files.sort()
1365 files.sort()
1363 for f in files:
1366 for f in files:
1364 self.ui.status("merging %s\n" % f)
1367 self.ui.status("merging %s\n" % f)
1365 my, other, flag = merge[f]
1368 my, other, flag = merge[f]
1366 self.merge3(f, my, other)
1369 self.merge3(f, my, other)
1367 util.set_exec(self.wjoin(f), flag)
1370 util.set_exec(self.wjoin(f), flag)
1368 if moddirstate:
1371 if moddirstate:
1369 if branch_merge:
1372 if branch_merge:
1370 # We've done a branch merge, mark this file as merged
1373 # We've done a branch merge, mark this file as merged
1371 # so that we properly record the merger later
1374 # so that we properly record the merger later
1372 self.dirstate.update([f], 'm')
1375 self.dirstate.update([f], 'm')
1373 else:
1376 else:
1374 # We've update-merged a locally modified file, so
1377 # We've update-merged a locally modified file, so
1375 # we set the dirstate to emulate a normal checkout
1378 # we set the dirstate to emulate a normal checkout
1376 # of that file some time in the past. Thus our
1379 # of that file some time in the past. Thus our
1377 # merge will appear as a normal local file
1380 # merge will appear as a normal local file
1378 # modification.
1381 # modification.
1379 f_len = len(self.file(f).read(other))
1382 f_len = len(self.file(f).read(other))
1380 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1383 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1381
1384
1382 remove.sort()
1385 remove.sort()
1383 for f in remove:
1386 for f in remove:
1384 self.ui.note("removing %s\n" % f)
1387 self.ui.note("removing %s\n" % f)
1385 try:
1388 try:
1386 os.unlink(self.wjoin(f))
1389 os.unlink(self.wjoin(f))
1387 except OSError, inst:
1390 except OSError, inst:
1388 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1391 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1389 # try removing directories that might now be empty
1392 # try removing directories that might now be empty
1390 try: os.removedirs(os.path.dirname(self.wjoin(f)))
1393 try: os.removedirs(os.path.dirname(self.wjoin(f)))
1391 except: pass
1394 except: pass
1392 if moddirstate:
1395 if moddirstate:
1393 if branch_merge:
1396 if branch_merge:
1394 self.dirstate.update(remove, 'r')
1397 self.dirstate.update(remove, 'r')
1395 else:
1398 else:
1396 self.dirstate.forget(remove)
1399 self.dirstate.forget(remove)
1397
1400
1398 def merge3(self, fn, my, other):
1401 def merge3(self, fn, my, other):
1399 """perform a 3-way merge in the working directory"""
1402 """perform a 3-way merge in the working directory"""
1400
1403
1401 def temp(prefix, node):
1404 def temp(prefix, node):
1402 pre = "%s~%s." % (os.path.basename(fn), prefix)
1405 pre = "%s~%s." % (os.path.basename(fn), prefix)
1403 (fd, name) = tempfile.mkstemp("", pre)
1406 (fd, name) = tempfile.mkstemp("", pre)
1404 f = os.fdopen(fd, "wb")
1407 f = os.fdopen(fd, "wb")
1405 self.wwrite(fn, fl.read(node), f)
1408 self.wwrite(fn, fl.read(node), f)
1406 f.close()
1409 f.close()
1407 return name
1410 return name
1408
1411
1409 fl = self.file(fn)
1412 fl = self.file(fn)
1410 base = fl.ancestor(my, other)
1413 base = fl.ancestor(my, other)
1411 a = self.wjoin(fn)
1414 a = self.wjoin(fn)
1412 b = temp("base", base)
1415 b = temp("base", base)
1413 c = temp("other", other)
1416 c = temp("other", other)
1414
1417
1415 self.ui.note("resolving %s\n" % fn)
1418 self.ui.note("resolving %s\n" % fn)
1416 self.ui.debug("file %s: my %s other %s ancestor %s\n" %
1419 self.ui.debug("file %s: my %s other %s ancestor %s\n" %
1417 (fn, short(my), short(other), short(base)))
1420 (fn, short(my), short(other), short(base)))
1418
1421
1419 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1422 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1420 or "hgmerge")
1423 or "hgmerge")
1421 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1424 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1422 if r:
1425 if r:
1423 self.ui.warn("merging %s failed!\n" % fn)
1426 self.ui.warn("merging %s failed!\n" % fn)
1424
1427
1425 os.unlink(b)
1428 os.unlink(b)
1426 os.unlink(c)
1429 os.unlink(c)
1427
1430
1428 def verify(self):
1431 def verify(self):
1429 filelinkrevs = {}
1432 filelinkrevs = {}
1430 filenodes = {}
1433 filenodes = {}
1431 changesets = revisions = files = 0
1434 changesets = revisions = files = 0
1432 errors = [0]
1435 errors = [0]
1433 neededmanifests = {}
1436 neededmanifests = {}
1434
1437
1435 def err(msg):
1438 def err(msg):
1436 self.ui.warn(msg + "\n")
1439 self.ui.warn(msg + "\n")
1437 errors[0] += 1
1440 errors[0] += 1
1438
1441
1439 seen = {}
1442 seen = {}
1440 self.ui.status("checking changesets\n")
1443 self.ui.status("checking changesets\n")
1441 for i in range(self.changelog.count()):
1444 for i in range(self.changelog.count()):
1442 changesets += 1
1445 changesets += 1
1443 n = self.changelog.node(i)
1446 n = self.changelog.node(i)
1444 l = self.changelog.linkrev(n)
1447 l = self.changelog.linkrev(n)
1445 if l != i:
1448 if l != i:
1446 err("incorrect link (%d) for changeset revision %d" % (l, i))
1449 err("incorrect link (%d) for changeset revision %d" % (l, i))
1447 if n in seen:
1450 if n in seen:
1448 err("duplicate changeset at revision %d" % i)
1451 err("duplicate changeset at revision %d" % i)
1449 seen[n] = 1
1452 seen[n] = 1
1450
1453
1451 for p in self.changelog.parents(n):
1454 for p in self.changelog.parents(n):
1452 if p not in self.changelog.nodemap:
1455 if p not in self.changelog.nodemap:
1453 err("changeset %s has unknown parent %s" %
1456 err("changeset %s has unknown parent %s" %
1454 (short(n), short(p)))
1457 (short(n), short(p)))
1455 try:
1458 try:
1456 changes = self.changelog.read(n)
1459 changes = self.changelog.read(n)
1457 except Exception, inst:
1460 except Exception, inst:
1458 err("unpacking changeset %s: %s" % (short(n), inst))
1461 err("unpacking changeset %s: %s" % (short(n), inst))
1459
1462
1460 neededmanifests[changes[0]] = n
1463 neededmanifests[changes[0]] = n
1461
1464
1462 for f in changes[3]:
1465 for f in changes[3]:
1463 filelinkrevs.setdefault(f, []).append(i)
1466 filelinkrevs.setdefault(f, []).append(i)
1464
1467
1465 seen = {}
1468 seen = {}
1466 self.ui.status("checking manifests\n")
1469 self.ui.status("checking manifests\n")
1467 for i in range(self.manifest.count()):
1470 for i in range(self.manifest.count()):
1468 n = self.manifest.node(i)
1471 n = self.manifest.node(i)
1469 l = self.manifest.linkrev(n)
1472 l = self.manifest.linkrev(n)
1470
1473
1471 if l < 0 or l >= self.changelog.count():
1474 if l < 0 or l >= self.changelog.count():
1472 err("bad manifest link (%d) at revision %d" % (l, i))
1475 err("bad manifest link (%d) at revision %d" % (l, i))
1473
1476
1474 if n in neededmanifests:
1477 if n in neededmanifests:
1475 del neededmanifests[n]
1478 del neededmanifests[n]
1476
1479
1477 if n in seen:
1480 if n in seen:
1478 err("duplicate manifest at revision %d" % i)
1481 err("duplicate manifest at revision %d" % i)
1479
1482
1480 seen[n] = 1
1483 seen[n] = 1
1481
1484
1482 for p in self.manifest.parents(n):
1485 for p in self.manifest.parents(n):
1483 if p not in self.manifest.nodemap:
1486 if p not in self.manifest.nodemap:
1484 err("manifest %s has unknown parent %s" %
1487 err("manifest %s has unknown parent %s" %
1485 (short(n), short(p)))
1488 (short(n), short(p)))
1486
1489
1487 try:
1490 try:
1488 delta = mdiff.patchtext(self.manifest.delta(n))
1491 delta = mdiff.patchtext(self.manifest.delta(n))
1489 except KeyboardInterrupt:
1492 except KeyboardInterrupt:
1490 self.ui.warn("interrupted")
1493 self.ui.warn("interrupted")
1491 raise
1494 raise
1492 except Exception, inst:
1495 except Exception, inst:
1493 err("unpacking manifest %s: %s" % (short(n), inst))
1496 err("unpacking manifest %s: %s" % (short(n), inst))
1494
1497
1495 ff = [ l.split('\0') for l in delta.splitlines() ]
1498 ff = [ l.split('\0') for l in delta.splitlines() ]
1496 for f, fn in ff:
1499 for f, fn in ff:
1497 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1500 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1498
1501
1499 self.ui.status("crosschecking files in changesets and manifests\n")
1502 self.ui.status("crosschecking files in changesets and manifests\n")
1500
1503
1501 for m,c in neededmanifests.items():
1504 for m,c in neededmanifests.items():
1502 err("Changeset %s refers to unknown manifest %s" %
1505 err("Changeset %s refers to unknown manifest %s" %
1503 (short(m), short(c)))
1506 (short(m), short(c)))
1504 del neededmanifests
1507 del neededmanifests
1505
1508
1506 for f in filenodes:
1509 for f in filenodes:
1507 if f not in filelinkrevs:
1510 if f not in filelinkrevs:
1508 err("file %s in manifest but not in changesets" % f)
1511 err("file %s in manifest but not in changesets" % f)
1509
1512
1510 for f in filelinkrevs:
1513 for f in filelinkrevs:
1511 if f not in filenodes:
1514 if f not in filenodes:
1512 err("file %s in changeset but not in manifest" % f)
1515 err("file %s in changeset but not in manifest" % f)
1513
1516
1514 self.ui.status("checking files\n")
1517 self.ui.status("checking files\n")
1515 ff = filenodes.keys()
1518 ff = filenodes.keys()
1516 ff.sort()
1519 ff.sort()
1517 for f in ff:
1520 for f in ff:
1518 if f == "/dev/null": continue
1521 if f == "/dev/null": continue
1519 files += 1
1522 files += 1
1520 fl = self.file(f)
1523 fl = self.file(f)
1521 nodes = { nullid: 1 }
1524 nodes = { nullid: 1 }
1522 seen = {}
1525 seen = {}
1523 for i in range(fl.count()):
1526 for i in range(fl.count()):
1524 revisions += 1
1527 revisions += 1
1525 n = fl.node(i)
1528 n = fl.node(i)
1526
1529
1527 if n in seen:
1530 if n in seen:
1528 err("%s: duplicate revision %d" % (f, i))
1531 err("%s: duplicate revision %d" % (f, i))
1529 if n not in filenodes[f]:
1532 if n not in filenodes[f]:
1530 err("%s: %d:%s not in manifests" % (f, i, short(n)))
1533 err("%s: %d:%s not in manifests" % (f, i, short(n)))
1531 else:
1534 else:
1532 del filenodes[f][n]
1535 del filenodes[f][n]
1533
1536
1534 flr = fl.linkrev(n)
1537 flr = fl.linkrev(n)
1535 if flr not in filelinkrevs[f]:
1538 if flr not in filelinkrevs[f]:
1536 err("%s:%s points to unexpected changeset %d"
1539 err("%s:%s points to unexpected changeset %d"
1537 % (f, short(n), flr))
1540 % (f, short(n), flr))
1538 else:
1541 else:
1539 filelinkrevs[f].remove(flr)
1542 filelinkrevs[f].remove(flr)
1540
1543
1541 # verify contents
1544 # verify contents
1542 try:
1545 try:
1543 t = fl.read(n)
1546 t = fl.read(n)
1544 except Exception, inst:
1547 except Exception, inst:
1545 err("unpacking file %s %s: %s" % (f, short(n), inst))
1548 err("unpacking file %s %s: %s" % (f, short(n), inst))
1546
1549
1547 # verify parents
1550 # verify parents
1548 (p1, p2) = fl.parents(n)
1551 (p1, p2) = fl.parents(n)
1549 if p1 not in nodes:
1552 if p1 not in nodes:
1550 err("file %s:%s unknown parent 1 %s" %
1553 err("file %s:%s unknown parent 1 %s" %
1551 (f, short(n), short(p1)))
1554 (f, short(n), short(p1)))
1552 if p2 not in nodes:
1555 if p2 not in nodes:
1553 err("file %s:%s unknown parent 2 %s" %
1556 err("file %s:%s unknown parent 2 %s" %
1554 (f, short(n), short(p1)))
1557 (f, short(n), short(p1)))
1555 nodes[n] = 1
1558 nodes[n] = 1
1556
1559
1557 # cross-check
1560 # cross-check
1558 for node in filenodes[f]:
1561 for node in filenodes[f]:
1559 err("node %s in manifests not in %s" % (hex(node), f))
1562 err("node %s in manifests not in %s" % (hex(node), f))
1560
1563
1561 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1564 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1562 (files, changesets, revisions))
1565 (files, changesets, revisions))
1563
1566
1564 if errors[0]:
1567 if errors[0]:
1565 self.ui.warn("%d integrity errors encountered!\n" % errors[0])
1568 self.ui.warn("%d integrity errors encountered!\n" % errors[0])
1566 return 1
1569 return 1
General Comments 0
You need to be logged in to leave comments. Login now