##// END OF EJS Templates
New option -i/--ignored for 'hg status' to show ignored files....
Thomas Arendsen Hein -
r2022:a59da8cc default
parent child Browse files
Show More
@@ -1,3329 +1,3334
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch hgweb mdiff random signal tempfile time")
13 demandload(globals(), "fnmatch hgweb mdiff random signal tempfile time")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 demandload(globals(), "changegroup")
15 demandload(globals(), "changegroup")
16
16
17 class UnknownCommand(Exception):
17 class UnknownCommand(Exception):
18 """Exception raised if command is not in the command table."""
18 """Exception raised if command is not in the command table."""
19 class AmbiguousCommand(Exception):
19 class AmbiguousCommand(Exception):
20 """Exception raised if command shortcut matches more than one command."""
20 """Exception raised if command shortcut matches more than one command."""
21
21
22 def filterfiles(filters, files):
22 def filterfiles(filters, files):
23 l = [x for x in files if x in filters]
23 l = [x for x in files if x in filters]
24
24
25 for t in filters:
25 for t in filters:
26 if t and t[-1] != "/":
26 if t and t[-1] != "/":
27 t += "/"
27 t += "/"
28 l += [x for x in files if x.startswith(t)]
28 l += [x for x in files if x.startswith(t)]
29 return l
29 return l
30
30
31 def relpath(repo, args):
31 def relpath(repo, args):
32 cwd = repo.getcwd()
32 cwd = repo.getcwd()
33 if cwd:
33 if cwd:
34 return [util.normpath(os.path.join(cwd, x)) for x in args]
34 return [util.normpath(os.path.join(cwd, x)) for x in args]
35 return args
35 return args
36
36
37 def matchpats(repo, pats=[], opts={}, head=''):
37 def matchpats(repo, pats=[], opts={}, head=''):
38 cwd = repo.getcwd()
38 cwd = repo.getcwd()
39 if not pats and cwd:
39 if not pats and cwd:
40 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
40 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
41 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
41 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
42 cwd = ''
42 cwd = ''
43 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
43 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
44 opts.get('exclude'), head)
44 opts.get('exclude'), head)
45
45
46 def makewalk(repo, pats, opts, node=None, head=''):
46 def makewalk(repo, pats, opts, node=None, head=''):
47 files, matchfn, anypats = matchpats(repo, pats, opts, head)
47 files, matchfn, anypats = matchpats(repo, pats, opts, head)
48 exact = dict(zip(files, files))
48 exact = dict(zip(files, files))
49 def walk():
49 def walk():
50 for src, fn in repo.walk(node=node, files=files, match=matchfn):
50 for src, fn in repo.walk(node=node, files=files, match=matchfn):
51 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
51 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
52 return files, matchfn, walk()
52 return files, matchfn, walk()
53
53
54 def walk(repo, pats, opts, node=None, head=''):
54 def walk(repo, pats, opts, node=None, head=''):
55 files, matchfn, results = makewalk(repo, pats, opts, node, head)
55 files, matchfn, results = makewalk(repo, pats, opts, node, head)
56 for r in results:
56 for r in results:
57 yield r
57 yield r
58
58
59 def walkchangerevs(ui, repo, pats, opts):
59 def walkchangerevs(ui, repo, pats, opts):
60 '''Iterate over files and the revs they changed in.
60 '''Iterate over files and the revs they changed in.
61
61
62 Callers most commonly need to iterate backwards over the history
62 Callers most commonly need to iterate backwards over the history
63 it is interested in. Doing so has awful (quadratic-looking)
63 it is interested in. Doing so has awful (quadratic-looking)
64 performance, so we use iterators in a "windowed" way.
64 performance, so we use iterators in a "windowed" way.
65
65
66 We walk a window of revisions in the desired order. Within the
66 We walk a window of revisions in the desired order. Within the
67 window, we first walk forwards to gather data, then in the desired
67 window, we first walk forwards to gather data, then in the desired
68 order (usually backwards) to display it.
68 order (usually backwards) to display it.
69
69
70 This function returns an (iterator, getchange, matchfn) tuple. The
70 This function returns an (iterator, getchange, matchfn) tuple. The
71 getchange function returns the changelog entry for a numeric
71 getchange function returns the changelog entry for a numeric
72 revision. The iterator yields 3-tuples. They will be of one of
72 revision. The iterator yields 3-tuples. They will be of one of
73 the following forms:
73 the following forms:
74
74
75 "window", incrementing, lastrev: stepping through a window,
75 "window", incrementing, lastrev: stepping through a window,
76 positive if walking forwards through revs, last rev in the
76 positive if walking forwards through revs, last rev in the
77 sequence iterated over - use to reset state for the current window
77 sequence iterated over - use to reset state for the current window
78
78
79 "add", rev, fns: out-of-order traversal of the given file names
79 "add", rev, fns: out-of-order traversal of the given file names
80 fns, which changed during revision rev - use to gather data for
80 fns, which changed during revision rev - use to gather data for
81 possible display
81 possible display
82
82
83 "iter", rev, None: in-order traversal of the revs earlier iterated
83 "iter", rev, None: in-order traversal of the revs earlier iterated
84 over with "add" - use to display data'''
84 over with "add" - use to display data'''
85
85
86 def increasing_windows(start, end, windowsize=8, sizelimit=512):
86 def increasing_windows(start, end, windowsize=8, sizelimit=512):
87 if start < end:
87 if start < end:
88 while start < end:
88 while start < end:
89 yield start, min(windowsize, end-start)
89 yield start, min(windowsize, end-start)
90 start += windowsize
90 start += windowsize
91 if windowsize < sizelimit:
91 if windowsize < sizelimit:
92 windowsize *= 2
92 windowsize *= 2
93 else:
93 else:
94 while start > end:
94 while start > end:
95 yield start, min(windowsize, start-end-1)
95 yield start, min(windowsize, start-end-1)
96 start -= windowsize
96 start -= windowsize
97 if windowsize < sizelimit:
97 if windowsize < sizelimit:
98 windowsize *= 2
98 windowsize *= 2
99
99
100
100
101 files, matchfn, anypats = matchpats(repo, pats, opts)
101 files, matchfn, anypats = matchpats(repo, pats, opts)
102
102
103 if repo.changelog.count() == 0:
103 if repo.changelog.count() == 0:
104 return [], False, matchfn
104 return [], False, matchfn
105
105
106 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
106 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
107 wanted = {}
107 wanted = {}
108 slowpath = anypats
108 slowpath = anypats
109 fncache = {}
109 fncache = {}
110
110
111 chcache = {}
111 chcache = {}
112 def getchange(rev):
112 def getchange(rev):
113 ch = chcache.get(rev)
113 ch = chcache.get(rev)
114 if ch is None:
114 if ch is None:
115 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
115 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
116 return ch
116 return ch
117
117
118 if not slowpath and not files:
118 if not slowpath and not files:
119 # No files, no patterns. Display all revs.
119 # No files, no patterns. Display all revs.
120 wanted = dict(zip(revs, revs))
120 wanted = dict(zip(revs, revs))
121 if not slowpath:
121 if not slowpath:
122 # Only files, no patterns. Check the history of each file.
122 # Only files, no patterns. Check the history of each file.
123 def filerevgen(filelog):
123 def filerevgen(filelog):
124 for i, window in increasing_windows(filelog.count()-1, -1):
124 for i, window in increasing_windows(filelog.count()-1, -1):
125 revs = []
125 revs = []
126 for j in xrange(i - window, i + 1):
126 for j in xrange(i - window, i + 1):
127 revs.append(filelog.linkrev(filelog.node(j)))
127 revs.append(filelog.linkrev(filelog.node(j)))
128 revs.reverse()
128 revs.reverse()
129 for rev in revs:
129 for rev in revs:
130 yield rev
130 yield rev
131
131
132 minrev, maxrev = min(revs), max(revs)
132 minrev, maxrev = min(revs), max(revs)
133 for file_ in files:
133 for file_ in files:
134 filelog = repo.file(file_)
134 filelog = repo.file(file_)
135 # A zero count may be a directory or deleted file, so
135 # A zero count may be a directory or deleted file, so
136 # try to find matching entries on the slow path.
136 # try to find matching entries on the slow path.
137 if filelog.count() == 0:
137 if filelog.count() == 0:
138 slowpath = True
138 slowpath = True
139 break
139 break
140 for rev in filerevgen(filelog):
140 for rev in filerevgen(filelog):
141 if rev <= maxrev:
141 if rev <= maxrev:
142 if rev < minrev:
142 if rev < minrev:
143 break
143 break
144 fncache.setdefault(rev, [])
144 fncache.setdefault(rev, [])
145 fncache[rev].append(file_)
145 fncache[rev].append(file_)
146 wanted[rev] = 1
146 wanted[rev] = 1
147 if slowpath:
147 if slowpath:
148 # The slow path checks files modified in every changeset.
148 # The slow path checks files modified in every changeset.
149 def changerevgen():
149 def changerevgen():
150 for i, window in increasing_windows(repo.changelog.count()-1, -1):
150 for i, window in increasing_windows(repo.changelog.count()-1, -1):
151 for j in xrange(i - window, i + 1):
151 for j in xrange(i - window, i + 1):
152 yield j, getchange(j)[3]
152 yield j, getchange(j)[3]
153
153
154 for rev, changefiles in changerevgen():
154 for rev, changefiles in changerevgen():
155 matches = filter(matchfn, changefiles)
155 matches = filter(matchfn, changefiles)
156 if matches:
156 if matches:
157 fncache[rev] = matches
157 fncache[rev] = matches
158 wanted[rev] = 1
158 wanted[rev] = 1
159
159
160 def iterate():
160 def iterate():
161 for i, window in increasing_windows(0, len(revs)):
161 for i, window in increasing_windows(0, len(revs)):
162 yield 'window', revs[0] < revs[-1], revs[-1]
162 yield 'window', revs[0] < revs[-1], revs[-1]
163 nrevs = [rev for rev in revs[i:i+window]
163 nrevs = [rev for rev in revs[i:i+window]
164 if rev in wanted]
164 if rev in wanted]
165 srevs = list(nrevs)
165 srevs = list(nrevs)
166 srevs.sort()
166 srevs.sort()
167 for rev in srevs:
167 for rev in srevs:
168 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
168 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
169 yield 'add', rev, fns
169 yield 'add', rev, fns
170 for rev in nrevs:
170 for rev in nrevs:
171 yield 'iter', rev, None
171 yield 'iter', rev, None
172 return iterate(), getchange, matchfn
172 return iterate(), getchange, matchfn
173
173
174 revrangesep = ':'
174 revrangesep = ':'
175
175
176 def revrange(ui, repo, revs, revlog=None):
176 def revrange(ui, repo, revs, revlog=None):
177 """Yield revision as strings from a list of revision specifications."""
177 """Yield revision as strings from a list of revision specifications."""
178 if revlog is None:
178 if revlog is None:
179 revlog = repo.changelog
179 revlog = repo.changelog
180 revcount = revlog.count()
180 revcount = revlog.count()
181 def fix(val, defval):
181 def fix(val, defval):
182 if not val:
182 if not val:
183 return defval
183 return defval
184 try:
184 try:
185 num = int(val)
185 num = int(val)
186 if str(num) != val:
186 if str(num) != val:
187 raise ValueError
187 raise ValueError
188 if num < 0:
188 if num < 0:
189 num += revcount
189 num += revcount
190 if num < 0:
190 if num < 0:
191 num = 0
191 num = 0
192 elif num >= revcount:
192 elif num >= revcount:
193 raise ValueError
193 raise ValueError
194 except ValueError:
194 except ValueError:
195 try:
195 try:
196 num = repo.changelog.rev(repo.lookup(val))
196 num = repo.changelog.rev(repo.lookup(val))
197 except KeyError:
197 except KeyError:
198 try:
198 try:
199 num = revlog.rev(revlog.lookup(val))
199 num = revlog.rev(revlog.lookup(val))
200 except KeyError:
200 except KeyError:
201 raise util.Abort(_('invalid revision identifier %s'), val)
201 raise util.Abort(_('invalid revision identifier %s'), val)
202 return num
202 return num
203 seen = {}
203 seen = {}
204 for spec in revs:
204 for spec in revs:
205 if spec.find(revrangesep) >= 0:
205 if spec.find(revrangesep) >= 0:
206 start, end = spec.split(revrangesep, 1)
206 start, end = spec.split(revrangesep, 1)
207 start = fix(start, 0)
207 start = fix(start, 0)
208 end = fix(end, revcount - 1)
208 end = fix(end, revcount - 1)
209 step = start > end and -1 or 1
209 step = start > end and -1 or 1
210 for rev in xrange(start, end+step, step):
210 for rev in xrange(start, end+step, step):
211 if rev in seen:
211 if rev in seen:
212 continue
212 continue
213 seen[rev] = 1
213 seen[rev] = 1
214 yield str(rev)
214 yield str(rev)
215 else:
215 else:
216 rev = fix(spec, None)
216 rev = fix(spec, None)
217 if rev in seen:
217 if rev in seen:
218 continue
218 continue
219 seen[rev] = 1
219 seen[rev] = 1
220 yield str(rev)
220 yield str(rev)
221
221
222 def make_filename(repo, r, pat, node=None,
222 def make_filename(repo, r, pat, node=None,
223 total=None, seqno=None, revwidth=None, pathname=None):
223 total=None, seqno=None, revwidth=None, pathname=None):
224 node_expander = {
224 node_expander = {
225 'H': lambda: hex(node),
225 'H': lambda: hex(node),
226 'R': lambda: str(r.rev(node)),
226 'R': lambda: str(r.rev(node)),
227 'h': lambda: short(node),
227 'h': lambda: short(node),
228 }
228 }
229 expander = {
229 expander = {
230 '%': lambda: '%',
230 '%': lambda: '%',
231 'b': lambda: os.path.basename(repo.root),
231 'b': lambda: os.path.basename(repo.root),
232 }
232 }
233
233
234 try:
234 try:
235 if node:
235 if node:
236 expander.update(node_expander)
236 expander.update(node_expander)
237 if node and revwidth is not None:
237 if node and revwidth is not None:
238 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
238 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
239 if total is not None:
239 if total is not None:
240 expander['N'] = lambda: str(total)
240 expander['N'] = lambda: str(total)
241 if seqno is not None:
241 if seqno is not None:
242 expander['n'] = lambda: str(seqno)
242 expander['n'] = lambda: str(seqno)
243 if total is not None and seqno is not None:
243 if total is not None and seqno is not None:
244 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
244 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
245 if pathname is not None:
245 if pathname is not None:
246 expander['s'] = lambda: os.path.basename(pathname)
246 expander['s'] = lambda: os.path.basename(pathname)
247 expander['d'] = lambda: os.path.dirname(pathname) or '.'
247 expander['d'] = lambda: os.path.dirname(pathname) or '.'
248 expander['p'] = lambda: pathname
248 expander['p'] = lambda: pathname
249
249
250 newname = []
250 newname = []
251 patlen = len(pat)
251 patlen = len(pat)
252 i = 0
252 i = 0
253 while i < patlen:
253 while i < patlen:
254 c = pat[i]
254 c = pat[i]
255 if c == '%':
255 if c == '%':
256 i += 1
256 i += 1
257 c = pat[i]
257 c = pat[i]
258 c = expander[c]()
258 c = expander[c]()
259 newname.append(c)
259 newname.append(c)
260 i += 1
260 i += 1
261 return ''.join(newname)
261 return ''.join(newname)
262 except KeyError, inst:
262 except KeyError, inst:
263 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
263 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
264 inst.args[0])
264 inst.args[0])
265
265
266 def make_file(repo, r, pat, node=None,
266 def make_file(repo, r, pat, node=None,
267 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
267 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
268 if not pat or pat == '-':
268 if not pat or pat == '-':
269 return 'w' in mode and sys.stdout or sys.stdin
269 return 'w' in mode and sys.stdout or sys.stdin
270 if hasattr(pat, 'write') and 'w' in mode:
270 if hasattr(pat, 'write') and 'w' in mode:
271 return pat
271 return pat
272 if hasattr(pat, 'read') and 'r' in mode:
272 if hasattr(pat, 'read') and 'r' in mode:
273 return pat
273 return pat
274 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
274 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
275 pathname),
275 pathname),
276 mode)
276 mode)
277
277
278 def write_bundle(cg, filename=None, compress=True):
278 def write_bundle(cg, filename=None, compress=True):
279 """Write a bundle file and return its filename.
279 """Write a bundle file and return its filename.
280
280
281 Existing files will not be overwritten.
281 Existing files will not be overwritten.
282 If no filename is specified, a temporary file is created.
282 If no filename is specified, a temporary file is created.
283 bz2 compression can be turned off.
283 bz2 compression can be turned off.
284 The bundle file will be deleted in case of errors.
284 The bundle file will be deleted in case of errors.
285 """
285 """
286 class nocompress(object):
286 class nocompress(object):
287 def compress(self, x):
287 def compress(self, x):
288 return x
288 return x
289 def flush(self):
289 def flush(self):
290 return ""
290 return ""
291
291
292 fh = None
292 fh = None
293 cleanup = None
293 cleanup = None
294 try:
294 try:
295 if filename:
295 if filename:
296 if os.path.exists(filename):
296 if os.path.exists(filename):
297 raise util.Abort(_("file '%s' already exists"), filename)
297 raise util.Abort(_("file '%s' already exists"), filename)
298 fh = open(filename, "wb")
298 fh = open(filename, "wb")
299 else:
299 else:
300 fd, filename = tempfile.mkstemp(suffix=".hg", prefix="hg-bundle-")
300 fd, filename = tempfile.mkstemp(suffix=".hg", prefix="hg-bundle-")
301 fh = os.fdopen(fd, "wb")
301 fh = os.fdopen(fd, "wb")
302 cleanup = filename
302 cleanup = filename
303
303
304 if compress:
304 if compress:
305 fh.write("HG10")
305 fh.write("HG10")
306 z = bz2.BZ2Compressor(9)
306 z = bz2.BZ2Compressor(9)
307 else:
307 else:
308 fh.write("HG10UN")
308 fh.write("HG10UN")
309 z = nocompress()
309 z = nocompress()
310 # parse the changegroup data, otherwise we will block
310 # parse the changegroup data, otherwise we will block
311 # in case of sshrepo because we don't know the end of the stream
311 # in case of sshrepo because we don't know the end of the stream
312
312
313 # an empty chunkiter is the end of the changegroup
313 # an empty chunkiter is the end of the changegroup
314 empty = False
314 empty = False
315 while not empty:
315 while not empty:
316 empty = True
316 empty = True
317 for chunk in changegroup.chunkiter(cg):
317 for chunk in changegroup.chunkiter(cg):
318 empty = False
318 empty = False
319 fh.write(z.compress(changegroup.genchunk(chunk)))
319 fh.write(z.compress(changegroup.genchunk(chunk)))
320 fh.write(z.compress(changegroup.closechunk()))
320 fh.write(z.compress(changegroup.closechunk()))
321 fh.write(z.flush())
321 fh.write(z.flush())
322 cleanup = None
322 cleanup = None
323 return filename
323 return filename
324 finally:
324 finally:
325 if fh is not None:
325 if fh is not None:
326 fh.close()
326 fh.close()
327 if cleanup is not None:
327 if cleanup is not None:
328 os.unlink(cleanup)
328 os.unlink(cleanup)
329
329
330 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
330 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
331 changes=None, text=False, opts={}):
331 changes=None, text=False, opts={}):
332 if not node1:
332 if not node1:
333 node1 = repo.dirstate.parents()[0]
333 node1 = repo.dirstate.parents()[0]
334 # reading the data for node1 early allows it to play nicely
334 # reading the data for node1 early allows it to play nicely
335 # with repo.changes and the revlog cache.
335 # with repo.changes and the revlog cache.
336 change = repo.changelog.read(node1)
336 change = repo.changelog.read(node1)
337 mmap = repo.manifest.read(change[0])
337 mmap = repo.manifest.read(change[0])
338 date1 = util.datestr(change[2])
338 date1 = util.datestr(change[2])
339
339
340 if not changes:
340 if not changes:
341 changes = repo.changes(node1, node2, files, match=match)
341 changes = repo.changes(node1, node2, files, match=match)
342 modified, added, removed, deleted, unknown = changes
342 modified, added, removed, deleted, unknown = changes
343 if files:
343 if files:
344 modified, added, removed = map(lambda x: filterfiles(files, x),
344 modified, added, removed = map(lambda x: filterfiles(files, x),
345 (modified, added, removed))
345 (modified, added, removed))
346
346
347 if not modified and not added and not removed:
347 if not modified and not added and not removed:
348 return
348 return
349
349
350 if node2:
350 if node2:
351 change = repo.changelog.read(node2)
351 change = repo.changelog.read(node2)
352 mmap2 = repo.manifest.read(change[0])
352 mmap2 = repo.manifest.read(change[0])
353 date2 = util.datestr(change[2])
353 date2 = util.datestr(change[2])
354 def read(f):
354 def read(f):
355 return repo.file(f).read(mmap2[f])
355 return repo.file(f).read(mmap2[f])
356 else:
356 else:
357 date2 = util.datestr()
357 date2 = util.datestr()
358 def read(f):
358 def read(f):
359 return repo.wread(f)
359 return repo.wread(f)
360
360
361 if ui.quiet:
361 if ui.quiet:
362 r = None
362 r = None
363 else:
363 else:
364 hexfunc = ui.verbose and hex or short
364 hexfunc = ui.verbose and hex or short
365 r = [hexfunc(node) for node in [node1, node2] if node]
365 r = [hexfunc(node) for node in [node1, node2] if node]
366
366
367 diffopts = ui.diffopts()
367 diffopts = ui.diffopts()
368 showfunc = opts.get('show_function') or diffopts['showfunc']
368 showfunc = opts.get('show_function') or diffopts['showfunc']
369 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
369 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
370 for f in modified:
370 for f in modified:
371 to = None
371 to = None
372 if f in mmap:
372 if f in mmap:
373 to = repo.file(f).read(mmap[f])
373 to = repo.file(f).read(mmap[f])
374 tn = read(f)
374 tn = read(f)
375 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
375 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
376 showfunc=showfunc, ignorews=ignorews))
376 showfunc=showfunc, ignorews=ignorews))
377 for f in added:
377 for f in added:
378 to = None
378 to = None
379 tn = read(f)
379 tn = read(f)
380 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
380 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
381 showfunc=showfunc, ignorews=ignorews))
381 showfunc=showfunc, ignorews=ignorews))
382 for f in removed:
382 for f in removed:
383 to = repo.file(f).read(mmap[f])
383 to = repo.file(f).read(mmap[f])
384 tn = None
384 tn = None
385 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
385 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
386 showfunc=showfunc, ignorews=ignorews))
386 showfunc=showfunc, ignorews=ignorews))
387
387
388 def trimuser(ui, name, rev, revcache):
388 def trimuser(ui, name, rev, revcache):
389 """trim the name of the user who committed a change"""
389 """trim the name of the user who committed a change"""
390 user = revcache.get(rev)
390 user = revcache.get(rev)
391 if user is None:
391 if user is None:
392 user = revcache[rev] = ui.shortuser(name)
392 user = revcache[rev] = ui.shortuser(name)
393 return user
393 return user
394
394
395 class changeset_templater(object):
395 class changeset_templater(object):
396 '''use templater module to format changeset information.'''
396 '''use templater module to format changeset information.'''
397
397
398 def __init__(self, ui, repo, mapfile):
398 def __init__(self, ui, repo, mapfile):
399 self.t = templater.templater(mapfile, templater.common_filters,
399 self.t = templater.templater(mapfile, templater.common_filters,
400 cache={'parent': '{rev}:{node|short} ',
400 cache={'parent': '{rev}:{node|short} ',
401 'manifest': '{rev}:{node|short}'})
401 'manifest': '{rev}:{node|short}'})
402 self.ui = ui
402 self.ui = ui
403 self.repo = repo
403 self.repo = repo
404
404
405 def use_template(self, t):
405 def use_template(self, t):
406 '''set template string to use'''
406 '''set template string to use'''
407 self.t.cache['changeset'] = t
407 self.t.cache['changeset'] = t
408
408
409 def write(self, thing):
409 def write(self, thing):
410 '''write expanded template.
410 '''write expanded template.
411 uses in-order recursive traverse of iterators.'''
411 uses in-order recursive traverse of iterators.'''
412 for t in thing:
412 for t in thing:
413 if hasattr(t, '__iter__'):
413 if hasattr(t, '__iter__'):
414 self.write(t)
414 self.write(t)
415 else:
415 else:
416 self.ui.write(t)
416 self.ui.write(t)
417
417
418 def show(self, rev=0, changenode=None, brinfo=None):
418 def show(self, rev=0, changenode=None, brinfo=None):
419 '''show a single changeset or file revision'''
419 '''show a single changeset or file revision'''
420 log = self.repo.changelog
420 log = self.repo.changelog
421 if changenode is None:
421 if changenode is None:
422 changenode = log.node(rev)
422 changenode = log.node(rev)
423 elif not rev:
423 elif not rev:
424 rev = log.rev(changenode)
424 rev = log.rev(changenode)
425
425
426 changes = log.read(changenode)
426 changes = log.read(changenode)
427
427
428 def showlist(name, values, plural=None, **args):
428 def showlist(name, values, plural=None, **args):
429 '''expand set of values.
429 '''expand set of values.
430 name is name of key in template map.
430 name is name of key in template map.
431 values is list of strings or dicts.
431 values is list of strings or dicts.
432 plural is plural of name, if not simply name + 's'.
432 plural is plural of name, if not simply name + 's'.
433
433
434 expansion works like this, given name 'foo'.
434 expansion works like this, given name 'foo'.
435
435
436 if values is empty, expand 'no_foos'.
436 if values is empty, expand 'no_foos'.
437
437
438 if 'foo' not in template map, return values as a string,
438 if 'foo' not in template map, return values as a string,
439 joined by space.
439 joined by space.
440
440
441 expand 'start_foos'.
441 expand 'start_foos'.
442
442
443 for each value, expand 'foo'. if 'last_foo' in template
443 for each value, expand 'foo'. if 'last_foo' in template
444 map, expand it instead of 'foo' for last key.
444 map, expand it instead of 'foo' for last key.
445
445
446 expand 'end_foos'.
446 expand 'end_foos'.
447 '''
447 '''
448 if plural: names = plural
448 if plural: names = plural
449 else: names = name + 's'
449 else: names = name + 's'
450 if not values:
450 if not values:
451 noname = 'no_' + names
451 noname = 'no_' + names
452 if noname in self.t:
452 if noname in self.t:
453 yield self.t(noname, **args)
453 yield self.t(noname, **args)
454 return
454 return
455 if name not in self.t:
455 if name not in self.t:
456 if isinstance(values[0], str):
456 if isinstance(values[0], str):
457 yield ' '.join(values)
457 yield ' '.join(values)
458 else:
458 else:
459 for v in values:
459 for v in values:
460 yield dict(v, **args)
460 yield dict(v, **args)
461 return
461 return
462 startname = 'start_' + names
462 startname = 'start_' + names
463 if startname in self.t:
463 if startname in self.t:
464 yield self.t(startname, **args)
464 yield self.t(startname, **args)
465 vargs = args.copy()
465 vargs = args.copy()
466 def one(v, tag=name):
466 def one(v, tag=name):
467 try:
467 try:
468 vargs.update(v)
468 vargs.update(v)
469 except (AttributeError, ValueError):
469 except (AttributeError, ValueError):
470 try:
470 try:
471 for a, b in v:
471 for a, b in v:
472 vargs[a] = b
472 vargs[a] = b
473 except ValueError:
473 except ValueError:
474 vargs[name] = v
474 vargs[name] = v
475 return self.t(tag, **vargs)
475 return self.t(tag, **vargs)
476 lastname = 'last_' + name
476 lastname = 'last_' + name
477 if lastname in self.t:
477 if lastname in self.t:
478 last = values.pop()
478 last = values.pop()
479 else:
479 else:
480 last = None
480 last = None
481 for v in values:
481 for v in values:
482 yield one(v)
482 yield one(v)
483 if last is not None:
483 if last is not None:
484 yield one(last, tag=lastname)
484 yield one(last, tag=lastname)
485 endname = 'end_' + names
485 endname = 'end_' + names
486 if endname in self.t:
486 if endname in self.t:
487 yield self.t(endname, **args)
487 yield self.t(endname, **args)
488
488
489 if brinfo:
489 if brinfo:
490 def showbranches(**args):
490 def showbranches(**args):
491 if changenode in brinfo:
491 if changenode in brinfo:
492 for x in showlist('branch', brinfo[changenode],
492 for x in showlist('branch', brinfo[changenode],
493 plural='branches', **args):
493 plural='branches', **args):
494 yield x
494 yield x
495 else:
495 else:
496 showbranches = ''
496 showbranches = ''
497
497
498 if self.ui.debugflag:
498 if self.ui.debugflag:
499 def showmanifest(**args):
499 def showmanifest(**args):
500 args = args.copy()
500 args = args.copy()
501 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
501 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
502 node=hex(changes[0])))
502 node=hex(changes[0])))
503 yield self.t('manifest', **args)
503 yield self.t('manifest', **args)
504 else:
504 else:
505 showmanifest = ''
505 showmanifest = ''
506
506
507 def showparents(**args):
507 def showparents(**args):
508 parents = [[('rev', log.rev(p)), ('node', hex(p))]
508 parents = [[('rev', log.rev(p)), ('node', hex(p))]
509 for p in log.parents(changenode)
509 for p in log.parents(changenode)
510 if self.ui.debugflag or p != nullid]
510 if self.ui.debugflag or p != nullid]
511 if (not self.ui.debugflag and len(parents) == 1 and
511 if (not self.ui.debugflag and len(parents) == 1 and
512 parents[0][0][1] == rev - 1):
512 parents[0][0][1] == rev - 1):
513 return
513 return
514 for x in showlist('parent', parents, **args):
514 for x in showlist('parent', parents, **args):
515 yield x
515 yield x
516
516
517 def showtags(**args):
517 def showtags(**args):
518 for x in showlist('tag', self.repo.nodetags(changenode), **args):
518 for x in showlist('tag', self.repo.nodetags(changenode), **args):
519 yield x
519 yield x
520
520
521 if self.ui.debugflag:
521 if self.ui.debugflag:
522 files = self.repo.changes(log.parents(changenode)[0], changenode)
522 files = self.repo.changes(log.parents(changenode)[0], changenode)
523 def showfiles(**args):
523 def showfiles(**args):
524 for x in showlist('file', files[0], **args): yield x
524 for x in showlist('file', files[0], **args): yield x
525 def showadds(**args):
525 def showadds(**args):
526 for x in showlist('file_add', files[1], **args): yield x
526 for x in showlist('file_add', files[1], **args): yield x
527 def showdels(**args):
527 def showdels(**args):
528 for x in showlist('file_del', files[2], **args): yield x
528 for x in showlist('file_del', files[2], **args): yield x
529 else:
529 else:
530 def showfiles(**args):
530 def showfiles(**args):
531 for x in showlist('file', changes[3], **args): yield x
531 for x in showlist('file', changes[3], **args): yield x
532 showadds = ''
532 showadds = ''
533 showdels = ''
533 showdels = ''
534
534
535 props = {
535 props = {
536 'author': changes[1],
536 'author': changes[1],
537 'branches': showbranches,
537 'branches': showbranches,
538 'date': changes[2],
538 'date': changes[2],
539 'desc': changes[4],
539 'desc': changes[4],
540 'file_adds': showadds,
540 'file_adds': showadds,
541 'file_dels': showdels,
541 'file_dels': showdels,
542 'files': showfiles,
542 'files': showfiles,
543 'manifest': showmanifest,
543 'manifest': showmanifest,
544 'node': hex(changenode),
544 'node': hex(changenode),
545 'parents': showparents,
545 'parents': showparents,
546 'rev': rev,
546 'rev': rev,
547 'tags': showtags,
547 'tags': showtags,
548 }
548 }
549
549
550 try:
550 try:
551 if self.ui.debugflag and 'changeset_debug' in self.t:
551 if self.ui.debugflag and 'changeset_debug' in self.t:
552 key = 'changeset_debug'
552 key = 'changeset_debug'
553 elif self.ui.quiet and 'changeset_quiet' in self.t:
553 elif self.ui.quiet and 'changeset_quiet' in self.t:
554 key = 'changeset_quiet'
554 key = 'changeset_quiet'
555 elif self.ui.verbose and 'changeset_verbose' in self.t:
555 elif self.ui.verbose and 'changeset_verbose' in self.t:
556 key = 'changeset_verbose'
556 key = 'changeset_verbose'
557 else:
557 else:
558 key = 'changeset'
558 key = 'changeset'
559 self.write(self.t(key, **props))
559 self.write(self.t(key, **props))
560 except KeyError, inst:
560 except KeyError, inst:
561 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
561 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
562 inst.args[0]))
562 inst.args[0]))
563 except SyntaxError, inst:
563 except SyntaxError, inst:
564 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
564 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
565
565
566 class changeset_printer(object):
566 class changeset_printer(object):
567 '''show changeset information when templating not requested.'''
567 '''show changeset information when templating not requested.'''
568
568
569 def __init__(self, ui, repo):
569 def __init__(self, ui, repo):
570 self.ui = ui
570 self.ui = ui
571 self.repo = repo
571 self.repo = repo
572
572
573 def show(self, rev=0, changenode=None, brinfo=None):
573 def show(self, rev=0, changenode=None, brinfo=None):
574 '''show a single changeset or file revision'''
574 '''show a single changeset or file revision'''
575 log = self.repo.changelog
575 log = self.repo.changelog
576 if changenode is None:
576 if changenode is None:
577 changenode = log.node(rev)
577 changenode = log.node(rev)
578 elif not rev:
578 elif not rev:
579 rev = log.rev(changenode)
579 rev = log.rev(changenode)
580
580
581 if self.ui.quiet:
581 if self.ui.quiet:
582 self.ui.write("%d:%s\n" % (rev, short(changenode)))
582 self.ui.write("%d:%s\n" % (rev, short(changenode)))
583 return
583 return
584
584
585 changes = log.read(changenode)
585 changes = log.read(changenode)
586 date = util.datestr(changes[2])
586 date = util.datestr(changes[2])
587
587
588 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
588 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
589 for p in log.parents(changenode)
589 for p in log.parents(changenode)
590 if self.ui.debugflag or p != nullid]
590 if self.ui.debugflag or p != nullid]
591 if (not self.ui.debugflag and len(parents) == 1 and
591 if (not self.ui.debugflag and len(parents) == 1 and
592 parents[0][0] == rev-1):
592 parents[0][0] == rev-1):
593 parents = []
593 parents = []
594
594
595 if self.ui.verbose:
595 if self.ui.verbose:
596 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
596 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
597 else:
597 else:
598 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
598 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
599
599
600 for tag in self.repo.nodetags(changenode):
600 for tag in self.repo.nodetags(changenode):
601 self.ui.status(_("tag: %s\n") % tag)
601 self.ui.status(_("tag: %s\n") % tag)
602 for parent in parents:
602 for parent in parents:
603 self.ui.write(_("parent: %d:%s\n") % parent)
603 self.ui.write(_("parent: %d:%s\n") % parent)
604
604
605 if brinfo and changenode in brinfo:
605 if brinfo and changenode in brinfo:
606 br = brinfo[changenode]
606 br = brinfo[changenode]
607 self.ui.write(_("branch: %s\n") % " ".join(br))
607 self.ui.write(_("branch: %s\n") % " ".join(br))
608
608
609 self.ui.debug(_("manifest: %d:%s\n") %
609 self.ui.debug(_("manifest: %d:%s\n") %
610 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
610 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
611 self.ui.status(_("user: %s\n") % changes[1])
611 self.ui.status(_("user: %s\n") % changes[1])
612 self.ui.status(_("date: %s\n") % date)
612 self.ui.status(_("date: %s\n") % date)
613
613
614 if self.ui.debugflag:
614 if self.ui.debugflag:
615 files = self.repo.changes(log.parents(changenode)[0], changenode)
615 files = self.repo.changes(log.parents(changenode)[0], changenode)
616 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
616 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
617 files):
617 files):
618 if value:
618 if value:
619 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
619 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
620 else:
620 else:
621 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
621 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
622
622
623 description = changes[4].strip()
623 description = changes[4].strip()
624 if description:
624 if description:
625 if self.ui.verbose:
625 if self.ui.verbose:
626 self.ui.status(_("description:\n"))
626 self.ui.status(_("description:\n"))
627 self.ui.status(description)
627 self.ui.status(description)
628 self.ui.status("\n\n")
628 self.ui.status("\n\n")
629 else:
629 else:
630 self.ui.status(_("summary: %s\n") %
630 self.ui.status(_("summary: %s\n") %
631 description.splitlines()[0])
631 description.splitlines()[0])
632 self.ui.status("\n")
632 self.ui.status("\n")
633
633
634 def show_changeset(ui, repo, opts):
634 def show_changeset(ui, repo, opts):
635 '''show one changeset. uses template or regular display. caller
635 '''show one changeset. uses template or regular display. caller
636 can pass in 'style' and 'template' options in opts.'''
636 can pass in 'style' and 'template' options in opts.'''
637
637
638 tmpl = opts.get('template')
638 tmpl = opts.get('template')
639 if tmpl:
639 if tmpl:
640 tmpl = templater.parsestring(tmpl, quoted=False)
640 tmpl = templater.parsestring(tmpl, quoted=False)
641 else:
641 else:
642 tmpl = ui.config('ui', 'logtemplate')
642 tmpl = ui.config('ui', 'logtemplate')
643 if tmpl: tmpl = templater.parsestring(tmpl)
643 if tmpl: tmpl = templater.parsestring(tmpl)
644 mapfile = opts.get('style') or ui.config('ui', 'style')
644 mapfile = opts.get('style') or ui.config('ui', 'style')
645 if tmpl or mapfile:
645 if tmpl or mapfile:
646 if mapfile:
646 if mapfile:
647 if not os.path.isfile(mapfile):
647 if not os.path.isfile(mapfile):
648 mapname = templater.templatepath('map-cmdline.' + mapfile)
648 mapname = templater.templatepath('map-cmdline.' + mapfile)
649 if not mapname: mapname = templater.templatepath(mapfile)
649 if not mapname: mapname = templater.templatepath(mapfile)
650 if mapname: mapfile = mapname
650 if mapname: mapfile = mapname
651 try:
651 try:
652 t = changeset_templater(ui, repo, mapfile)
652 t = changeset_templater(ui, repo, mapfile)
653 except SyntaxError, inst:
653 except SyntaxError, inst:
654 raise util.Abort(inst.args[0])
654 raise util.Abort(inst.args[0])
655 if tmpl: t.use_template(tmpl)
655 if tmpl: t.use_template(tmpl)
656 return t
656 return t
657 return changeset_printer(ui, repo)
657 return changeset_printer(ui, repo)
658
658
659 def show_version(ui):
659 def show_version(ui):
660 """output version and copyright information"""
660 """output version and copyright information"""
661 ui.write(_("Mercurial Distributed SCM (version %s)\n")
661 ui.write(_("Mercurial Distributed SCM (version %s)\n")
662 % version.get_version())
662 % version.get_version())
663 ui.status(_(
663 ui.status(_(
664 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
664 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
665 "This is free software; see the source for copying conditions. "
665 "This is free software; see the source for copying conditions. "
666 "There is NO\nwarranty; "
666 "There is NO\nwarranty; "
667 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
667 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
668 ))
668 ))
669
669
670 def help_(ui, cmd=None, with_version=False):
670 def help_(ui, cmd=None, with_version=False):
671 """show help for a given command or all commands"""
671 """show help for a given command or all commands"""
672 option_lists = []
672 option_lists = []
673 if cmd and cmd != 'shortlist':
673 if cmd and cmd != 'shortlist':
674 if with_version:
674 if with_version:
675 show_version(ui)
675 show_version(ui)
676 ui.write('\n')
676 ui.write('\n')
677 aliases, i = find(cmd)
677 aliases, i = find(cmd)
678 # synopsis
678 # synopsis
679 ui.write("%s\n\n" % i[2])
679 ui.write("%s\n\n" % i[2])
680
680
681 # description
681 # description
682 doc = i[0].__doc__
682 doc = i[0].__doc__
683 if not doc:
683 if not doc:
684 doc = _("(No help text available)")
684 doc = _("(No help text available)")
685 if ui.quiet:
685 if ui.quiet:
686 doc = doc.splitlines(0)[0]
686 doc = doc.splitlines(0)[0]
687 ui.write("%s\n" % doc.rstrip())
687 ui.write("%s\n" % doc.rstrip())
688
688
689 if not ui.quiet:
689 if not ui.quiet:
690 # aliases
690 # aliases
691 if len(aliases) > 1:
691 if len(aliases) > 1:
692 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
692 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
693
693
694 # options
694 # options
695 if i[1]:
695 if i[1]:
696 option_lists.append(("options", i[1]))
696 option_lists.append(("options", i[1]))
697
697
698 else:
698 else:
699 # program name
699 # program name
700 if ui.verbose or with_version:
700 if ui.verbose or with_version:
701 show_version(ui)
701 show_version(ui)
702 else:
702 else:
703 ui.status(_("Mercurial Distributed SCM\n"))
703 ui.status(_("Mercurial Distributed SCM\n"))
704 ui.status('\n')
704 ui.status('\n')
705
705
706 # list of commands
706 # list of commands
707 if cmd == "shortlist":
707 if cmd == "shortlist":
708 ui.status(_('basic commands (use "hg help" '
708 ui.status(_('basic commands (use "hg help" '
709 'for the full list or option "-v" for details):\n\n'))
709 'for the full list or option "-v" for details):\n\n'))
710 elif ui.verbose:
710 elif ui.verbose:
711 ui.status(_('list of commands:\n\n'))
711 ui.status(_('list of commands:\n\n'))
712 else:
712 else:
713 ui.status(_('list of commands (use "hg help -v" '
713 ui.status(_('list of commands (use "hg help -v" '
714 'to show aliases and global options):\n\n'))
714 'to show aliases and global options):\n\n'))
715
715
716 h = {}
716 h = {}
717 cmds = {}
717 cmds = {}
718 for c, e in table.items():
718 for c, e in table.items():
719 f = c.split("|")[0]
719 f = c.split("|")[0]
720 if cmd == "shortlist" and not f.startswith("^"):
720 if cmd == "shortlist" and not f.startswith("^"):
721 continue
721 continue
722 f = f.lstrip("^")
722 f = f.lstrip("^")
723 if not ui.debugflag and f.startswith("debug"):
723 if not ui.debugflag and f.startswith("debug"):
724 continue
724 continue
725 doc = e[0].__doc__
725 doc = e[0].__doc__
726 if not doc:
726 if not doc:
727 doc = _("(No help text available)")
727 doc = _("(No help text available)")
728 h[f] = doc.splitlines(0)[0].rstrip()
728 h[f] = doc.splitlines(0)[0].rstrip()
729 cmds[f] = c.lstrip("^")
729 cmds[f] = c.lstrip("^")
730
730
731 fns = h.keys()
731 fns = h.keys()
732 fns.sort()
732 fns.sort()
733 m = max(map(len, fns))
733 m = max(map(len, fns))
734 for f in fns:
734 for f in fns:
735 if ui.verbose:
735 if ui.verbose:
736 commands = cmds[f].replace("|",", ")
736 commands = cmds[f].replace("|",", ")
737 ui.write(" %s:\n %s\n"%(commands, h[f]))
737 ui.write(" %s:\n %s\n"%(commands, h[f]))
738 else:
738 else:
739 ui.write(' %-*s %s\n' % (m, f, h[f]))
739 ui.write(' %-*s %s\n' % (m, f, h[f]))
740
740
741 # global options
741 # global options
742 if ui.verbose:
742 if ui.verbose:
743 option_lists.append(("global options", globalopts))
743 option_lists.append(("global options", globalopts))
744
744
745 # list all option lists
745 # list all option lists
746 opt_output = []
746 opt_output = []
747 for title, options in option_lists:
747 for title, options in option_lists:
748 opt_output.append(("\n%s:\n" % title, None))
748 opt_output.append(("\n%s:\n" % title, None))
749 for shortopt, longopt, default, desc in options:
749 for shortopt, longopt, default, desc in options:
750 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
750 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
751 longopt and " --%s" % longopt),
751 longopt and " --%s" % longopt),
752 "%s%s" % (desc,
752 "%s%s" % (desc,
753 default
753 default
754 and _(" (default: %s)") % default
754 and _(" (default: %s)") % default
755 or "")))
755 or "")))
756
756
757 if opt_output:
757 if opt_output:
758 opts_len = max([len(line[0]) for line in opt_output if line[1]])
758 opts_len = max([len(line[0]) for line in opt_output if line[1]])
759 for first, second in opt_output:
759 for first, second in opt_output:
760 if second:
760 if second:
761 ui.write(" %-*s %s\n" % (opts_len, first, second))
761 ui.write(" %-*s %s\n" % (opts_len, first, second))
762 else:
762 else:
763 ui.write("%s\n" % first)
763 ui.write("%s\n" % first)
764
764
765 # Commands start here, listed alphabetically
765 # Commands start here, listed alphabetically
766
766
767 def add(ui, repo, *pats, **opts):
767 def add(ui, repo, *pats, **opts):
768 """add the specified files on the next commit
768 """add the specified files on the next commit
769
769
770 Schedule files to be version controlled and added to the repository.
770 Schedule files to be version controlled and added to the repository.
771
771
772 The files will be added to the repository at the next commit.
772 The files will be added to the repository at the next commit.
773
773
774 If no names are given, add all files in the repository.
774 If no names are given, add all files in the repository.
775 """
775 """
776
776
777 names = []
777 names = []
778 for src, abs, rel, exact in walk(repo, pats, opts):
778 for src, abs, rel, exact in walk(repo, pats, opts):
779 if exact:
779 if exact:
780 if ui.verbose:
780 if ui.verbose:
781 ui.status(_('adding %s\n') % rel)
781 ui.status(_('adding %s\n') % rel)
782 names.append(abs)
782 names.append(abs)
783 elif repo.dirstate.state(abs) == '?':
783 elif repo.dirstate.state(abs) == '?':
784 ui.status(_('adding %s\n') % rel)
784 ui.status(_('adding %s\n') % rel)
785 names.append(abs)
785 names.append(abs)
786 repo.add(names)
786 repo.add(names)
787
787
788 def addremove(ui, repo, *pats, **opts):
788 def addremove(ui, repo, *pats, **opts):
789 """add all new files, delete all missing files
789 """add all new files, delete all missing files
790
790
791 Add all new files and remove all missing files from the repository.
791 Add all new files and remove all missing files from the repository.
792
792
793 New files are ignored if they match any of the patterns in .hgignore. As
793 New files are ignored if they match any of the patterns in .hgignore. As
794 with add, these changes take effect at the next commit.
794 with add, these changes take effect at the next commit.
795 """
795 """
796 return addremove_lock(ui, repo, pats, opts)
796 return addremove_lock(ui, repo, pats, opts)
797
797
798 def addremove_lock(ui, repo, pats, opts, wlock=None):
798 def addremove_lock(ui, repo, pats, opts, wlock=None):
799 add, remove = [], []
799 add, remove = [], []
800 for src, abs, rel, exact in walk(repo, pats, opts):
800 for src, abs, rel, exact in walk(repo, pats, opts):
801 if src == 'f' and repo.dirstate.state(abs) == '?':
801 if src == 'f' and repo.dirstate.state(abs) == '?':
802 add.append(abs)
802 add.append(abs)
803 if ui.verbose or not exact:
803 if ui.verbose or not exact:
804 ui.status(_('adding %s\n') % ((pats and rel) or abs))
804 ui.status(_('adding %s\n') % ((pats and rel) or abs))
805 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
805 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
806 remove.append(abs)
806 remove.append(abs)
807 if ui.verbose or not exact:
807 if ui.verbose or not exact:
808 ui.status(_('removing %s\n') % ((pats and rel) or abs))
808 ui.status(_('removing %s\n') % ((pats and rel) or abs))
809 repo.add(add, wlock=wlock)
809 repo.add(add, wlock=wlock)
810 repo.remove(remove, wlock=wlock)
810 repo.remove(remove, wlock=wlock)
811
811
812 def annotate(ui, repo, *pats, **opts):
812 def annotate(ui, repo, *pats, **opts):
813 """show changeset information per file line
813 """show changeset information per file line
814
814
815 List changes in files, showing the revision id responsible for each line
815 List changes in files, showing the revision id responsible for each line
816
816
817 This command is useful to discover who did a change or when a change took
817 This command is useful to discover who did a change or when a change took
818 place.
818 place.
819
819
820 Without the -a option, annotate will avoid processing files it
820 Without the -a option, annotate will avoid processing files it
821 detects as binary. With -a, annotate will generate an annotation
821 detects as binary. With -a, annotate will generate an annotation
822 anyway, probably with undesirable results.
822 anyway, probably with undesirable results.
823 """
823 """
824 def getnode(rev):
824 def getnode(rev):
825 return short(repo.changelog.node(rev))
825 return short(repo.changelog.node(rev))
826
826
827 ucache = {}
827 ucache = {}
828 def getname(rev):
828 def getname(rev):
829 cl = repo.changelog.read(repo.changelog.node(rev))
829 cl = repo.changelog.read(repo.changelog.node(rev))
830 return trimuser(ui, cl[1], rev, ucache)
830 return trimuser(ui, cl[1], rev, ucache)
831
831
832 dcache = {}
832 dcache = {}
833 def getdate(rev):
833 def getdate(rev):
834 datestr = dcache.get(rev)
834 datestr = dcache.get(rev)
835 if datestr is None:
835 if datestr is None:
836 cl = repo.changelog.read(repo.changelog.node(rev))
836 cl = repo.changelog.read(repo.changelog.node(rev))
837 datestr = dcache[rev] = util.datestr(cl[2])
837 datestr = dcache[rev] = util.datestr(cl[2])
838 return datestr
838 return datestr
839
839
840 if not pats:
840 if not pats:
841 raise util.Abort(_('at least one file name or pattern required'))
841 raise util.Abort(_('at least one file name or pattern required'))
842
842
843 opmap = [['user', getname], ['number', str], ['changeset', getnode],
843 opmap = [['user', getname], ['number', str], ['changeset', getnode],
844 ['date', getdate]]
844 ['date', getdate]]
845 if not opts['user'] and not opts['changeset'] and not opts['date']:
845 if not opts['user'] and not opts['changeset'] and not opts['date']:
846 opts['number'] = 1
846 opts['number'] = 1
847
847
848 if opts['rev']:
848 if opts['rev']:
849 node = repo.changelog.lookup(opts['rev'])
849 node = repo.changelog.lookup(opts['rev'])
850 else:
850 else:
851 node = repo.dirstate.parents()[0]
851 node = repo.dirstate.parents()[0]
852 change = repo.changelog.read(node)
852 change = repo.changelog.read(node)
853 mmap = repo.manifest.read(change[0])
853 mmap = repo.manifest.read(change[0])
854
854
855 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
855 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
856 f = repo.file(abs)
856 f = repo.file(abs)
857 if not opts['text'] and util.binary(f.read(mmap[abs])):
857 if not opts['text'] and util.binary(f.read(mmap[abs])):
858 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
858 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
859 continue
859 continue
860
860
861 lines = f.annotate(mmap[abs])
861 lines = f.annotate(mmap[abs])
862 pieces = []
862 pieces = []
863
863
864 for o, f in opmap:
864 for o, f in opmap:
865 if opts[o]:
865 if opts[o]:
866 l = [f(n) for n, dummy in lines]
866 l = [f(n) for n, dummy in lines]
867 if l:
867 if l:
868 m = max(map(len, l))
868 m = max(map(len, l))
869 pieces.append(["%*s" % (m, x) for x in l])
869 pieces.append(["%*s" % (m, x) for x in l])
870
870
871 if pieces:
871 if pieces:
872 for p, l in zip(zip(*pieces), lines):
872 for p, l in zip(zip(*pieces), lines):
873 ui.write("%s: %s" % (" ".join(p), l[1]))
873 ui.write("%s: %s" % (" ".join(p), l[1]))
874
874
875 def bundle(ui, repo, fname, dest="default-push", **opts):
875 def bundle(ui, repo, fname, dest="default-push", **opts):
876 """create a changegroup file
876 """create a changegroup file
877
877
878 Generate a compressed changegroup file collecting all changesets
878 Generate a compressed changegroup file collecting all changesets
879 not found in the other repository.
879 not found in the other repository.
880
880
881 This file can then be transferred using conventional means and
881 This file can then be transferred using conventional means and
882 applied to another repository with the unbundle command. This is
882 applied to another repository with the unbundle command. This is
883 useful when native push and pull are not available or when
883 useful when native push and pull are not available or when
884 exporting an entire repository is undesirable. The standard file
884 exporting an entire repository is undesirable. The standard file
885 extension is ".hg".
885 extension is ".hg".
886
886
887 Unlike import/export, this exactly preserves all changeset
887 Unlike import/export, this exactly preserves all changeset
888 contents including permissions, rename data, and revision history.
888 contents including permissions, rename data, and revision history.
889 """
889 """
890 dest = ui.expandpath(dest)
890 dest = ui.expandpath(dest)
891 other = hg.repository(ui, dest)
891 other = hg.repository(ui, dest)
892 o = repo.findoutgoing(other, force=opts['force'])
892 o = repo.findoutgoing(other, force=opts['force'])
893 cg = repo.changegroup(o, 'bundle')
893 cg = repo.changegroup(o, 'bundle')
894 write_bundle(cg, fname)
894 write_bundle(cg, fname)
895
895
896 def cat(ui, repo, file1, *pats, **opts):
896 def cat(ui, repo, file1, *pats, **opts):
897 """output the latest or given revisions of files
897 """output the latest or given revisions of files
898
898
899 Print the specified files as they were at the given revision.
899 Print the specified files as they were at the given revision.
900 If no revision is given then the tip is used.
900 If no revision is given then the tip is used.
901
901
902 Output may be to a file, in which case the name of the file is
902 Output may be to a file, in which case the name of the file is
903 given using a format string. The formatting rules are the same as
903 given using a format string. The formatting rules are the same as
904 for the export command, with the following additions:
904 for the export command, with the following additions:
905
905
906 %s basename of file being printed
906 %s basename of file being printed
907 %d dirname of file being printed, or '.' if in repo root
907 %d dirname of file being printed, or '.' if in repo root
908 %p root-relative path name of file being printed
908 %p root-relative path name of file being printed
909 """
909 """
910 mf = {}
910 mf = {}
911 rev = opts['rev']
911 rev = opts['rev']
912 if rev:
912 if rev:
913 node = repo.lookup(rev)
913 node = repo.lookup(rev)
914 else:
914 else:
915 node = repo.changelog.tip()
915 node = repo.changelog.tip()
916 change = repo.changelog.read(node)
916 change = repo.changelog.read(node)
917 mf = repo.manifest.read(change[0])
917 mf = repo.manifest.read(change[0])
918 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
918 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
919 r = repo.file(abs)
919 r = repo.file(abs)
920 n = mf[abs]
920 n = mf[abs]
921 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
921 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
922 fp.write(r.read(n))
922 fp.write(r.read(n))
923
923
924 def clone(ui, source, dest=None, **opts):
924 def clone(ui, source, dest=None, **opts):
925 """make a copy of an existing repository
925 """make a copy of an existing repository
926
926
927 Create a copy of an existing repository in a new directory.
927 Create a copy of an existing repository in a new directory.
928
928
929 If no destination directory name is specified, it defaults to the
929 If no destination directory name is specified, it defaults to the
930 basename of the source.
930 basename of the source.
931
931
932 The location of the source is added to the new repository's
932 The location of the source is added to the new repository's
933 .hg/hgrc file, as the default to be used for future pulls.
933 .hg/hgrc file, as the default to be used for future pulls.
934
934
935 For efficiency, hardlinks are used for cloning whenever the source
935 For efficiency, hardlinks are used for cloning whenever the source
936 and destination are on the same filesystem. Some filesystems,
936 and destination are on the same filesystem. Some filesystems,
937 such as AFS, implement hardlinking incorrectly, but do not report
937 such as AFS, implement hardlinking incorrectly, but do not report
938 errors. In these cases, use the --pull option to avoid
938 errors. In these cases, use the --pull option to avoid
939 hardlinking.
939 hardlinking.
940
940
941 See pull for valid source format details.
941 See pull for valid source format details.
942 """
942 """
943 if dest is None:
943 if dest is None:
944 dest = os.path.basename(os.path.normpath(source))
944 dest = os.path.basename(os.path.normpath(source))
945
945
946 if os.path.exists(dest):
946 if os.path.exists(dest):
947 raise util.Abort(_("destination '%s' already exists"), dest)
947 raise util.Abort(_("destination '%s' already exists"), dest)
948
948
949 dest = os.path.realpath(dest)
949 dest = os.path.realpath(dest)
950
950
951 class Dircleanup(object):
951 class Dircleanup(object):
952 def __init__(self, dir_):
952 def __init__(self, dir_):
953 self.rmtree = shutil.rmtree
953 self.rmtree = shutil.rmtree
954 self.dir_ = dir_
954 self.dir_ = dir_
955 os.mkdir(dir_)
955 os.mkdir(dir_)
956 def close(self):
956 def close(self):
957 self.dir_ = None
957 self.dir_ = None
958 def __del__(self):
958 def __del__(self):
959 if self.dir_:
959 if self.dir_:
960 self.rmtree(self.dir_, True)
960 self.rmtree(self.dir_, True)
961
961
962 if opts['ssh']:
962 if opts['ssh']:
963 ui.setconfig("ui", "ssh", opts['ssh'])
963 ui.setconfig("ui", "ssh", opts['ssh'])
964 if opts['remotecmd']:
964 if opts['remotecmd']:
965 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
965 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
966
966
967 source = ui.expandpath(source)
967 source = ui.expandpath(source)
968
968
969 d = Dircleanup(dest)
969 d = Dircleanup(dest)
970 abspath = source
970 abspath = source
971 other = hg.repository(ui, source)
971 other = hg.repository(ui, source)
972
972
973 copy = False
973 copy = False
974 if other.dev() != -1:
974 if other.dev() != -1:
975 abspath = os.path.abspath(source)
975 abspath = os.path.abspath(source)
976 if not opts['pull'] and not opts['rev']:
976 if not opts['pull'] and not opts['rev']:
977 copy = True
977 copy = True
978
978
979 if copy:
979 if copy:
980 try:
980 try:
981 # we use a lock here because if we race with commit, we
981 # we use a lock here because if we race with commit, we
982 # can end up with extra data in the cloned revlogs that's
982 # can end up with extra data in the cloned revlogs that's
983 # not pointed to by changesets, thus causing verify to
983 # not pointed to by changesets, thus causing verify to
984 # fail
984 # fail
985 l1 = other.lock()
985 l1 = other.lock()
986 except lock.LockException:
986 except lock.LockException:
987 copy = False
987 copy = False
988
988
989 if copy:
989 if copy:
990 # we lock here to avoid premature writing to the target
990 # we lock here to avoid premature writing to the target
991 os.mkdir(os.path.join(dest, ".hg"))
991 os.mkdir(os.path.join(dest, ".hg"))
992 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
992 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
993
993
994 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
994 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
995 for f in files.split():
995 for f in files.split():
996 src = os.path.join(source, ".hg", f)
996 src = os.path.join(source, ".hg", f)
997 dst = os.path.join(dest, ".hg", f)
997 dst = os.path.join(dest, ".hg", f)
998 try:
998 try:
999 util.copyfiles(src, dst)
999 util.copyfiles(src, dst)
1000 except OSError, inst:
1000 except OSError, inst:
1001 if inst.errno != errno.ENOENT:
1001 if inst.errno != errno.ENOENT:
1002 raise
1002 raise
1003
1003
1004 repo = hg.repository(ui, dest)
1004 repo = hg.repository(ui, dest)
1005
1005
1006 else:
1006 else:
1007 revs = None
1007 revs = None
1008 if opts['rev']:
1008 if opts['rev']:
1009 if not other.local():
1009 if not other.local():
1010 error = _("clone -r not supported yet for remote repositories.")
1010 error = _("clone -r not supported yet for remote repositories.")
1011 raise util.Abort(error)
1011 raise util.Abort(error)
1012 else:
1012 else:
1013 revs = [other.lookup(rev) for rev in opts['rev']]
1013 revs = [other.lookup(rev) for rev in opts['rev']]
1014 repo = hg.repository(ui, dest, create=1)
1014 repo = hg.repository(ui, dest, create=1)
1015 repo.pull(other, heads = revs)
1015 repo.pull(other, heads = revs)
1016
1016
1017 f = repo.opener("hgrc", "w", text=True)
1017 f = repo.opener("hgrc", "w", text=True)
1018 f.write("[paths]\n")
1018 f.write("[paths]\n")
1019 f.write("default = %s\n" % abspath)
1019 f.write("default = %s\n" % abspath)
1020 f.close()
1020 f.close()
1021
1021
1022 if not opts['noupdate']:
1022 if not opts['noupdate']:
1023 update(repo.ui, repo)
1023 update(repo.ui, repo)
1024
1024
1025 d.close()
1025 d.close()
1026
1026
1027 def commit(ui, repo, *pats, **opts):
1027 def commit(ui, repo, *pats, **opts):
1028 """commit the specified files or all outstanding changes
1028 """commit the specified files or all outstanding changes
1029
1029
1030 Commit changes to the given files into the repository.
1030 Commit changes to the given files into the repository.
1031
1031
1032 If a list of files is omitted, all changes reported by "hg status"
1032 If a list of files is omitted, all changes reported by "hg status"
1033 will be committed.
1033 will be committed.
1034
1034
1035 If no commit message is specified, the editor configured in your hgrc
1035 If no commit message is specified, the editor configured in your hgrc
1036 or in the EDITOR environment variable is started to enter a message.
1036 or in the EDITOR environment variable is started to enter a message.
1037 """
1037 """
1038 message = opts['message']
1038 message = opts['message']
1039 logfile = opts['logfile']
1039 logfile = opts['logfile']
1040
1040
1041 if message and logfile:
1041 if message and logfile:
1042 raise util.Abort(_('options --message and --logfile are mutually '
1042 raise util.Abort(_('options --message and --logfile are mutually '
1043 'exclusive'))
1043 'exclusive'))
1044 if not message and logfile:
1044 if not message and logfile:
1045 try:
1045 try:
1046 if logfile == '-':
1046 if logfile == '-':
1047 message = sys.stdin.read()
1047 message = sys.stdin.read()
1048 else:
1048 else:
1049 message = open(logfile).read()
1049 message = open(logfile).read()
1050 except IOError, inst:
1050 except IOError, inst:
1051 raise util.Abort(_("can't read commit message '%s': %s") %
1051 raise util.Abort(_("can't read commit message '%s': %s") %
1052 (logfile, inst.strerror))
1052 (logfile, inst.strerror))
1053
1053
1054 if opts['addremove']:
1054 if opts['addremove']:
1055 addremove(ui, repo, *pats, **opts)
1055 addremove(ui, repo, *pats, **opts)
1056 fns, match, anypats = matchpats(repo, pats, opts)
1056 fns, match, anypats = matchpats(repo, pats, opts)
1057 if pats:
1057 if pats:
1058 modified, added, removed, deleted, unknown = (
1058 modified, added, removed, deleted, unknown = (
1059 repo.changes(files=fns, match=match))
1059 repo.changes(files=fns, match=match))
1060 files = modified + added + removed
1060 files = modified + added + removed
1061 else:
1061 else:
1062 files = []
1062 files = []
1063 try:
1063 try:
1064 repo.commit(files, message, opts['user'], opts['date'], match)
1064 repo.commit(files, message, opts['user'], opts['date'], match)
1065 except ValueError, inst:
1065 except ValueError, inst:
1066 raise util.Abort(str(inst))
1066 raise util.Abort(str(inst))
1067
1067
1068 def docopy(ui, repo, pats, opts, wlock):
1068 def docopy(ui, repo, pats, opts, wlock):
1069 # called with the repo lock held
1069 # called with the repo lock held
1070 cwd = repo.getcwd()
1070 cwd = repo.getcwd()
1071 errors = 0
1071 errors = 0
1072 copied = []
1072 copied = []
1073 targets = {}
1073 targets = {}
1074
1074
1075 def okaytocopy(abs, rel, exact):
1075 def okaytocopy(abs, rel, exact):
1076 reasons = {'?': _('is not managed'),
1076 reasons = {'?': _('is not managed'),
1077 'a': _('has been marked for add'),
1077 'a': _('has been marked for add'),
1078 'r': _('has been marked for remove')}
1078 'r': _('has been marked for remove')}
1079 state = repo.dirstate.state(abs)
1079 state = repo.dirstate.state(abs)
1080 reason = reasons.get(state)
1080 reason = reasons.get(state)
1081 if reason:
1081 if reason:
1082 if state == 'a':
1082 if state == 'a':
1083 origsrc = repo.dirstate.copied(abs)
1083 origsrc = repo.dirstate.copied(abs)
1084 if origsrc is not None:
1084 if origsrc is not None:
1085 return origsrc
1085 return origsrc
1086 if exact:
1086 if exact:
1087 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1087 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1088 else:
1088 else:
1089 return abs
1089 return abs
1090
1090
1091 def copy(origsrc, abssrc, relsrc, target, exact):
1091 def copy(origsrc, abssrc, relsrc, target, exact):
1092 abstarget = util.canonpath(repo.root, cwd, target)
1092 abstarget = util.canonpath(repo.root, cwd, target)
1093 reltarget = util.pathto(cwd, abstarget)
1093 reltarget = util.pathto(cwd, abstarget)
1094 prevsrc = targets.get(abstarget)
1094 prevsrc = targets.get(abstarget)
1095 if prevsrc is not None:
1095 if prevsrc is not None:
1096 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1096 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1097 (reltarget, abssrc, prevsrc))
1097 (reltarget, abssrc, prevsrc))
1098 return
1098 return
1099 if (not opts['after'] and os.path.exists(reltarget) or
1099 if (not opts['after'] and os.path.exists(reltarget) or
1100 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1100 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1101 if not opts['force']:
1101 if not opts['force']:
1102 ui.warn(_('%s: not overwriting - file exists\n') %
1102 ui.warn(_('%s: not overwriting - file exists\n') %
1103 reltarget)
1103 reltarget)
1104 return
1104 return
1105 if not opts['after']:
1105 if not opts['after']:
1106 os.unlink(reltarget)
1106 os.unlink(reltarget)
1107 if opts['after']:
1107 if opts['after']:
1108 if not os.path.exists(reltarget):
1108 if not os.path.exists(reltarget):
1109 return
1109 return
1110 else:
1110 else:
1111 targetdir = os.path.dirname(reltarget) or '.'
1111 targetdir = os.path.dirname(reltarget) or '.'
1112 if not os.path.isdir(targetdir):
1112 if not os.path.isdir(targetdir):
1113 os.makedirs(targetdir)
1113 os.makedirs(targetdir)
1114 try:
1114 try:
1115 restore = repo.dirstate.state(abstarget) == 'r'
1115 restore = repo.dirstate.state(abstarget) == 'r'
1116 if restore:
1116 if restore:
1117 repo.undelete([abstarget], wlock)
1117 repo.undelete([abstarget], wlock)
1118 try:
1118 try:
1119 shutil.copyfile(relsrc, reltarget)
1119 shutil.copyfile(relsrc, reltarget)
1120 shutil.copymode(relsrc, reltarget)
1120 shutil.copymode(relsrc, reltarget)
1121 restore = False
1121 restore = False
1122 finally:
1122 finally:
1123 if restore:
1123 if restore:
1124 repo.remove([abstarget], wlock)
1124 repo.remove([abstarget], wlock)
1125 except shutil.Error, inst:
1125 except shutil.Error, inst:
1126 raise util.Abort(str(inst))
1126 raise util.Abort(str(inst))
1127 except IOError, inst:
1127 except IOError, inst:
1128 if inst.errno == errno.ENOENT:
1128 if inst.errno == errno.ENOENT:
1129 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1129 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1130 else:
1130 else:
1131 ui.warn(_('%s: cannot copy - %s\n') %
1131 ui.warn(_('%s: cannot copy - %s\n') %
1132 (relsrc, inst.strerror))
1132 (relsrc, inst.strerror))
1133 errors += 1
1133 errors += 1
1134 return
1134 return
1135 if ui.verbose or not exact:
1135 if ui.verbose or not exact:
1136 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1136 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1137 targets[abstarget] = abssrc
1137 targets[abstarget] = abssrc
1138 if abstarget != origsrc:
1138 if abstarget != origsrc:
1139 repo.copy(origsrc, abstarget, wlock)
1139 repo.copy(origsrc, abstarget, wlock)
1140 copied.append((abssrc, relsrc, exact))
1140 copied.append((abssrc, relsrc, exact))
1141
1141
1142 def targetpathfn(pat, dest, srcs):
1142 def targetpathfn(pat, dest, srcs):
1143 if os.path.isdir(pat):
1143 if os.path.isdir(pat):
1144 abspfx = util.canonpath(repo.root, cwd, pat)
1144 abspfx = util.canonpath(repo.root, cwd, pat)
1145 if destdirexists:
1145 if destdirexists:
1146 striplen = len(os.path.split(abspfx)[0])
1146 striplen = len(os.path.split(abspfx)[0])
1147 else:
1147 else:
1148 striplen = len(abspfx)
1148 striplen = len(abspfx)
1149 if striplen:
1149 if striplen:
1150 striplen += len(os.sep)
1150 striplen += len(os.sep)
1151 res = lambda p: os.path.join(dest, p[striplen:])
1151 res = lambda p: os.path.join(dest, p[striplen:])
1152 elif destdirexists:
1152 elif destdirexists:
1153 res = lambda p: os.path.join(dest, os.path.basename(p))
1153 res = lambda p: os.path.join(dest, os.path.basename(p))
1154 else:
1154 else:
1155 res = lambda p: dest
1155 res = lambda p: dest
1156 return res
1156 return res
1157
1157
1158 def targetpathafterfn(pat, dest, srcs):
1158 def targetpathafterfn(pat, dest, srcs):
1159 if util.patkind(pat, None)[0]:
1159 if util.patkind(pat, None)[0]:
1160 # a mercurial pattern
1160 # a mercurial pattern
1161 res = lambda p: os.path.join(dest, os.path.basename(p))
1161 res = lambda p: os.path.join(dest, os.path.basename(p))
1162 else:
1162 else:
1163 abspfx = util.canonpath(repo.root, cwd, pat)
1163 abspfx = util.canonpath(repo.root, cwd, pat)
1164 if len(abspfx) < len(srcs[0][0]):
1164 if len(abspfx) < len(srcs[0][0]):
1165 # A directory. Either the target path contains the last
1165 # A directory. Either the target path contains the last
1166 # component of the source path or it does not.
1166 # component of the source path or it does not.
1167 def evalpath(striplen):
1167 def evalpath(striplen):
1168 score = 0
1168 score = 0
1169 for s in srcs:
1169 for s in srcs:
1170 t = os.path.join(dest, s[0][striplen:])
1170 t = os.path.join(dest, s[0][striplen:])
1171 if os.path.exists(t):
1171 if os.path.exists(t):
1172 score += 1
1172 score += 1
1173 return score
1173 return score
1174
1174
1175 striplen = len(abspfx)
1175 striplen = len(abspfx)
1176 if striplen:
1176 if striplen:
1177 striplen += len(os.sep)
1177 striplen += len(os.sep)
1178 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1178 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1179 score = evalpath(striplen)
1179 score = evalpath(striplen)
1180 striplen1 = len(os.path.split(abspfx)[0])
1180 striplen1 = len(os.path.split(abspfx)[0])
1181 if striplen1:
1181 if striplen1:
1182 striplen1 += len(os.sep)
1182 striplen1 += len(os.sep)
1183 if evalpath(striplen1) > score:
1183 if evalpath(striplen1) > score:
1184 striplen = striplen1
1184 striplen = striplen1
1185 res = lambda p: os.path.join(dest, p[striplen:])
1185 res = lambda p: os.path.join(dest, p[striplen:])
1186 else:
1186 else:
1187 # a file
1187 # a file
1188 if destdirexists:
1188 if destdirexists:
1189 res = lambda p: os.path.join(dest, os.path.basename(p))
1189 res = lambda p: os.path.join(dest, os.path.basename(p))
1190 else:
1190 else:
1191 res = lambda p: dest
1191 res = lambda p: dest
1192 return res
1192 return res
1193
1193
1194
1194
1195 pats = list(pats)
1195 pats = list(pats)
1196 if not pats:
1196 if not pats:
1197 raise util.Abort(_('no source or destination specified'))
1197 raise util.Abort(_('no source or destination specified'))
1198 if len(pats) == 1:
1198 if len(pats) == 1:
1199 raise util.Abort(_('no destination specified'))
1199 raise util.Abort(_('no destination specified'))
1200 dest = pats.pop()
1200 dest = pats.pop()
1201 destdirexists = os.path.isdir(dest)
1201 destdirexists = os.path.isdir(dest)
1202 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1202 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1203 raise util.Abort(_('with multiple sources, destination must be an '
1203 raise util.Abort(_('with multiple sources, destination must be an '
1204 'existing directory'))
1204 'existing directory'))
1205 if opts['after']:
1205 if opts['after']:
1206 tfn = targetpathafterfn
1206 tfn = targetpathafterfn
1207 else:
1207 else:
1208 tfn = targetpathfn
1208 tfn = targetpathfn
1209 copylist = []
1209 copylist = []
1210 for pat in pats:
1210 for pat in pats:
1211 srcs = []
1211 srcs = []
1212 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1212 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1213 origsrc = okaytocopy(abssrc, relsrc, exact)
1213 origsrc = okaytocopy(abssrc, relsrc, exact)
1214 if origsrc:
1214 if origsrc:
1215 srcs.append((origsrc, abssrc, relsrc, exact))
1215 srcs.append((origsrc, abssrc, relsrc, exact))
1216 if not srcs:
1216 if not srcs:
1217 continue
1217 continue
1218 copylist.append((tfn(pat, dest, srcs), srcs))
1218 copylist.append((tfn(pat, dest, srcs), srcs))
1219 if not copylist:
1219 if not copylist:
1220 raise util.Abort(_('no files to copy'))
1220 raise util.Abort(_('no files to copy'))
1221
1221
1222 for targetpath, srcs in copylist:
1222 for targetpath, srcs in copylist:
1223 for origsrc, abssrc, relsrc, exact in srcs:
1223 for origsrc, abssrc, relsrc, exact in srcs:
1224 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1224 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1225
1225
1226 if errors:
1226 if errors:
1227 ui.warn(_('(consider using --after)\n'))
1227 ui.warn(_('(consider using --after)\n'))
1228 return errors, copied
1228 return errors, copied
1229
1229
1230 def copy(ui, repo, *pats, **opts):
1230 def copy(ui, repo, *pats, **opts):
1231 """mark files as copied for the next commit
1231 """mark files as copied for the next commit
1232
1232
1233 Mark dest as having copies of source files. If dest is a
1233 Mark dest as having copies of source files. If dest is a
1234 directory, copies are put in that directory. If dest is a file,
1234 directory, copies are put in that directory. If dest is a file,
1235 there can only be one source.
1235 there can only be one source.
1236
1236
1237 By default, this command copies the contents of files as they
1237 By default, this command copies the contents of files as they
1238 stand in the working directory. If invoked with --after, the
1238 stand in the working directory. If invoked with --after, the
1239 operation is recorded, but no copying is performed.
1239 operation is recorded, but no copying is performed.
1240
1240
1241 This command takes effect in the next commit.
1241 This command takes effect in the next commit.
1242
1242
1243 NOTE: This command should be treated as experimental. While it
1243 NOTE: This command should be treated as experimental. While it
1244 should properly record copied files, this information is not yet
1244 should properly record copied files, this information is not yet
1245 fully used by merge, nor fully reported by log.
1245 fully used by merge, nor fully reported by log.
1246 """
1246 """
1247 wlock = repo.wlock(0)
1247 wlock = repo.wlock(0)
1248 errs, copied = docopy(ui, repo, pats, opts, wlock)
1248 errs, copied = docopy(ui, repo, pats, opts, wlock)
1249 return errs
1249 return errs
1250
1250
1251 def debugancestor(ui, index, rev1, rev2):
1251 def debugancestor(ui, index, rev1, rev2):
1252 """find the ancestor revision of two revisions in a given index"""
1252 """find the ancestor revision of two revisions in a given index"""
1253 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "")
1253 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "")
1254 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1254 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1255 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1255 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1256
1256
1257 def debugcomplete(ui, cmd):
1257 def debugcomplete(ui, cmd):
1258 """returns the completion list associated with the given command"""
1258 """returns the completion list associated with the given command"""
1259 clist = findpossible(cmd).keys()
1259 clist = findpossible(cmd).keys()
1260 clist.sort()
1260 clist.sort()
1261 ui.write("%s\n" % " ".join(clist))
1261 ui.write("%s\n" % " ".join(clist))
1262
1262
1263 def debugrebuildstate(ui, repo, rev=None):
1263 def debugrebuildstate(ui, repo, rev=None):
1264 """rebuild the dirstate as it would look like for the given revision"""
1264 """rebuild the dirstate as it would look like for the given revision"""
1265 if not rev:
1265 if not rev:
1266 rev = repo.changelog.tip()
1266 rev = repo.changelog.tip()
1267 else:
1267 else:
1268 rev = repo.lookup(rev)
1268 rev = repo.lookup(rev)
1269 change = repo.changelog.read(rev)
1269 change = repo.changelog.read(rev)
1270 n = change[0]
1270 n = change[0]
1271 files = repo.manifest.readflags(n)
1271 files = repo.manifest.readflags(n)
1272 wlock = repo.wlock()
1272 wlock = repo.wlock()
1273 repo.dirstate.rebuild(rev, files.iteritems())
1273 repo.dirstate.rebuild(rev, files.iteritems())
1274
1274
1275 def debugcheckstate(ui, repo):
1275 def debugcheckstate(ui, repo):
1276 """validate the correctness of the current dirstate"""
1276 """validate the correctness of the current dirstate"""
1277 parent1, parent2 = repo.dirstate.parents()
1277 parent1, parent2 = repo.dirstate.parents()
1278 repo.dirstate.read()
1278 repo.dirstate.read()
1279 dc = repo.dirstate.map
1279 dc = repo.dirstate.map
1280 keys = dc.keys()
1280 keys = dc.keys()
1281 keys.sort()
1281 keys.sort()
1282 m1n = repo.changelog.read(parent1)[0]
1282 m1n = repo.changelog.read(parent1)[0]
1283 m2n = repo.changelog.read(parent2)[0]
1283 m2n = repo.changelog.read(parent2)[0]
1284 m1 = repo.manifest.read(m1n)
1284 m1 = repo.manifest.read(m1n)
1285 m2 = repo.manifest.read(m2n)
1285 m2 = repo.manifest.read(m2n)
1286 errors = 0
1286 errors = 0
1287 for f in dc:
1287 for f in dc:
1288 state = repo.dirstate.state(f)
1288 state = repo.dirstate.state(f)
1289 if state in "nr" and f not in m1:
1289 if state in "nr" and f not in m1:
1290 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1290 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1291 errors += 1
1291 errors += 1
1292 if state in "a" and f in m1:
1292 if state in "a" and f in m1:
1293 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1293 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1294 errors += 1
1294 errors += 1
1295 if state in "m" and f not in m1 and f not in m2:
1295 if state in "m" and f not in m1 and f not in m2:
1296 ui.warn(_("%s in state %s, but not in either manifest\n") %
1296 ui.warn(_("%s in state %s, but not in either manifest\n") %
1297 (f, state))
1297 (f, state))
1298 errors += 1
1298 errors += 1
1299 for f in m1:
1299 for f in m1:
1300 state = repo.dirstate.state(f)
1300 state = repo.dirstate.state(f)
1301 if state not in "nrm":
1301 if state not in "nrm":
1302 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1302 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1303 errors += 1
1303 errors += 1
1304 if errors:
1304 if errors:
1305 error = _(".hg/dirstate inconsistent with current parent's manifest")
1305 error = _(".hg/dirstate inconsistent with current parent's manifest")
1306 raise util.Abort(error)
1306 raise util.Abort(error)
1307
1307
1308 def debugconfig(ui, repo):
1308 def debugconfig(ui, repo):
1309 """show combined config settings from all hgrc files"""
1309 """show combined config settings from all hgrc files"""
1310 for section, name, value in ui.walkconfig():
1310 for section, name, value in ui.walkconfig():
1311 ui.write('%s.%s=%s\n' % (section, name, value))
1311 ui.write('%s.%s=%s\n' % (section, name, value))
1312
1312
1313 def debugsetparents(ui, repo, rev1, rev2=None):
1313 def debugsetparents(ui, repo, rev1, rev2=None):
1314 """manually set the parents of the current working directory
1314 """manually set the parents of the current working directory
1315
1315
1316 This is useful for writing repository conversion tools, but should
1316 This is useful for writing repository conversion tools, but should
1317 be used with care.
1317 be used with care.
1318 """
1318 """
1319
1319
1320 if not rev2:
1320 if not rev2:
1321 rev2 = hex(nullid)
1321 rev2 = hex(nullid)
1322
1322
1323 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1323 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1324
1324
1325 def debugstate(ui, repo):
1325 def debugstate(ui, repo):
1326 """show the contents of the current dirstate"""
1326 """show the contents of the current dirstate"""
1327 repo.dirstate.read()
1327 repo.dirstate.read()
1328 dc = repo.dirstate.map
1328 dc = repo.dirstate.map
1329 keys = dc.keys()
1329 keys = dc.keys()
1330 keys.sort()
1330 keys.sort()
1331 for file_ in keys:
1331 for file_ in keys:
1332 ui.write("%c %3o %10d %s %s\n"
1332 ui.write("%c %3o %10d %s %s\n"
1333 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1333 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1334 time.strftime("%x %X",
1334 time.strftime("%x %X",
1335 time.localtime(dc[file_][3])), file_))
1335 time.localtime(dc[file_][3])), file_))
1336 for f in repo.dirstate.copies:
1336 for f in repo.dirstate.copies:
1337 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1337 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1338
1338
1339 def debugdata(ui, file_, rev):
1339 def debugdata(ui, file_, rev):
1340 """dump the contents of an data file revision"""
1340 """dump the contents of an data file revision"""
1341 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1341 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1342 file_[:-2] + ".i", file_)
1342 file_[:-2] + ".i", file_)
1343 try:
1343 try:
1344 ui.write(r.revision(r.lookup(rev)))
1344 ui.write(r.revision(r.lookup(rev)))
1345 except KeyError:
1345 except KeyError:
1346 raise util.Abort(_('invalid revision identifier %s'), rev)
1346 raise util.Abort(_('invalid revision identifier %s'), rev)
1347
1347
1348 def debugindex(ui, file_):
1348 def debugindex(ui, file_):
1349 """dump the contents of an index file"""
1349 """dump the contents of an index file"""
1350 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1350 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1351 ui.write(" rev offset length base linkrev" +
1351 ui.write(" rev offset length base linkrev" +
1352 " nodeid p1 p2\n")
1352 " nodeid p1 p2\n")
1353 for i in range(r.count()):
1353 for i in range(r.count()):
1354 e = r.index[i]
1354 e = r.index[i]
1355 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1355 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1356 i, e[0], e[1], e[2], e[3],
1356 i, e[0], e[1], e[2], e[3],
1357 short(e[6]), short(e[4]), short(e[5])))
1357 short(e[6]), short(e[4]), short(e[5])))
1358
1358
1359 def debugindexdot(ui, file_):
1359 def debugindexdot(ui, file_):
1360 """dump an index DAG as a .dot file"""
1360 """dump an index DAG as a .dot file"""
1361 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1361 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1362 ui.write("digraph G {\n")
1362 ui.write("digraph G {\n")
1363 for i in range(r.count()):
1363 for i in range(r.count()):
1364 e = r.index[i]
1364 e = r.index[i]
1365 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1365 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1366 if e[5] != nullid:
1366 if e[5] != nullid:
1367 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1367 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1368 ui.write("}\n")
1368 ui.write("}\n")
1369
1369
1370 def debugrename(ui, repo, file, rev=None):
1370 def debugrename(ui, repo, file, rev=None):
1371 """dump rename information"""
1371 """dump rename information"""
1372 r = repo.file(relpath(repo, [file])[0])
1372 r = repo.file(relpath(repo, [file])[0])
1373 if rev:
1373 if rev:
1374 try:
1374 try:
1375 # assume all revision numbers are for changesets
1375 # assume all revision numbers are for changesets
1376 n = repo.lookup(rev)
1376 n = repo.lookup(rev)
1377 change = repo.changelog.read(n)
1377 change = repo.changelog.read(n)
1378 m = repo.manifest.read(change[0])
1378 m = repo.manifest.read(change[0])
1379 n = m[relpath(repo, [file])[0]]
1379 n = m[relpath(repo, [file])[0]]
1380 except (hg.RepoError, KeyError):
1380 except (hg.RepoError, KeyError):
1381 n = r.lookup(rev)
1381 n = r.lookup(rev)
1382 else:
1382 else:
1383 n = r.tip()
1383 n = r.tip()
1384 m = r.renamed(n)
1384 m = r.renamed(n)
1385 if m:
1385 if m:
1386 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1386 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1387 else:
1387 else:
1388 ui.write(_("not renamed\n"))
1388 ui.write(_("not renamed\n"))
1389
1389
1390 def debugwalk(ui, repo, *pats, **opts):
1390 def debugwalk(ui, repo, *pats, **opts):
1391 """show how files match on given patterns"""
1391 """show how files match on given patterns"""
1392 items = list(walk(repo, pats, opts))
1392 items = list(walk(repo, pats, opts))
1393 if not items:
1393 if not items:
1394 return
1394 return
1395 fmt = '%%s %%-%ds %%-%ds %%s' % (
1395 fmt = '%%s %%-%ds %%-%ds %%s' % (
1396 max([len(abs) for (src, abs, rel, exact) in items]),
1396 max([len(abs) for (src, abs, rel, exact) in items]),
1397 max([len(rel) for (src, abs, rel, exact) in items]))
1397 max([len(rel) for (src, abs, rel, exact) in items]))
1398 for src, abs, rel, exact in items:
1398 for src, abs, rel, exact in items:
1399 line = fmt % (src, abs, rel, exact and 'exact' or '')
1399 line = fmt % (src, abs, rel, exact and 'exact' or '')
1400 ui.write("%s\n" % line.rstrip())
1400 ui.write("%s\n" % line.rstrip())
1401
1401
1402 def diff(ui, repo, *pats, **opts):
1402 def diff(ui, repo, *pats, **opts):
1403 """diff repository (or selected files)
1403 """diff repository (or selected files)
1404
1404
1405 Show differences between revisions for the specified files.
1405 Show differences between revisions for the specified files.
1406
1406
1407 Differences between files are shown using the unified diff format.
1407 Differences between files are shown using the unified diff format.
1408
1408
1409 When two revision arguments are given, then changes are shown
1409 When two revision arguments are given, then changes are shown
1410 between those revisions. If only one revision is specified then
1410 between those revisions. If only one revision is specified then
1411 that revision is compared to the working directory, and, when no
1411 that revision is compared to the working directory, and, when no
1412 revisions are specified, the working directory files are compared
1412 revisions are specified, the working directory files are compared
1413 to its parent.
1413 to its parent.
1414
1414
1415 Without the -a option, diff will avoid generating diffs of files
1415 Without the -a option, diff will avoid generating diffs of files
1416 it detects as binary. With -a, diff will generate a diff anyway,
1416 it detects as binary. With -a, diff will generate a diff anyway,
1417 probably with undesirable results.
1417 probably with undesirable results.
1418 """
1418 """
1419 node1, node2 = None, None
1419 node1, node2 = None, None
1420 revs = [repo.lookup(x) for x in opts['rev']]
1420 revs = [repo.lookup(x) for x in opts['rev']]
1421
1421
1422 if len(revs) > 0:
1422 if len(revs) > 0:
1423 node1 = revs[0]
1423 node1 = revs[0]
1424 if len(revs) > 1:
1424 if len(revs) > 1:
1425 node2 = revs[1]
1425 node2 = revs[1]
1426 if len(revs) > 2:
1426 if len(revs) > 2:
1427 raise util.Abort(_("too many revisions to diff"))
1427 raise util.Abort(_("too many revisions to diff"))
1428
1428
1429 fns, matchfn, anypats = matchpats(repo, pats, opts)
1429 fns, matchfn, anypats = matchpats(repo, pats, opts)
1430
1430
1431 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1431 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1432 text=opts['text'], opts=opts)
1432 text=opts['text'], opts=opts)
1433
1433
1434 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1434 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1435 node = repo.lookup(changeset)
1435 node = repo.lookup(changeset)
1436 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1436 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1437 if opts['switch_parent']:
1437 if opts['switch_parent']:
1438 parents.reverse()
1438 parents.reverse()
1439 prev = (parents and parents[0]) or nullid
1439 prev = (parents and parents[0]) or nullid
1440 change = repo.changelog.read(node)
1440 change = repo.changelog.read(node)
1441
1441
1442 fp = make_file(repo, repo.changelog, opts['output'],
1442 fp = make_file(repo, repo.changelog, opts['output'],
1443 node=node, total=total, seqno=seqno,
1443 node=node, total=total, seqno=seqno,
1444 revwidth=revwidth)
1444 revwidth=revwidth)
1445 if fp != sys.stdout:
1445 if fp != sys.stdout:
1446 ui.note("%s\n" % fp.name)
1446 ui.note("%s\n" % fp.name)
1447
1447
1448 fp.write("# HG changeset patch\n")
1448 fp.write("# HG changeset patch\n")
1449 fp.write("# User %s\n" % change[1])
1449 fp.write("# User %s\n" % change[1])
1450 fp.write("# Node ID %s\n" % hex(node))
1450 fp.write("# Node ID %s\n" % hex(node))
1451 fp.write("# Parent %s\n" % hex(prev))
1451 fp.write("# Parent %s\n" % hex(prev))
1452 if len(parents) > 1:
1452 if len(parents) > 1:
1453 fp.write("# Parent %s\n" % hex(parents[1]))
1453 fp.write("# Parent %s\n" % hex(parents[1]))
1454 fp.write(change[4].rstrip())
1454 fp.write(change[4].rstrip())
1455 fp.write("\n\n")
1455 fp.write("\n\n")
1456
1456
1457 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1457 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1458 if fp != sys.stdout:
1458 if fp != sys.stdout:
1459 fp.close()
1459 fp.close()
1460
1460
1461 def export(ui, repo, *changesets, **opts):
1461 def export(ui, repo, *changesets, **opts):
1462 """dump the header and diffs for one or more changesets
1462 """dump the header and diffs for one or more changesets
1463
1463
1464 Print the changeset header and diffs for one or more revisions.
1464 Print the changeset header and diffs for one or more revisions.
1465
1465
1466 The information shown in the changeset header is: author,
1466 The information shown in the changeset header is: author,
1467 changeset hash, parent and commit comment.
1467 changeset hash, parent and commit comment.
1468
1468
1469 Output may be to a file, in which case the name of the file is
1469 Output may be to a file, in which case the name of the file is
1470 given using a format string. The formatting rules are as follows:
1470 given using a format string. The formatting rules are as follows:
1471
1471
1472 %% literal "%" character
1472 %% literal "%" character
1473 %H changeset hash (40 bytes of hexadecimal)
1473 %H changeset hash (40 bytes of hexadecimal)
1474 %N number of patches being generated
1474 %N number of patches being generated
1475 %R changeset revision number
1475 %R changeset revision number
1476 %b basename of the exporting repository
1476 %b basename of the exporting repository
1477 %h short-form changeset hash (12 bytes of hexadecimal)
1477 %h short-form changeset hash (12 bytes of hexadecimal)
1478 %n zero-padded sequence number, starting at 1
1478 %n zero-padded sequence number, starting at 1
1479 %r zero-padded changeset revision number
1479 %r zero-padded changeset revision number
1480
1480
1481 Without the -a option, export will avoid generating diffs of files
1481 Without the -a option, export will avoid generating diffs of files
1482 it detects as binary. With -a, export will generate a diff anyway,
1482 it detects as binary. With -a, export will generate a diff anyway,
1483 probably with undesirable results.
1483 probably with undesirable results.
1484
1484
1485 With the --switch-parent option, the diff will be against the second
1485 With the --switch-parent option, the diff will be against the second
1486 parent. It can be useful to review a merge.
1486 parent. It can be useful to review a merge.
1487 """
1487 """
1488 if not changesets:
1488 if not changesets:
1489 raise util.Abort(_("export requires at least one changeset"))
1489 raise util.Abort(_("export requires at least one changeset"))
1490 seqno = 0
1490 seqno = 0
1491 revs = list(revrange(ui, repo, changesets))
1491 revs = list(revrange(ui, repo, changesets))
1492 total = len(revs)
1492 total = len(revs)
1493 revwidth = max(map(len, revs))
1493 revwidth = max(map(len, revs))
1494 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1494 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1495 ui.note(msg)
1495 ui.note(msg)
1496 for cset in revs:
1496 for cset in revs:
1497 seqno += 1
1497 seqno += 1
1498 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1498 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1499
1499
1500 def forget(ui, repo, *pats, **opts):
1500 def forget(ui, repo, *pats, **opts):
1501 """don't add the specified files on the next commit
1501 """don't add the specified files on the next commit
1502
1502
1503 Undo an 'hg add' scheduled for the next commit.
1503 Undo an 'hg add' scheduled for the next commit.
1504 """
1504 """
1505 forget = []
1505 forget = []
1506 for src, abs, rel, exact in walk(repo, pats, opts):
1506 for src, abs, rel, exact in walk(repo, pats, opts):
1507 if repo.dirstate.state(abs) == 'a':
1507 if repo.dirstate.state(abs) == 'a':
1508 forget.append(abs)
1508 forget.append(abs)
1509 if ui.verbose or not exact:
1509 if ui.verbose or not exact:
1510 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1510 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1511 repo.forget(forget)
1511 repo.forget(forget)
1512
1512
1513 def grep(ui, repo, pattern, *pats, **opts):
1513 def grep(ui, repo, pattern, *pats, **opts):
1514 """search for a pattern in specified files and revisions
1514 """search for a pattern in specified files and revisions
1515
1515
1516 Search revisions of files for a regular expression.
1516 Search revisions of files for a regular expression.
1517
1517
1518 This command behaves differently than Unix grep. It only accepts
1518 This command behaves differently than Unix grep. It only accepts
1519 Python/Perl regexps. It searches repository history, not the
1519 Python/Perl regexps. It searches repository history, not the
1520 working directory. It always prints the revision number in which
1520 working directory. It always prints the revision number in which
1521 a match appears.
1521 a match appears.
1522
1522
1523 By default, grep only prints output for the first revision of a
1523 By default, grep only prints output for the first revision of a
1524 file in which it finds a match. To get it to print every revision
1524 file in which it finds a match. To get it to print every revision
1525 that contains a change in match status ("-" for a match that
1525 that contains a change in match status ("-" for a match that
1526 becomes a non-match, or "+" for a non-match that becomes a match),
1526 becomes a non-match, or "+" for a non-match that becomes a match),
1527 use the --all flag.
1527 use the --all flag.
1528 """
1528 """
1529 reflags = 0
1529 reflags = 0
1530 if opts['ignore_case']:
1530 if opts['ignore_case']:
1531 reflags |= re.I
1531 reflags |= re.I
1532 regexp = re.compile(pattern, reflags)
1532 regexp = re.compile(pattern, reflags)
1533 sep, eol = ':', '\n'
1533 sep, eol = ':', '\n'
1534 if opts['print0']:
1534 if opts['print0']:
1535 sep = eol = '\0'
1535 sep = eol = '\0'
1536
1536
1537 fcache = {}
1537 fcache = {}
1538 def getfile(fn):
1538 def getfile(fn):
1539 if fn not in fcache:
1539 if fn not in fcache:
1540 fcache[fn] = repo.file(fn)
1540 fcache[fn] = repo.file(fn)
1541 return fcache[fn]
1541 return fcache[fn]
1542
1542
1543 def matchlines(body):
1543 def matchlines(body):
1544 begin = 0
1544 begin = 0
1545 linenum = 0
1545 linenum = 0
1546 while True:
1546 while True:
1547 match = regexp.search(body, begin)
1547 match = regexp.search(body, begin)
1548 if not match:
1548 if not match:
1549 break
1549 break
1550 mstart, mend = match.span()
1550 mstart, mend = match.span()
1551 linenum += body.count('\n', begin, mstart) + 1
1551 linenum += body.count('\n', begin, mstart) + 1
1552 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1552 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1553 lend = body.find('\n', mend)
1553 lend = body.find('\n', mend)
1554 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1554 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1555 begin = lend + 1
1555 begin = lend + 1
1556
1556
1557 class linestate(object):
1557 class linestate(object):
1558 def __init__(self, line, linenum, colstart, colend):
1558 def __init__(self, line, linenum, colstart, colend):
1559 self.line = line
1559 self.line = line
1560 self.linenum = linenum
1560 self.linenum = linenum
1561 self.colstart = colstart
1561 self.colstart = colstart
1562 self.colend = colend
1562 self.colend = colend
1563 def __eq__(self, other):
1563 def __eq__(self, other):
1564 return self.line == other.line
1564 return self.line == other.line
1565 def __hash__(self):
1565 def __hash__(self):
1566 return hash(self.line)
1566 return hash(self.line)
1567
1567
1568 matches = {}
1568 matches = {}
1569 def grepbody(fn, rev, body):
1569 def grepbody(fn, rev, body):
1570 matches[rev].setdefault(fn, {})
1570 matches[rev].setdefault(fn, {})
1571 m = matches[rev][fn]
1571 m = matches[rev][fn]
1572 for lnum, cstart, cend, line in matchlines(body):
1572 for lnum, cstart, cend, line in matchlines(body):
1573 s = linestate(line, lnum, cstart, cend)
1573 s = linestate(line, lnum, cstart, cend)
1574 m[s] = s
1574 m[s] = s
1575
1575
1576 # FIXME: prev isn't used, why ?
1576 # FIXME: prev isn't used, why ?
1577 prev = {}
1577 prev = {}
1578 ucache = {}
1578 ucache = {}
1579 def display(fn, rev, states, prevstates):
1579 def display(fn, rev, states, prevstates):
1580 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1580 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1581 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1581 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1582 counts = {'-': 0, '+': 0}
1582 counts = {'-': 0, '+': 0}
1583 filerevmatches = {}
1583 filerevmatches = {}
1584 for l in diff:
1584 for l in diff:
1585 if incrementing or not opts['all']:
1585 if incrementing or not opts['all']:
1586 change = ((l in prevstates) and '-') or '+'
1586 change = ((l in prevstates) and '-') or '+'
1587 r = rev
1587 r = rev
1588 else:
1588 else:
1589 change = ((l in states) and '-') or '+'
1589 change = ((l in states) and '-') or '+'
1590 r = prev[fn]
1590 r = prev[fn]
1591 cols = [fn, str(rev)]
1591 cols = [fn, str(rev)]
1592 if opts['line_number']:
1592 if opts['line_number']:
1593 cols.append(str(l.linenum))
1593 cols.append(str(l.linenum))
1594 if opts['all']:
1594 if opts['all']:
1595 cols.append(change)
1595 cols.append(change)
1596 if opts['user']:
1596 if opts['user']:
1597 cols.append(trimuser(ui, getchange(rev)[1], rev,
1597 cols.append(trimuser(ui, getchange(rev)[1], rev,
1598 ucache))
1598 ucache))
1599 if opts['files_with_matches']:
1599 if opts['files_with_matches']:
1600 c = (fn, rev)
1600 c = (fn, rev)
1601 if c in filerevmatches:
1601 if c in filerevmatches:
1602 continue
1602 continue
1603 filerevmatches[c] = 1
1603 filerevmatches[c] = 1
1604 else:
1604 else:
1605 cols.append(l.line)
1605 cols.append(l.line)
1606 ui.write(sep.join(cols), eol)
1606 ui.write(sep.join(cols), eol)
1607 counts[change] += 1
1607 counts[change] += 1
1608 return counts['+'], counts['-']
1608 return counts['+'], counts['-']
1609
1609
1610 fstate = {}
1610 fstate = {}
1611 skip = {}
1611 skip = {}
1612 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1612 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1613 count = 0
1613 count = 0
1614 incrementing = False
1614 incrementing = False
1615 for st, rev, fns in changeiter:
1615 for st, rev, fns in changeiter:
1616 if st == 'window':
1616 if st == 'window':
1617 incrementing = rev
1617 incrementing = rev
1618 matches.clear()
1618 matches.clear()
1619 elif st == 'add':
1619 elif st == 'add':
1620 change = repo.changelog.read(repo.lookup(str(rev)))
1620 change = repo.changelog.read(repo.lookup(str(rev)))
1621 mf = repo.manifest.read(change[0])
1621 mf = repo.manifest.read(change[0])
1622 matches[rev] = {}
1622 matches[rev] = {}
1623 for fn in fns:
1623 for fn in fns:
1624 if fn in skip:
1624 if fn in skip:
1625 continue
1625 continue
1626 fstate.setdefault(fn, {})
1626 fstate.setdefault(fn, {})
1627 try:
1627 try:
1628 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1628 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1629 except KeyError:
1629 except KeyError:
1630 pass
1630 pass
1631 elif st == 'iter':
1631 elif st == 'iter':
1632 states = matches[rev].items()
1632 states = matches[rev].items()
1633 states.sort()
1633 states.sort()
1634 for fn, m in states:
1634 for fn, m in states:
1635 if fn in skip:
1635 if fn in skip:
1636 continue
1636 continue
1637 if incrementing or not opts['all'] or fstate[fn]:
1637 if incrementing or not opts['all'] or fstate[fn]:
1638 pos, neg = display(fn, rev, m, fstate[fn])
1638 pos, neg = display(fn, rev, m, fstate[fn])
1639 count += pos + neg
1639 count += pos + neg
1640 if pos and not opts['all']:
1640 if pos and not opts['all']:
1641 skip[fn] = True
1641 skip[fn] = True
1642 fstate[fn] = m
1642 fstate[fn] = m
1643 prev[fn] = rev
1643 prev[fn] = rev
1644
1644
1645 if not incrementing:
1645 if not incrementing:
1646 fstate = fstate.items()
1646 fstate = fstate.items()
1647 fstate.sort()
1647 fstate.sort()
1648 for fn, state in fstate:
1648 for fn, state in fstate:
1649 if fn in skip:
1649 if fn in skip:
1650 continue
1650 continue
1651 display(fn, rev, {}, state)
1651 display(fn, rev, {}, state)
1652 return (count == 0 and 1) or 0
1652 return (count == 0 and 1) or 0
1653
1653
1654 def heads(ui, repo, **opts):
1654 def heads(ui, repo, **opts):
1655 """show current repository heads
1655 """show current repository heads
1656
1656
1657 Show all repository head changesets.
1657 Show all repository head changesets.
1658
1658
1659 Repository "heads" are changesets that don't have children
1659 Repository "heads" are changesets that don't have children
1660 changesets. They are where development generally takes place and
1660 changesets. They are where development generally takes place and
1661 are the usual targets for update and merge operations.
1661 are the usual targets for update and merge operations.
1662 """
1662 """
1663 if opts['rev']:
1663 if opts['rev']:
1664 heads = repo.heads(repo.lookup(opts['rev']))
1664 heads = repo.heads(repo.lookup(opts['rev']))
1665 else:
1665 else:
1666 heads = repo.heads()
1666 heads = repo.heads()
1667 br = None
1667 br = None
1668 if opts['branches']:
1668 if opts['branches']:
1669 br = repo.branchlookup(heads)
1669 br = repo.branchlookup(heads)
1670 displayer = show_changeset(ui, repo, opts)
1670 displayer = show_changeset(ui, repo, opts)
1671 for n in heads:
1671 for n in heads:
1672 displayer.show(changenode=n, brinfo=br)
1672 displayer.show(changenode=n, brinfo=br)
1673
1673
1674 def identify(ui, repo):
1674 def identify(ui, repo):
1675 """print information about the working copy
1675 """print information about the working copy
1676
1676
1677 Print a short summary of the current state of the repo.
1677 Print a short summary of the current state of the repo.
1678
1678
1679 This summary identifies the repository state using one or two parent
1679 This summary identifies the repository state using one or two parent
1680 hash identifiers, followed by a "+" if there are uncommitted changes
1680 hash identifiers, followed by a "+" if there are uncommitted changes
1681 in the working directory, followed by a list of tags for this revision.
1681 in the working directory, followed by a list of tags for this revision.
1682 """
1682 """
1683 parents = [p for p in repo.dirstate.parents() if p != nullid]
1683 parents = [p for p in repo.dirstate.parents() if p != nullid]
1684 if not parents:
1684 if not parents:
1685 ui.write(_("unknown\n"))
1685 ui.write(_("unknown\n"))
1686 return
1686 return
1687
1687
1688 hexfunc = ui.verbose and hex or short
1688 hexfunc = ui.verbose and hex or short
1689 modified, added, removed, deleted, unknown = repo.changes()
1689 modified, added, removed, deleted, unknown = repo.changes()
1690 output = ["%s%s" %
1690 output = ["%s%s" %
1691 ('+'.join([hexfunc(parent) for parent in parents]),
1691 ('+'.join([hexfunc(parent) for parent in parents]),
1692 (modified or added or removed or deleted) and "+" or "")]
1692 (modified or added or removed or deleted) and "+" or "")]
1693
1693
1694 if not ui.quiet:
1694 if not ui.quiet:
1695 # multiple tags for a single parent separated by '/'
1695 # multiple tags for a single parent separated by '/'
1696 parenttags = ['/'.join(tags)
1696 parenttags = ['/'.join(tags)
1697 for tags in map(repo.nodetags, parents) if tags]
1697 for tags in map(repo.nodetags, parents) if tags]
1698 # tags for multiple parents separated by ' + '
1698 # tags for multiple parents separated by ' + '
1699 if parenttags:
1699 if parenttags:
1700 output.append(' + '.join(parenttags))
1700 output.append(' + '.join(parenttags))
1701
1701
1702 ui.write("%s\n" % ' '.join(output))
1702 ui.write("%s\n" % ' '.join(output))
1703
1703
1704 def import_(ui, repo, patch1, *patches, **opts):
1704 def import_(ui, repo, patch1, *patches, **opts):
1705 """import an ordered set of patches
1705 """import an ordered set of patches
1706
1706
1707 Import a list of patches and commit them individually.
1707 Import a list of patches and commit them individually.
1708
1708
1709 If there are outstanding changes in the working directory, import
1709 If there are outstanding changes in the working directory, import
1710 will abort unless given the -f flag.
1710 will abort unless given the -f flag.
1711
1711
1712 If a patch looks like a mail message (its first line starts with
1712 If a patch looks like a mail message (its first line starts with
1713 "From " or looks like an RFC822 header), it will not be applied
1713 "From " or looks like an RFC822 header), it will not be applied
1714 unless the -f option is used. The importer neither parses nor
1714 unless the -f option is used. The importer neither parses nor
1715 discards mail headers, so use -f only to override the "mailness"
1715 discards mail headers, so use -f only to override the "mailness"
1716 safety check, not to import a real mail message.
1716 safety check, not to import a real mail message.
1717 """
1717 """
1718 patches = (patch1,) + patches
1718 patches = (patch1,) + patches
1719
1719
1720 if not opts['force']:
1720 if not opts['force']:
1721 modified, added, removed, deleted, unknown = repo.changes()
1721 modified, added, removed, deleted, unknown = repo.changes()
1722 if modified or added or removed or deleted:
1722 if modified or added or removed or deleted:
1723 raise util.Abort(_("outstanding uncommitted changes"))
1723 raise util.Abort(_("outstanding uncommitted changes"))
1724
1724
1725 d = opts["base"]
1725 d = opts["base"]
1726 strip = opts["strip"]
1726 strip = opts["strip"]
1727
1727
1728 mailre = re.compile(r'(?:From |[\w-]+:)')
1728 mailre = re.compile(r'(?:From |[\w-]+:)')
1729
1729
1730 # attempt to detect the start of a patch
1730 # attempt to detect the start of a patch
1731 # (this heuristic is borrowed from quilt)
1731 # (this heuristic is borrowed from quilt)
1732 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1732 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1733 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1733 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1734 '(---|\*\*\*)[ \t])')
1734 '(---|\*\*\*)[ \t])')
1735
1735
1736 for patch in patches:
1736 for patch in patches:
1737 ui.status(_("applying %s\n") % patch)
1737 ui.status(_("applying %s\n") % patch)
1738 pf = os.path.join(d, patch)
1738 pf = os.path.join(d, patch)
1739
1739
1740 message = []
1740 message = []
1741 user = None
1741 user = None
1742 hgpatch = False
1742 hgpatch = False
1743 for line in file(pf):
1743 for line in file(pf):
1744 line = line.rstrip()
1744 line = line.rstrip()
1745 if (not message and not hgpatch and
1745 if (not message and not hgpatch and
1746 mailre.match(line) and not opts['force']):
1746 mailre.match(line) and not opts['force']):
1747 if len(line) > 35:
1747 if len(line) > 35:
1748 line = line[:32] + '...'
1748 line = line[:32] + '...'
1749 raise util.Abort(_('first line looks like a '
1749 raise util.Abort(_('first line looks like a '
1750 'mail header: ') + line)
1750 'mail header: ') + line)
1751 if diffre.match(line):
1751 if diffre.match(line):
1752 break
1752 break
1753 elif hgpatch:
1753 elif hgpatch:
1754 # parse values when importing the result of an hg export
1754 # parse values when importing the result of an hg export
1755 if line.startswith("# User "):
1755 if line.startswith("# User "):
1756 user = line[7:]
1756 user = line[7:]
1757 ui.debug(_('User: %s\n') % user)
1757 ui.debug(_('User: %s\n') % user)
1758 elif not line.startswith("# ") and line:
1758 elif not line.startswith("# ") and line:
1759 message.append(line)
1759 message.append(line)
1760 hgpatch = False
1760 hgpatch = False
1761 elif line == '# HG changeset patch':
1761 elif line == '# HG changeset patch':
1762 hgpatch = True
1762 hgpatch = True
1763 message = [] # We may have collected garbage
1763 message = [] # We may have collected garbage
1764 else:
1764 else:
1765 message.append(line)
1765 message.append(line)
1766
1766
1767 # make sure message isn't empty
1767 # make sure message isn't empty
1768 if not message:
1768 if not message:
1769 message = _("imported patch %s\n") % patch
1769 message = _("imported patch %s\n") % patch
1770 else:
1770 else:
1771 message = "%s\n" % '\n'.join(message)
1771 message = "%s\n" % '\n'.join(message)
1772 ui.debug(_('message:\n%s\n') % message)
1772 ui.debug(_('message:\n%s\n') % message)
1773
1773
1774 files = util.patch(strip, pf, ui)
1774 files = util.patch(strip, pf, ui)
1775
1775
1776 if len(files) > 0:
1776 if len(files) > 0:
1777 addremove(ui, repo, *files)
1777 addremove(ui, repo, *files)
1778 repo.commit(files, message, user)
1778 repo.commit(files, message, user)
1779
1779
1780 def incoming(ui, repo, source="default", **opts):
1780 def incoming(ui, repo, source="default", **opts):
1781 """show new changesets found in source
1781 """show new changesets found in source
1782
1782
1783 Show new changesets found in the specified path/URL or the default
1783 Show new changesets found in the specified path/URL or the default
1784 pull location. These are the changesets that would be pulled if a pull
1784 pull location. These are the changesets that would be pulled if a pull
1785 was requested.
1785 was requested.
1786
1786
1787 For remote repository, using --bundle avoids downloading the changesets
1787 For remote repository, using --bundle avoids downloading the changesets
1788 twice if the incoming is followed by a pull.
1788 twice if the incoming is followed by a pull.
1789
1789
1790 See pull for valid source format details.
1790 See pull for valid source format details.
1791 """
1791 """
1792 source = ui.expandpath(source)
1792 source = ui.expandpath(source)
1793 if opts['ssh']:
1793 if opts['ssh']:
1794 ui.setconfig("ui", "ssh", opts['ssh'])
1794 ui.setconfig("ui", "ssh", opts['ssh'])
1795 if opts['remotecmd']:
1795 if opts['remotecmd']:
1796 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1796 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1797
1797
1798 other = hg.repository(ui, source)
1798 other = hg.repository(ui, source)
1799 incoming = repo.findincoming(other, force=opts["force"])
1799 incoming = repo.findincoming(other, force=opts["force"])
1800 if not incoming:
1800 if not incoming:
1801 ui.status(_("no changes found\n"))
1801 ui.status(_("no changes found\n"))
1802 return
1802 return
1803
1803
1804 cleanup = None
1804 cleanup = None
1805 try:
1805 try:
1806 fname = opts["bundle"]
1806 fname = opts["bundle"]
1807 if fname or not other.local():
1807 if fname or not other.local():
1808 # create a bundle (uncompressed if other repo is not local)
1808 # create a bundle (uncompressed if other repo is not local)
1809 cg = other.changegroup(incoming, "incoming")
1809 cg = other.changegroup(incoming, "incoming")
1810 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1810 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1811 # keep written bundle?
1811 # keep written bundle?
1812 if opts["bundle"]:
1812 if opts["bundle"]:
1813 cleanup = None
1813 cleanup = None
1814 if not other.local():
1814 if not other.local():
1815 # use the created uncompressed bundlerepo
1815 # use the created uncompressed bundlerepo
1816 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1816 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1817
1817
1818 o = other.changelog.nodesbetween(incoming)[0]
1818 o = other.changelog.nodesbetween(incoming)[0]
1819 if opts['newest_first']:
1819 if opts['newest_first']:
1820 o.reverse()
1820 o.reverse()
1821 displayer = show_changeset(ui, other, opts)
1821 displayer = show_changeset(ui, other, opts)
1822 for n in o:
1822 for n in o:
1823 parents = [p for p in other.changelog.parents(n) if p != nullid]
1823 parents = [p for p in other.changelog.parents(n) if p != nullid]
1824 if opts['no_merges'] and len(parents) == 2:
1824 if opts['no_merges'] and len(parents) == 2:
1825 continue
1825 continue
1826 displayer.show(changenode=n)
1826 displayer.show(changenode=n)
1827 if opts['patch']:
1827 if opts['patch']:
1828 prev = (parents and parents[0]) or nullid
1828 prev = (parents and parents[0]) or nullid
1829 dodiff(ui, ui, other, prev, n)
1829 dodiff(ui, ui, other, prev, n)
1830 ui.write("\n")
1830 ui.write("\n")
1831 finally:
1831 finally:
1832 if hasattr(other, 'close'):
1832 if hasattr(other, 'close'):
1833 other.close()
1833 other.close()
1834 if cleanup:
1834 if cleanup:
1835 os.unlink(cleanup)
1835 os.unlink(cleanup)
1836
1836
1837 def init(ui, dest="."):
1837 def init(ui, dest="."):
1838 """create a new repository in the given directory
1838 """create a new repository in the given directory
1839
1839
1840 Initialize a new repository in the given directory. If the given
1840 Initialize a new repository in the given directory. If the given
1841 directory does not exist, it is created.
1841 directory does not exist, it is created.
1842
1842
1843 If no directory is given, the current directory is used.
1843 If no directory is given, the current directory is used.
1844 """
1844 """
1845 if not os.path.exists(dest):
1845 if not os.path.exists(dest):
1846 os.mkdir(dest)
1846 os.mkdir(dest)
1847 hg.repository(ui, dest, create=1)
1847 hg.repository(ui, dest, create=1)
1848
1848
1849 def locate(ui, repo, *pats, **opts):
1849 def locate(ui, repo, *pats, **opts):
1850 """locate files matching specific patterns
1850 """locate files matching specific patterns
1851
1851
1852 Print all files under Mercurial control whose names match the
1852 Print all files under Mercurial control whose names match the
1853 given patterns.
1853 given patterns.
1854
1854
1855 This command searches the current directory and its
1855 This command searches the current directory and its
1856 subdirectories. To search an entire repository, move to the root
1856 subdirectories. To search an entire repository, move to the root
1857 of the repository.
1857 of the repository.
1858
1858
1859 If no patterns are given to match, this command prints all file
1859 If no patterns are given to match, this command prints all file
1860 names.
1860 names.
1861
1861
1862 If you want to feed the output of this command into the "xargs"
1862 If you want to feed the output of this command into the "xargs"
1863 command, use the "-0" option to both this command and "xargs".
1863 command, use the "-0" option to both this command and "xargs".
1864 This will avoid the problem of "xargs" treating single filenames
1864 This will avoid the problem of "xargs" treating single filenames
1865 that contain white space as multiple filenames.
1865 that contain white space as multiple filenames.
1866 """
1866 """
1867 end = opts['print0'] and '\0' or '\n'
1867 end = opts['print0'] and '\0' or '\n'
1868 rev = opts['rev']
1868 rev = opts['rev']
1869 if rev:
1869 if rev:
1870 node = repo.lookup(rev)
1870 node = repo.lookup(rev)
1871 else:
1871 else:
1872 node = None
1872 node = None
1873
1873
1874 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1874 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1875 head='(?:.*/|)'):
1875 head='(?:.*/|)'):
1876 if not node and repo.dirstate.state(abs) == '?':
1876 if not node and repo.dirstate.state(abs) == '?':
1877 continue
1877 continue
1878 if opts['fullpath']:
1878 if opts['fullpath']:
1879 ui.write(os.path.join(repo.root, abs), end)
1879 ui.write(os.path.join(repo.root, abs), end)
1880 else:
1880 else:
1881 ui.write(((pats and rel) or abs), end)
1881 ui.write(((pats and rel) or abs), end)
1882
1882
1883 def log(ui, repo, *pats, **opts):
1883 def log(ui, repo, *pats, **opts):
1884 """show revision history of entire repository or files
1884 """show revision history of entire repository or files
1885
1885
1886 Print the revision history of the specified files or the entire project.
1886 Print the revision history of the specified files or the entire project.
1887
1887
1888 By default this command outputs: changeset id and hash, tags,
1888 By default this command outputs: changeset id and hash, tags,
1889 non-trivial parents, user, date and time, and a summary for each
1889 non-trivial parents, user, date and time, and a summary for each
1890 commit. When the -v/--verbose switch is used, the list of changed
1890 commit. When the -v/--verbose switch is used, the list of changed
1891 files and full commit message is shown.
1891 files and full commit message is shown.
1892 """
1892 """
1893 class dui(object):
1893 class dui(object):
1894 # Implement and delegate some ui protocol. Save hunks of
1894 # Implement and delegate some ui protocol. Save hunks of
1895 # output for later display in the desired order.
1895 # output for later display in the desired order.
1896 def __init__(self, ui):
1896 def __init__(self, ui):
1897 self.ui = ui
1897 self.ui = ui
1898 self.hunk = {}
1898 self.hunk = {}
1899 def bump(self, rev):
1899 def bump(self, rev):
1900 self.rev = rev
1900 self.rev = rev
1901 self.hunk[rev] = []
1901 self.hunk[rev] = []
1902 def note(self, *args):
1902 def note(self, *args):
1903 if self.verbose:
1903 if self.verbose:
1904 self.write(*args)
1904 self.write(*args)
1905 def status(self, *args):
1905 def status(self, *args):
1906 if not self.quiet:
1906 if not self.quiet:
1907 self.write(*args)
1907 self.write(*args)
1908 def write(self, *args):
1908 def write(self, *args):
1909 self.hunk[self.rev].append(args)
1909 self.hunk[self.rev].append(args)
1910 def debug(self, *args):
1910 def debug(self, *args):
1911 if self.debugflag:
1911 if self.debugflag:
1912 self.write(*args)
1912 self.write(*args)
1913 def __getattr__(self, key):
1913 def __getattr__(self, key):
1914 return getattr(self.ui, key)
1914 return getattr(self.ui, key)
1915
1915
1916 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1916 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1917
1917
1918 if opts['limit']:
1918 if opts['limit']:
1919 try:
1919 try:
1920 limit = int(opts['limit'])
1920 limit = int(opts['limit'])
1921 except ValueError:
1921 except ValueError:
1922 raise util.Abort(_('limit must be a positive integer'))
1922 raise util.Abort(_('limit must be a positive integer'))
1923 if limit <= 0: raise util.Abort(_('limit must be positive'))
1923 if limit <= 0: raise util.Abort(_('limit must be positive'))
1924 else:
1924 else:
1925 limit = sys.maxint
1925 limit = sys.maxint
1926 count = 0
1926 count = 0
1927
1927
1928 displayer = show_changeset(ui, repo, opts)
1928 displayer = show_changeset(ui, repo, opts)
1929 for st, rev, fns in changeiter:
1929 for st, rev, fns in changeiter:
1930 if st == 'window':
1930 if st == 'window':
1931 du = dui(ui)
1931 du = dui(ui)
1932 displayer.ui = du
1932 displayer.ui = du
1933 elif st == 'add':
1933 elif st == 'add':
1934 du.bump(rev)
1934 du.bump(rev)
1935 changenode = repo.changelog.node(rev)
1935 changenode = repo.changelog.node(rev)
1936 parents = [p for p in repo.changelog.parents(changenode)
1936 parents = [p for p in repo.changelog.parents(changenode)
1937 if p != nullid]
1937 if p != nullid]
1938 if opts['no_merges'] and len(parents) == 2:
1938 if opts['no_merges'] and len(parents) == 2:
1939 continue
1939 continue
1940 if opts['only_merges'] and len(parents) != 2:
1940 if opts['only_merges'] and len(parents) != 2:
1941 continue
1941 continue
1942
1942
1943 if opts['keyword']:
1943 if opts['keyword']:
1944 changes = getchange(rev)
1944 changes = getchange(rev)
1945 miss = 0
1945 miss = 0
1946 for k in [kw.lower() for kw in opts['keyword']]:
1946 for k in [kw.lower() for kw in opts['keyword']]:
1947 if not (k in changes[1].lower() or
1947 if not (k in changes[1].lower() or
1948 k in changes[4].lower() or
1948 k in changes[4].lower() or
1949 k in " ".join(changes[3][:20]).lower()):
1949 k in " ".join(changes[3][:20]).lower()):
1950 miss = 1
1950 miss = 1
1951 break
1951 break
1952 if miss:
1952 if miss:
1953 continue
1953 continue
1954
1954
1955 br = None
1955 br = None
1956 if opts['branches']:
1956 if opts['branches']:
1957 br = repo.branchlookup([repo.changelog.node(rev)])
1957 br = repo.branchlookup([repo.changelog.node(rev)])
1958
1958
1959 displayer.show(rev, brinfo=br)
1959 displayer.show(rev, brinfo=br)
1960 if opts['patch']:
1960 if opts['patch']:
1961 prev = (parents and parents[0]) or nullid
1961 prev = (parents and parents[0]) or nullid
1962 dodiff(du, du, repo, prev, changenode, match=matchfn)
1962 dodiff(du, du, repo, prev, changenode, match=matchfn)
1963 du.write("\n\n")
1963 du.write("\n\n")
1964 elif st == 'iter':
1964 elif st == 'iter':
1965 if count == limit: break
1965 if count == limit: break
1966 if du.hunk[rev]:
1966 if du.hunk[rev]:
1967 count += 1
1967 count += 1
1968 for args in du.hunk[rev]:
1968 for args in du.hunk[rev]:
1969 ui.write(*args)
1969 ui.write(*args)
1970
1970
1971 def manifest(ui, repo, rev=None):
1971 def manifest(ui, repo, rev=None):
1972 """output the latest or given revision of the project manifest
1972 """output the latest or given revision of the project manifest
1973
1973
1974 Print a list of version controlled files for the given revision.
1974 Print a list of version controlled files for the given revision.
1975
1975
1976 The manifest is the list of files being version controlled. If no revision
1976 The manifest is the list of files being version controlled. If no revision
1977 is given then the tip is used.
1977 is given then the tip is used.
1978 """
1978 """
1979 if rev:
1979 if rev:
1980 try:
1980 try:
1981 # assume all revision numbers are for changesets
1981 # assume all revision numbers are for changesets
1982 n = repo.lookup(rev)
1982 n = repo.lookup(rev)
1983 change = repo.changelog.read(n)
1983 change = repo.changelog.read(n)
1984 n = change[0]
1984 n = change[0]
1985 except hg.RepoError:
1985 except hg.RepoError:
1986 n = repo.manifest.lookup(rev)
1986 n = repo.manifest.lookup(rev)
1987 else:
1987 else:
1988 n = repo.manifest.tip()
1988 n = repo.manifest.tip()
1989 m = repo.manifest.read(n)
1989 m = repo.manifest.read(n)
1990 mf = repo.manifest.readflags(n)
1990 mf = repo.manifest.readflags(n)
1991 files = m.keys()
1991 files = m.keys()
1992 files.sort()
1992 files.sort()
1993
1993
1994 for f in files:
1994 for f in files:
1995 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1995 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1996
1996
1997 def merge(ui, repo, node=None, **opts):
1997 def merge(ui, repo, node=None, **opts):
1998 """Merge working directory with another revision
1998 """Merge working directory with another revision
1999
1999
2000 Merge the contents of the current working directory and the
2000 Merge the contents of the current working directory and the
2001 requested revision. Files that changed between either parent are
2001 requested revision. Files that changed between either parent are
2002 marked as changed for the next commit and a commit must be
2002 marked as changed for the next commit and a commit must be
2003 performed before any further updates are allowed.
2003 performed before any further updates are allowed.
2004 """
2004 """
2005 return update(ui, repo, node=node, merge=True, **opts)
2005 return update(ui, repo, node=node, merge=True, **opts)
2006
2006
2007 def outgoing(ui, repo, dest="default-push", **opts):
2007 def outgoing(ui, repo, dest="default-push", **opts):
2008 """show changesets not found in destination
2008 """show changesets not found in destination
2009
2009
2010 Show changesets not found in the specified destination repository or
2010 Show changesets not found in the specified destination repository or
2011 the default push location. These are the changesets that would be pushed
2011 the default push location. These are the changesets that would be pushed
2012 if a push was requested.
2012 if a push was requested.
2013
2013
2014 See pull for valid destination format details.
2014 See pull for valid destination format details.
2015 """
2015 """
2016 dest = ui.expandpath(dest)
2016 dest = ui.expandpath(dest)
2017 if opts['ssh']:
2017 if opts['ssh']:
2018 ui.setconfig("ui", "ssh", opts['ssh'])
2018 ui.setconfig("ui", "ssh", opts['ssh'])
2019 if opts['remotecmd']:
2019 if opts['remotecmd']:
2020 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2020 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2021
2021
2022 other = hg.repository(ui, dest)
2022 other = hg.repository(ui, dest)
2023 o = repo.findoutgoing(other, force=opts['force'])
2023 o = repo.findoutgoing(other, force=opts['force'])
2024 if not o:
2024 if not o:
2025 ui.status(_("no changes found\n"))
2025 ui.status(_("no changes found\n"))
2026 return
2026 return
2027 o = repo.changelog.nodesbetween(o)[0]
2027 o = repo.changelog.nodesbetween(o)[0]
2028 if opts['newest_first']:
2028 if opts['newest_first']:
2029 o.reverse()
2029 o.reverse()
2030 displayer = show_changeset(ui, repo, opts)
2030 displayer = show_changeset(ui, repo, opts)
2031 for n in o:
2031 for n in o:
2032 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2032 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2033 if opts['no_merges'] and len(parents) == 2:
2033 if opts['no_merges'] and len(parents) == 2:
2034 continue
2034 continue
2035 displayer.show(changenode=n)
2035 displayer.show(changenode=n)
2036 if opts['patch']:
2036 if opts['patch']:
2037 prev = (parents and parents[0]) or nullid
2037 prev = (parents and parents[0]) or nullid
2038 dodiff(ui, ui, repo, prev, n)
2038 dodiff(ui, ui, repo, prev, n)
2039 ui.write("\n")
2039 ui.write("\n")
2040
2040
2041 def parents(ui, repo, rev=None, branches=None, **opts):
2041 def parents(ui, repo, rev=None, branches=None, **opts):
2042 """show the parents of the working dir or revision
2042 """show the parents of the working dir or revision
2043
2043
2044 Print the working directory's parent revisions.
2044 Print the working directory's parent revisions.
2045 """
2045 """
2046 if rev:
2046 if rev:
2047 p = repo.changelog.parents(repo.lookup(rev))
2047 p = repo.changelog.parents(repo.lookup(rev))
2048 else:
2048 else:
2049 p = repo.dirstate.parents()
2049 p = repo.dirstate.parents()
2050
2050
2051 br = None
2051 br = None
2052 if branches is not None:
2052 if branches is not None:
2053 br = repo.branchlookup(p)
2053 br = repo.branchlookup(p)
2054 displayer = show_changeset(ui, repo, opts)
2054 displayer = show_changeset(ui, repo, opts)
2055 for n in p:
2055 for n in p:
2056 if n != nullid:
2056 if n != nullid:
2057 displayer.show(changenode=n, brinfo=br)
2057 displayer.show(changenode=n, brinfo=br)
2058
2058
2059 def paths(ui, repo, search=None):
2059 def paths(ui, repo, search=None):
2060 """show definition of symbolic path names
2060 """show definition of symbolic path names
2061
2061
2062 Show definition of symbolic path name NAME. If no name is given, show
2062 Show definition of symbolic path name NAME. If no name is given, show
2063 definition of available names.
2063 definition of available names.
2064
2064
2065 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2065 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2066 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2066 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2067 """
2067 """
2068 if search:
2068 if search:
2069 for name, path in ui.configitems("paths"):
2069 for name, path in ui.configitems("paths"):
2070 if name == search:
2070 if name == search:
2071 ui.write("%s\n" % path)
2071 ui.write("%s\n" % path)
2072 return
2072 return
2073 ui.warn(_("not found!\n"))
2073 ui.warn(_("not found!\n"))
2074 return 1
2074 return 1
2075 else:
2075 else:
2076 for name, path in ui.configitems("paths"):
2076 for name, path in ui.configitems("paths"):
2077 ui.write("%s = %s\n" % (name, path))
2077 ui.write("%s = %s\n" % (name, path))
2078
2078
2079 def postincoming(ui, repo, modheads, optupdate):
2079 def postincoming(ui, repo, modheads, optupdate):
2080 if modheads == 0:
2080 if modheads == 0:
2081 return
2081 return
2082 if optupdate:
2082 if optupdate:
2083 if modheads == 1:
2083 if modheads == 1:
2084 return update(ui, repo)
2084 return update(ui, repo)
2085 else:
2085 else:
2086 ui.status(_("not updating, since new heads added\n"))
2086 ui.status(_("not updating, since new heads added\n"))
2087 if modheads > 1:
2087 if modheads > 1:
2088 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2088 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2089 else:
2089 else:
2090 ui.status(_("(run 'hg update' to get a working copy)\n"))
2090 ui.status(_("(run 'hg update' to get a working copy)\n"))
2091
2091
2092 def pull(ui, repo, source="default", **opts):
2092 def pull(ui, repo, source="default", **opts):
2093 """pull changes from the specified source
2093 """pull changes from the specified source
2094
2094
2095 Pull changes from a remote repository to a local one.
2095 Pull changes from a remote repository to a local one.
2096
2096
2097 This finds all changes from the repository at the specified path
2097 This finds all changes from the repository at the specified path
2098 or URL and adds them to the local repository. By default, this
2098 or URL and adds them to the local repository. By default, this
2099 does not update the copy of the project in the working directory.
2099 does not update the copy of the project in the working directory.
2100
2100
2101 Valid URLs are of the form:
2101 Valid URLs are of the form:
2102
2102
2103 local/filesystem/path
2103 local/filesystem/path
2104 http://[user@]host[:port][/path]
2104 http://[user@]host[:port][/path]
2105 https://[user@]host[:port][/path]
2105 https://[user@]host[:port][/path]
2106 ssh://[user@]host[:port][/path]
2106 ssh://[user@]host[:port][/path]
2107
2107
2108 Some notes about using SSH with Mercurial:
2108 Some notes about using SSH with Mercurial:
2109 - SSH requires an accessible shell account on the destination machine
2109 - SSH requires an accessible shell account on the destination machine
2110 and a copy of hg in the remote path or specified with as remotecmd.
2110 and a copy of hg in the remote path or specified with as remotecmd.
2111 - /path is relative to the remote user's home directory by default.
2111 - /path is relative to the remote user's home directory by default.
2112 Use two slashes at the start of a path to specify an absolute path.
2112 Use two slashes at the start of a path to specify an absolute path.
2113 - Mercurial doesn't use its own compression via SSH; the right thing
2113 - Mercurial doesn't use its own compression via SSH; the right thing
2114 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2114 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2115 Host *.mylocalnetwork.example.com
2115 Host *.mylocalnetwork.example.com
2116 Compression off
2116 Compression off
2117 Host *
2117 Host *
2118 Compression on
2118 Compression on
2119 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2119 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2120 with the --ssh command line option.
2120 with the --ssh command line option.
2121 """
2121 """
2122 source = ui.expandpath(source)
2122 source = ui.expandpath(source)
2123 ui.status(_('pulling from %s\n') % (source))
2123 ui.status(_('pulling from %s\n') % (source))
2124
2124
2125 if opts['ssh']:
2125 if opts['ssh']:
2126 ui.setconfig("ui", "ssh", opts['ssh'])
2126 ui.setconfig("ui", "ssh", opts['ssh'])
2127 if opts['remotecmd']:
2127 if opts['remotecmd']:
2128 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2128 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2129
2129
2130 other = hg.repository(ui, source)
2130 other = hg.repository(ui, source)
2131 revs = None
2131 revs = None
2132 if opts['rev'] and not other.local():
2132 if opts['rev'] and not other.local():
2133 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2133 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2134 elif opts['rev']:
2134 elif opts['rev']:
2135 revs = [other.lookup(rev) for rev in opts['rev']]
2135 revs = [other.lookup(rev) for rev in opts['rev']]
2136 modheads = repo.pull(other, heads=revs, force=opts['force'])
2136 modheads = repo.pull(other, heads=revs, force=opts['force'])
2137 return postincoming(ui, repo, modheads, opts['update'])
2137 return postincoming(ui, repo, modheads, opts['update'])
2138
2138
2139 def push(ui, repo, dest="default-push", **opts):
2139 def push(ui, repo, dest="default-push", **opts):
2140 """push changes to the specified destination
2140 """push changes to the specified destination
2141
2141
2142 Push changes from the local repository to the given destination.
2142 Push changes from the local repository to the given destination.
2143
2143
2144 This is the symmetrical operation for pull. It helps to move
2144 This is the symmetrical operation for pull. It helps to move
2145 changes from the current repository to a different one. If the
2145 changes from the current repository to a different one. If the
2146 destination is local this is identical to a pull in that directory
2146 destination is local this is identical to a pull in that directory
2147 from the current one.
2147 from the current one.
2148
2148
2149 By default, push will refuse to run if it detects the result would
2149 By default, push will refuse to run if it detects the result would
2150 increase the number of remote heads. This generally indicates the
2150 increase the number of remote heads. This generally indicates the
2151 the client has forgotten to sync and merge before pushing.
2151 the client has forgotten to sync and merge before pushing.
2152
2152
2153 Valid URLs are of the form:
2153 Valid URLs are of the form:
2154
2154
2155 local/filesystem/path
2155 local/filesystem/path
2156 ssh://[user@]host[:port][/path]
2156 ssh://[user@]host[:port][/path]
2157
2157
2158 Look at the help text for the pull command for important details
2158 Look at the help text for the pull command for important details
2159 about ssh:// URLs.
2159 about ssh:// URLs.
2160 """
2160 """
2161 dest = ui.expandpath(dest)
2161 dest = ui.expandpath(dest)
2162 ui.status('pushing to %s\n' % (dest))
2162 ui.status('pushing to %s\n' % (dest))
2163
2163
2164 if opts['ssh']:
2164 if opts['ssh']:
2165 ui.setconfig("ui", "ssh", opts['ssh'])
2165 ui.setconfig("ui", "ssh", opts['ssh'])
2166 if opts['remotecmd']:
2166 if opts['remotecmd']:
2167 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2167 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2168
2168
2169 other = hg.repository(ui, dest)
2169 other = hg.repository(ui, dest)
2170 revs = None
2170 revs = None
2171 if opts['rev']:
2171 if opts['rev']:
2172 revs = [repo.lookup(rev) for rev in opts['rev']]
2172 revs = [repo.lookup(rev) for rev in opts['rev']]
2173 r = repo.push(other, opts['force'], revs=revs)
2173 r = repo.push(other, opts['force'], revs=revs)
2174 return r == 0
2174 return r == 0
2175
2175
2176 def rawcommit(ui, repo, *flist, **rc):
2176 def rawcommit(ui, repo, *flist, **rc):
2177 """raw commit interface (DEPRECATED)
2177 """raw commit interface (DEPRECATED)
2178
2178
2179 (DEPRECATED)
2179 (DEPRECATED)
2180 Lowlevel commit, for use in helper scripts.
2180 Lowlevel commit, for use in helper scripts.
2181
2181
2182 This command is not intended to be used by normal users, as it is
2182 This command is not intended to be used by normal users, as it is
2183 primarily useful for importing from other SCMs.
2183 primarily useful for importing from other SCMs.
2184
2184
2185 This command is now deprecated and will be removed in a future
2185 This command is now deprecated and will be removed in a future
2186 release, please use debugsetparents and commit instead.
2186 release, please use debugsetparents and commit instead.
2187 """
2187 """
2188
2188
2189 ui.warn(_("(the rawcommit command is deprecated)\n"))
2189 ui.warn(_("(the rawcommit command is deprecated)\n"))
2190
2190
2191 message = rc['message']
2191 message = rc['message']
2192 if not message and rc['logfile']:
2192 if not message and rc['logfile']:
2193 try:
2193 try:
2194 message = open(rc['logfile']).read()
2194 message = open(rc['logfile']).read()
2195 except IOError:
2195 except IOError:
2196 pass
2196 pass
2197 if not message and not rc['logfile']:
2197 if not message and not rc['logfile']:
2198 raise util.Abort(_("missing commit message"))
2198 raise util.Abort(_("missing commit message"))
2199
2199
2200 files = relpath(repo, list(flist))
2200 files = relpath(repo, list(flist))
2201 if rc['files']:
2201 if rc['files']:
2202 files += open(rc['files']).read().splitlines()
2202 files += open(rc['files']).read().splitlines()
2203
2203
2204 rc['parent'] = map(repo.lookup, rc['parent'])
2204 rc['parent'] = map(repo.lookup, rc['parent'])
2205
2205
2206 try:
2206 try:
2207 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2207 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2208 except ValueError, inst:
2208 except ValueError, inst:
2209 raise util.Abort(str(inst))
2209 raise util.Abort(str(inst))
2210
2210
2211 def recover(ui, repo):
2211 def recover(ui, repo):
2212 """roll back an interrupted transaction
2212 """roll back an interrupted transaction
2213
2213
2214 Recover from an interrupted commit or pull.
2214 Recover from an interrupted commit or pull.
2215
2215
2216 This command tries to fix the repository status after an interrupted
2216 This command tries to fix the repository status after an interrupted
2217 operation. It should only be necessary when Mercurial suggests it.
2217 operation. It should only be necessary when Mercurial suggests it.
2218 """
2218 """
2219 if repo.recover():
2219 if repo.recover():
2220 return repo.verify()
2220 return repo.verify()
2221 return False
2221 return False
2222
2222
2223 def remove(ui, repo, pat, *pats, **opts):
2223 def remove(ui, repo, pat, *pats, **opts):
2224 """remove the specified files on the next commit
2224 """remove the specified files on the next commit
2225
2225
2226 Schedule the indicated files for removal from the repository.
2226 Schedule the indicated files for removal from the repository.
2227
2227
2228 This command schedules the files to be removed at the next commit.
2228 This command schedules the files to be removed at the next commit.
2229 This only removes files from the current branch, not from the
2229 This only removes files from the current branch, not from the
2230 entire project history. If the files still exist in the working
2230 entire project history. If the files still exist in the working
2231 directory, they will be deleted from it.
2231 directory, they will be deleted from it.
2232 """
2232 """
2233 names = []
2233 names = []
2234 def okaytoremove(abs, rel, exact):
2234 def okaytoremove(abs, rel, exact):
2235 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2235 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2236 reason = None
2236 reason = None
2237 if modified and not opts['force']:
2237 if modified and not opts['force']:
2238 reason = _('is modified')
2238 reason = _('is modified')
2239 elif added:
2239 elif added:
2240 reason = _('has been marked for add')
2240 reason = _('has been marked for add')
2241 elif unknown:
2241 elif unknown:
2242 reason = _('is not managed')
2242 reason = _('is not managed')
2243 if reason:
2243 if reason:
2244 if exact:
2244 if exact:
2245 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2245 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2246 else:
2246 else:
2247 return True
2247 return True
2248 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
2248 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
2249 if okaytoremove(abs, rel, exact):
2249 if okaytoremove(abs, rel, exact):
2250 if ui.verbose or not exact:
2250 if ui.verbose or not exact:
2251 ui.status(_('removing %s\n') % rel)
2251 ui.status(_('removing %s\n') % rel)
2252 names.append(abs)
2252 names.append(abs)
2253 repo.remove(names, unlink=True)
2253 repo.remove(names, unlink=True)
2254
2254
2255 def rename(ui, repo, *pats, **opts):
2255 def rename(ui, repo, *pats, **opts):
2256 """rename files; equivalent of copy + remove
2256 """rename files; equivalent of copy + remove
2257
2257
2258 Mark dest as copies of sources; mark sources for deletion. If
2258 Mark dest as copies of sources; mark sources for deletion. If
2259 dest is a directory, copies are put in that directory. If dest is
2259 dest is a directory, copies are put in that directory. If dest is
2260 a file, there can only be one source.
2260 a file, there can only be one source.
2261
2261
2262 By default, this command copies the contents of files as they
2262 By default, this command copies the contents of files as they
2263 stand in the working directory. If invoked with --after, the
2263 stand in the working directory. If invoked with --after, the
2264 operation is recorded, but no copying is performed.
2264 operation is recorded, but no copying is performed.
2265
2265
2266 This command takes effect in the next commit.
2266 This command takes effect in the next commit.
2267
2267
2268 NOTE: This command should be treated as experimental. While it
2268 NOTE: This command should be treated as experimental. While it
2269 should properly record rename files, this information is not yet
2269 should properly record rename files, this information is not yet
2270 fully used by merge, nor fully reported by log.
2270 fully used by merge, nor fully reported by log.
2271 """
2271 """
2272 wlock = repo.wlock(0)
2272 wlock = repo.wlock(0)
2273 errs, copied = docopy(ui, repo, pats, opts, wlock)
2273 errs, copied = docopy(ui, repo, pats, opts, wlock)
2274 names = []
2274 names = []
2275 for abs, rel, exact in copied:
2275 for abs, rel, exact in copied:
2276 if ui.verbose or not exact:
2276 if ui.verbose or not exact:
2277 ui.status(_('removing %s\n') % rel)
2277 ui.status(_('removing %s\n') % rel)
2278 names.append(abs)
2278 names.append(abs)
2279 repo.remove(names, True, wlock)
2279 repo.remove(names, True, wlock)
2280 return errs
2280 return errs
2281
2281
2282 def revert(ui, repo, *pats, **opts):
2282 def revert(ui, repo, *pats, **opts):
2283 """revert modified files or dirs back to their unmodified states
2283 """revert modified files or dirs back to their unmodified states
2284
2284
2285 In its default mode, it reverts any uncommitted modifications made
2285 In its default mode, it reverts any uncommitted modifications made
2286 to the named files or directories. This restores the contents of
2286 to the named files or directories. This restores the contents of
2287 the affected files to an unmodified state.
2287 the affected files to an unmodified state.
2288
2288
2289 Using the -r option, it reverts the given files or directories to
2289 Using the -r option, it reverts the given files or directories to
2290 their state as of an earlier revision. This can be helpful to "roll
2290 their state as of an earlier revision. This can be helpful to "roll
2291 back" some or all of a change that should not have been committed.
2291 back" some or all of a change that should not have been committed.
2292
2292
2293 Revert modifies the working directory. It does not commit any
2293 Revert modifies the working directory. It does not commit any
2294 changes, or change the parent of the current working directory.
2294 changes, or change the parent of the current working directory.
2295
2295
2296 If a file has been deleted, it is recreated. If the executable
2296 If a file has been deleted, it is recreated. If the executable
2297 mode of a file was changed, it is reset.
2297 mode of a file was changed, it is reset.
2298
2298
2299 If names are given, all files matching the names are reverted.
2299 If names are given, all files matching the names are reverted.
2300
2300
2301 If no arguments are given, all files in the repository are reverted.
2301 If no arguments are given, all files in the repository are reverted.
2302 """
2302 """
2303 node = opts['rev'] and repo.lookup(opts['rev']) or \
2303 node = opts['rev'] and repo.lookup(opts['rev']) or \
2304 repo.dirstate.parents()[0]
2304 repo.dirstate.parents()[0]
2305
2305
2306 files, choose, anypats = matchpats(repo, pats, opts)
2306 files, choose, anypats = matchpats(repo, pats, opts)
2307 modified, added, removed, deleted, unknown = repo.changes(match=choose)
2307 modified, added, removed, deleted, unknown = repo.changes(match=choose)
2308 repo.forget(added)
2308 repo.forget(added)
2309 repo.undelete(removed)
2309 repo.undelete(removed)
2310
2310
2311 return repo.update(node, False, True, choose, False)
2311 return repo.update(node, False, True, choose, False)
2312
2312
2313 def root(ui, repo):
2313 def root(ui, repo):
2314 """print the root (top) of the current working dir
2314 """print the root (top) of the current working dir
2315
2315
2316 Print the root directory of the current repository.
2316 Print the root directory of the current repository.
2317 """
2317 """
2318 ui.write(repo.root + "\n")
2318 ui.write(repo.root + "\n")
2319
2319
2320 def serve(ui, repo, **opts):
2320 def serve(ui, repo, **opts):
2321 """export the repository via HTTP
2321 """export the repository via HTTP
2322
2322
2323 Start a local HTTP repository browser and pull server.
2323 Start a local HTTP repository browser and pull server.
2324
2324
2325 By default, the server logs accesses to stdout and errors to
2325 By default, the server logs accesses to stdout and errors to
2326 stderr. Use the "-A" and "-E" options to log to files.
2326 stderr. Use the "-A" and "-E" options to log to files.
2327 """
2327 """
2328
2328
2329 if opts["stdio"]:
2329 if opts["stdio"]:
2330 fin, fout = sys.stdin, sys.stdout
2330 fin, fout = sys.stdin, sys.stdout
2331 sys.stdout = sys.stderr
2331 sys.stdout = sys.stderr
2332
2332
2333 # Prevent insertion/deletion of CRs
2333 # Prevent insertion/deletion of CRs
2334 util.set_binary(fin)
2334 util.set_binary(fin)
2335 util.set_binary(fout)
2335 util.set_binary(fout)
2336
2336
2337 def getarg():
2337 def getarg():
2338 argline = fin.readline()[:-1]
2338 argline = fin.readline()[:-1]
2339 arg, l = argline.split()
2339 arg, l = argline.split()
2340 val = fin.read(int(l))
2340 val = fin.read(int(l))
2341 return arg, val
2341 return arg, val
2342 def respond(v):
2342 def respond(v):
2343 fout.write("%d\n" % len(v))
2343 fout.write("%d\n" % len(v))
2344 fout.write(v)
2344 fout.write(v)
2345 fout.flush()
2345 fout.flush()
2346
2346
2347 lock = None
2347 lock = None
2348
2348
2349 while 1:
2349 while 1:
2350 cmd = fin.readline()[:-1]
2350 cmd = fin.readline()[:-1]
2351 if cmd == '':
2351 if cmd == '':
2352 return
2352 return
2353 if cmd == "heads":
2353 if cmd == "heads":
2354 h = repo.heads()
2354 h = repo.heads()
2355 respond(" ".join(map(hex, h)) + "\n")
2355 respond(" ".join(map(hex, h)) + "\n")
2356 if cmd == "lock":
2356 if cmd == "lock":
2357 lock = repo.lock()
2357 lock = repo.lock()
2358 respond("")
2358 respond("")
2359 if cmd == "unlock":
2359 if cmd == "unlock":
2360 if lock:
2360 if lock:
2361 lock.release()
2361 lock.release()
2362 lock = None
2362 lock = None
2363 respond("")
2363 respond("")
2364 elif cmd == "branches":
2364 elif cmd == "branches":
2365 arg, nodes = getarg()
2365 arg, nodes = getarg()
2366 nodes = map(bin, nodes.split(" "))
2366 nodes = map(bin, nodes.split(" "))
2367 r = []
2367 r = []
2368 for b in repo.branches(nodes):
2368 for b in repo.branches(nodes):
2369 r.append(" ".join(map(hex, b)) + "\n")
2369 r.append(" ".join(map(hex, b)) + "\n")
2370 respond("".join(r))
2370 respond("".join(r))
2371 elif cmd == "between":
2371 elif cmd == "between":
2372 arg, pairs = getarg()
2372 arg, pairs = getarg()
2373 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2373 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2374 r = []
2374 r = []
2375 for b in repo.between(pairs):
2375 for b in repo.between(pairs):
2376 r.append(" ".join(map(hex, b)) + "\n")
2376 r.append(" ".join(map(hex, b)) + "\n")
2377 respond("".join(r))
2377 respond("".join(r))
2378 elif cmd == "changegroup":
2378 elif cmd == "changegroup":
2379 nodes = []
2379 nodes = []
2380 arg, roots = getarg()
2380 arg, roots = getarg()
2381 nodes = map(bin, roots.split(" "))
2381 nodes = map(bin, roots.split(" "))
2382
2382
2383 cg = repo.changegroup(nodes, 'serve')
2383 cg = repo.changegroup(nodes, 'serve')
2384 while 1:
2384 while 1:
2385 d = cg.read(4096)
2385 d = cg.read(4096)
2386 if not d:
2386 if not d:
2387 break
2387 break
2388 fout.write(d)
2388 fout.write(d)
2389
2389
2390 fout.flush()
2390 fout.flush()
2391
2391
2392 elif cmd == "addchangegroup":
2392 elif cmd == "addchangegroup":
2393 if not lock:
2393 if not lock:
2394 respond("not locked")
2394 respond("not locked")
2395 continue
2395 continue
2396 respond("")
2396 respond("")
2397
2397
2398 r = repo.addchangegroup(fin)
2398 r = repo.addchangegroup(fin)
2399 respond(str(r))
2399 respond(str(r))
2400
2400
2401 optlist = "name templates style address port ipv6 accesslog errorlog"
2401 optlist = "name templates style address port ipv6 accesslog errorlog"
2402 for o in optlist.split():
2402 for o in optlist.split():
2403 if opts[o]:
2403 if opts[o]:
2404 ui.setconfig("web", o, opts[o])
2404 ui.setconfig("web", o, opts[o])
2405
2405
2406 if opts['daemon'] and not opts['daemon_pipefds']:
2406 if opts['daemon'] and not opts['daemon_pipefds']:
2407 rfd, wfd = os.pipe()
2407 rfd, wfd = os.pipe()
2408 args = sys.argv[:]
2408 args = sys.argv[:]
2409 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2409 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2410 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2410 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2411 args[0], args)
2411 args[0], args)
2412 os.close(wfd)
2412 os.close(wfd)
2413 os.read(rfd, 1)
2413 os.read(rfd, 1)
2414 os._exit(0)
2414 os._exit(0)
2415
2415
2416 try:
2416 try:
2417 httpd = hgweb.create_server(repo)
2417 httpd = hgweb.create_server(repo)
2418 except socket.error, inst:
2418 except socket.error, inst:
2419 raise util.Abort(_('cannot start server: ') + inst.args[1])
2419 raise util.Abort(_('cannot start server: ') + inst.args[1])
2420
2420
2421 if ui.verbose:
2421 if ui.verbose:
2422 addr, port = httpd.socket.getsockname()
2422 addr, port = httpd.socket.getsockname()
2423 if addr == '0.0.0.0':
2423 if addr == '0.0.0.0':
2424 addr = socket.gethostname()
2424 addr = socket.gethostname()
2425 else:
2425 else:
2426 try:
2426 try:
2427 addr = socket.gethostbyaddr(addr)[0]
2427 addr = socket.gethostbyaddr(addr)[0]
2428 except socket.error:
2428 except socket.error:
2429 pass
2429 pass
2430 if port != 80:
2430 if port != 80:
2431 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2431 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2432 else:
2432 else:
2433 ui.status(_('listening at http://%s/\n') % addr)
2433 ui.status(_('listening at http://%s/\n') % addr)
2434
2434
2435 if opts['pid_file']:
2435 if opts['pid_file']:
2436 fp = open(opts['pid_file'], 'w')
2436 fp = open(opts['pid_file'], 'w')
2437 fp.write(str(os.getpid()))
2437 fp.write(str(os.getpid()))
2438 fp.close()
2438 fp.close()
2439
2439
2440 if opts['daemon_pipefds']:
2440 if opts['daemon_pipefds']:
2441 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2441 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2442 os.close(rfd)
2442 os.close(rfd)
2443 os.write(wfd, 'y')
2443 os.write(wfd, 'y')
2444 os.close(wfd)
2444 os.close(wfd)
2445 sys.stdout.flush()
2445 sys.stdout.flush()
2446 sys.stderr.flush()
2446 sys.stderr.flush()
2447 fd = os.open(util.nulldev, os.O_RDWR)
2447 fd = os.open(util.nulldev, os.O_RDWR)
2448 if fd != 0: os.dup2(fd, 0)
2448 if fd != 0: os.dup2(fd, 0)
2449 if fd != 1: os.dup2(fd, 1)
2449 if fd != 1: os.dup2(fd, 1)
2450 if fd != 2: os.dup2(fd, 2)
2450 if fd != 2: os.dup2(fd, 2)
2451 if fd not in (0, 1, 2): os.close(fd)
2451 if fd not in (0, 1, 2): os.close(fd)
2452
2452
2453 httpd.serve_forever()
2453 httpd.serve_forever()
2454
2454
2455 def status(ui, repo, *pats, **opts):
2455 def status(ui, repo, *pats, **opts):
2456 """show changed files in the working directory
2456 """show changed files in the working directory
2457
2457
2458 Show changed files in the repository. If names are
2458 Show changed files in the repository. If names are
2459 given, only files that match are shown.
2459 given, only files that match are shown.
2460
2460
2461 The codes used to show the status of files are:
2461 The codes used to show the status of files are:
2462 M = modified
2462 M = modified
2463 A = added
2463 A = added
2464 R = removed
2464 R = removed
2465 ! = deleted, but still tracked
2465 ! = deleted, but still tracked
2466 ? = not tracked
2466 ? = not tracked
2467 I = ignored (not shown by default)
2467 """
2468 """
2468
2469
2470 show_ignored = opts['ignored'] and True or False
2469 files, matchfn, anypats = matchpats(repo, pats, opts)
2471 files, matchfn, anypats = matchpats(repo, pats, opts)
2470 cwd = (pats and repo.getcwd()) or ''
2472 cwd = (pats and repo.getcwd()) or ''
2471 modified, added, removed, deleted, unknown = [
2473 modified, added, removed, deleted, unknown, ignored = [
2472 [util.pathto(cwd, x) for x in n]
2474 [util.pathto(cwd, x) for x in n]
2473 for n in repo.changes(files=files, match=matchfn)]
2475 for n in repo.changes(files=files, match=matchfn,
2476 show_ignored=show_ignored)]
2474
2477
2475 changetypes = [('modified', 'M', modified),
2478 changetypes = [('modified', 'M', modified),
2476 ('added', 'A', added),
2479 ('added', 'A', added),
2477 ('removed', 'R', removed),
2480 ('removed', 'R', removed),
2478 ('deleted', '!', deleted),
2481 ('deleted', '!', deleted),
2479 ('unknown', '?', unknown)]
2482 ('unknown', '?', unknown),
2483 ('ignored', 'I', ignored)]
2480
2484
2481 end = opts['print0'] and '\0' or '\n'
2485 end = opts['print0'] and '\0' or '\n'
2482
2486
2483 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2487 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2484 or changetypes):
2488 or changetypes):
2485 if opts['no_status']:
2489 if opts['no_status']:
2486 format = "%%s%s" % end
2490 format = "%%s%s" % end
2487 else:
2491 else:
2488 format = "%s %%s%s" % (char, end)
2492 format = "%s %%s%s" % (char, end)
2489
2493
2490 for f in changes:
2494 for f in changes:
2491 ui.write(format % f)
2495 ui.write(format % f)
2492
2496
2493 def tag(ui, repo, name, rev_=None, **opts):
2497 def tag(ui, repo, name, rev_=None, **opts):
2494 """add a tag for the current tip or a given revision
2498 """add a tag for the current tip or a given revision
2495
2499
2496 Name a particular revision using <name>.
2500 Name a particular revision using <name>.
2497
2501
2498 Tags are used to name particular revisions of the repository and are
2502 Tags are used to name particular revisions of the repository and are
2499 very useful to compare different revision, to go back to significant
2503 very useful to compare different revision, to go back to significant
2500 earlier versions or to mark branch points as releases, etc.
2504 earlier versions or to mark branch points as releases, etc.
2501
2505
2502 If no revision is given, the tip is used.
2506 If no revision is given, the tip is used.
2503
2507
2504 To facilitate version control, distribution, and merging of tags,
2508 To facilitate version control, distribution, and merging of tags,
2505 they are stored as a file named ".hgtags" which is managed
2509 they are stored as a file named ".hgtags" which is managed
2506 similarly to other project files and can be hand-edited if
2510 similarly to other project files and can be hand-edited if
2507 necessary. The file '.hg/localtags' is used for local tags (not
2511 necessary. The file '.hg/localtags' is used for local tags (not
2508 shared among repositories).
2512 shared among repositories).
2509 """
2513 """
2510 if name == "tip":
2514 if name == "tip":
2511 raise util.Abort(_("the name 'tip' is reserved"))
2515 raise util.Abort(_("the name 'tip' is reserved"))
2512 if rev_ is not None:
2516 if rev_ is not None:
2513 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2517 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2514 "please use 'hg tag [-r REV] NAME' instead\n"))
2518 "please use 'hg tag [-r REV] NAME' instead\n"))
2515 if opts['rev']:
2519 if opts['rev']:
2516 raise util.Abort(_("use only one form to specify the revision"))
2520 raise util.Abort(_("use only one form to specify the revision"))
2517 if opts['rev']:
2521 if opts['rev']:
2518 rev_ = opts['rev']
2522 rev_ = opts['rev']
2519 if rev_:
2523 if rev_:
2520 r = hex(repo.lookup(rev_))
2524 r = hex(repo.lookup(rev_))
2521 else:
2525 else:
2522 r = hex(repo.changelog.tip())
2526 r = hex(repo.changelog.tip())
2523
2527
2524 disallowed = (revrangesep, '\r', '\n')
2528 disallowed = (revrangesep, '\r', '\n')
2525 for c in disallowed:
2529 for c in disallowed:
2526 if name.find(c) >= 0:
2530 if name.find(c) >= 0:
2527 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2531 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2528
2532
2529 repo.hook('pretag', throw=True, node=r, tag=name,
2533 repo.hook('pretag', throw=True, node=r, tag=name,
2530 local=int(not not opts['local']))
2534 local=int(not not opts['local']))
2531
2535
2532 if opts['local']:
2536 if opts['local']:
2533 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2537 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2534 repo.hook('tag', node=r, tag=name, local=1)
2538 repo.hook('tag', node=r, tag=name, local=1)
2535 return
2539 return
2536
2540
2537 for x in repo.changes():
2541 for x in repo.changes():
2538 if ".hgtags" in x:
2542 if ".hgtags" in x:
2539 raise util.Abort(_("working copy of .hgtags is changed "
2543 raise util.Abort(_("working copy of .hgtags is changed "
2540 "(please commit .hgtags manually)"))
2544 "(please commit .hgtags manually)"))
2541
2545
2542 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2546 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2543 if repo.dirstate.state(".hgtags") == '?':
2547 if repo.dirstate.state(".hgtags") == '?':
2544 repo.add([".hgtags"])
2548 repo.add([".hgtags"])
2545
2549
2546 message = (opts['message'] or
2550 message = (opts['message'] or
2547 _("Added tag %s for changeset %s") % (name, r))
2551 _("Added tag %s for changeset %s") % (name, r))
2548 try:
2552 try:
2549 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2553 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2550 repo.hook('tag', node=r, tag=name, local=0)
2554 repo.hook('tag', node=r, tag=name, local=0)
2551 except ValueError, inst:
2555 except ValueError, inst:
2552 raise util.Abort(str(inst))
2556 raise util.Abort(str(inst))
2553
2557
2554 def tags(ui, repo):
2558 def tags(ui, repo):
2555 """list repository tags
2559 """list repository tags
2556
2560
2557 List the repository tags.
2561 List the repository tags.
2558
2562
2559 This lists both regular and local tags.
2563 This lists both regular and local tags.
2560 """
2564 """
2561
2565
2562 l = repo.tagslist()
2566 l = repo.tagslist()
2563 l.reverse()
2567 l.reverse()
2564 for t, n in l:
2568 for t, n in l:
2565 try:
2569 try:
2566 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2570 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2567 except KeyError:
2571 except KeyError:
2568 r = " ?:?"
2572 r = " ?:?"
2569 ui.write("%-30s %s\n" % (t, r))
2573 ui.write("%-30s %s\n" % (t, r))
2570
2574
2571 def tip(ui, repo, **opts):
2575 def tip(ui, repo, **opts):
2572 """show the tip revision
2576 """show the tip revision
2573
2577
2574 Show the tip revision.
2578 Show the tip revision.
2575 """
2579 """
2576 n = repo.changelog.tip()
2580 n = repo.changelog.tip()
2577 br = None
2581 br = None
2578 if opts['branches']:
2582 if opts['branches']:
2579 br = repo.branchlookup([n])
2583 br = repo.branchlookup([n])
2580 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2584 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2581 if opts['patch']:
2585 if opts['patch']:
2582 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2586 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2583
2587
2584 def unbundle(ui, repo, fname, **opts):
2588 def unbundle(ui, repo, fname, **opts):
2585 """apply a changegroup file
2589 """apply a changegroup file
2586
2590
2587 Apply a compressed changegroup file generated by the bundle
2591 Apply a compressed changegroup file generated by the bundle
2588 command.
2592 command.
2589 """
2593 """
2590 f = urllib.urlopen(fname)
2594 f = urllib.urlopen(fname)
2591
2595
2592 header = f.read(6)
2596 header = f.read(6)
2593 if not header.startswith("HG"):
2597 if not header.startswith("HG"):
2594 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2598 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2595 elif not header.startswith("HG10"):
2599 elif not header.startswith("HG10"):
2596 raise util.Abort(_("%s: unknown bundle version") % fname)
2600 raise util.Abort(_("%s: unknown bundle version") % fname)
2597 elif header == "HG10BZ":
2601 elif header == "HG10BZ":
2598 def generator(f):
2602 def generator(f):
2599 zd = bz2.BZ2Decompressor()
2603 zd = bz2.BZ2Decompressor()
2600 zd.decompress("BZ")
2604 zd.decompress("BZ")
2601 for chunk in f:
2605 for chunk in f:
2602 yield zd.decompress(chunk)
2606 yield zd.decompress(chunk)
2603 elif header == "HG10UN":
2607 elif header == "HG10UN":
2604 def generator(f):
2608 def generator(f):
2605 for chunk in f:
2609 for chunk in f:
2606 yield chunk
2610 yield chunk
2607 else:
2611 else:
2608 raise util.Abort(_("%s: unknown bundle compression type")
2612 raise util.Abort(_("%s: unknown bundle compression type")
2609 % fname)
2613 % fname)
2610 gen = generator(util.filechunkiter(f, 4096))
2614 gen = generator(util.filechunkiter(f, 4096))
2611 modheads = repo.addchangegroup(util.chunkbuffer(gen))
2615 modheads = repo.addchangegroup(util.chunkbuffer(gen))
2612 return postincoming(ui, repo, modheads, opts['update'])
2616 return postincoming(ui, repo, modheads, opts['update'])
2613
2617
2614 def undo(ui, repo):
2618 def undo(ui, repo):
2615 """undo the last commit or pull
2619 """undo the last commit or pull
2616
2620
2617 Roll back the last pull or commit transaction on the
2621 Roll back the last pull or commit transaction on the
2618 repository, restoring the project to its earlier state.
2622 repository, restoring the project to its earlier state.
2619
2623
2620 This command should be used with care. There is only one level of
2624 This command should be used with care. There is only one level of
2621 undo and there is no redo.
2625 undo and there is no redo.
2622
2626
2623 This command is not intended for use on public repositories. Once
2627 This command is not intended for use on public repositories. Once
2624 a change is visible for pull by other users, undoing it locally is
2628 a change is visible for pull by other users, undoing it locally is
2625 ineffective.
2629 ineffective.
2626 """
2630 """
2627 repo.undo()
2631 repo.undo()
2628
2632
2629 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2633 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2630 branch=None, **opts):
2634 branch=None, **opts):
2631 """update or merge working directory
2635 """update or merge working directory
2632
2636
2633 Update the working directory to the specified revision.
2637 Update the working directory to the specified revision.
2634
2638
2635 If there are no outstanding changes in the working directory and
2639 If there are no outstanding changes in the working directory and
2636 there is a linear relationship between the current version and the
2640 there is a linear relationship between the current version and the
2637 requested version, the result is the requested version.
2641 requested version, the result is the requested version.
2638
2642
2639 Otherwise the result is a merge between the contents of the
2643 Otherwise the result is a merge between the contents of the
2640 current working directory and the requested version. Files that
2644 current working directory and the requested version. Files that
2641 changed between either parent are marked as changed for the next
2645 changed between either parent are marked as changed for the next
2642 commit and a commit must be performed before any further updates
2646 commit and a commit must be performed before any further updates
2643 are allowed.
2647 are allowed.
2644
2648
2645 By default, update will refuse to run if doing so would require
2649 By default, update will refuse to run if doing so would require
2646 merging or discarding local changes.
2650 merging or discarding local changes.
2647 """
2651 """
2648 if branch:
2652 if branch:
2649 br = repo.branchlookup(branch=branch)
2653 br = repo.branchlookup(branch=branch)
2650 found = []
2654 found = []
2651 for x in br:
2655 for x in br:
2652 if branch in br[x]:
2656 if branch in br[x]:
2653 found.append(x)
2657 found.append(x)
2654 if len(found) > 1:
2658 if len(found) > 1:
2655 ui.warn(_("Found multiple heads for %s\n") % branch)
2659 ui.warn(_("Found multiple heads for %s\n") % branch)
2656 for x in found:
2660 for x in found:
2657 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2661 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2658 return 1
2662 return 1
2659 if len(found) == 1:
2663 if len(found) == 1:
2660 node = found[0]
2664 node = found[0]
2661 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2665 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2662 else:
2666 else:
2663 ui.warn(_("branch %s not found\n") % (branch))
2667 ui.warn(_("branch %s not found\n") % (branch))
2664 return 1
2668 return 1
2665 else:
2669 else:
2666 node = node and repo.lookup(node) or repo.changelog.tip()
2670 node = node and repo.lookup(node) or repo.changelog.tip()
2667 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2671 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2668
2672
2669 def verify(ui, repo):
2673 def verify(ui, repo):
2670 """verify the integrity of the repository
2674 """verify the integrity of the repository
2671
2675
2672 Verify the integrity of the current repository.
2676 Verify the integrity of the current repository.
2673
2677
2674 This will perform an extensive check of the repository's
2678 This will perform an extensive check of the repository's
2675 integrity, validating the hashes and checksums of each entry in
2679 integrity, validating the hashes and checksums of each entry in
2676 the changelog, manifest, and tracked files, as well as the
2680 the changelog, manifest, and tracked files, as well as the
2677 integrity of their crosslinks and indices.
2681 integrity of their crosslinks and indices.
2678 """
2682 """
2679 return repo.verify()
2683 return repo.verify()
2680
2684
2681 # Command options and aliases are listed here, alphabetically
2685 # Command options and aliases are listed here, alphabetically
2682
2686
2683 table = {
2687 table = {
2684 "^add":
2688 "^add":
2685 (add,
2689 (add,
2686 [('I', 'include', [], _('include names matching the given patterns')),
2690 [('I', 'include', [], _('include names matching the given patterns')),
2687 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2691 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2688 _('hg add [OPTION]... [FILE]...')),
2692 _('hg add [OPTION]... [FILE]...')),
2689 "addremove":
2693 "addremove":
2690 (addremove,
2694 (addremove,
2691 [('I', 'include', [], _('include names matching the given patterns')),
2695 [('I', 'include', [], _('include names matching the given patterns')),
2692 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2696 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2693 _('hg addremove [OPTION]... [FILE]...')),
2697 _('hg addremove [OPTION]... [FILE]...')),
2694 "^annotate":
2698 "^annotate":
2695 (annotate,
2699 (annotate,
2696 [('r', 'rev', '', _('annotate the specified revision')),
2700 [('r', 'rev', '', _('annotate the specified revision')),
2697 ('a', 'text', None, _('treat all files as text')),
2701 ('a', 'text', None, _('treat all files as text')),
2698 ('u', 'user', None, _('list the author')),
2702 ('u', 'user', None, _('list the author')),
2699 ('d', 'date', None, _('list the date')),
2703 ('d', 'date', None, _('list the date')),
2700 ('n', 'number', None, _('list the revision number (default)')),
2704 ('n', 'number', None, _('list the revision number (default)')),
2701 ('c', 'changeset', None, _('list the changeset')),
2705 ('c', 'changeset', None, _('list the changeset')),
2702 ('I', 'include', [], _('include names matching the given patterns')),
2706 ('I', 'include', [], _('include names matching the given patterns')),
2703 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2707 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2704 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2708 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2705 "bundle":
2709 "bundle":
2706 (bundle,
2710 (bundle,
2707 [('f', 'force', None,
2711 [('f', 'force', None,
2708 _('run even when remote repository is unrelated'))],
2712 _('run even when remote repository is unrelated'))],
2709 _('hg bundle FILE DEST')),
2713 _('hg bundle FILE DEST')),
2710 "cat":
2714 "cat":
2711 (cat,
2715 (cat,
2712 [('o', 'output', '', _('print output to file with formatted name')),
2716 [('o', 'output', '', _('print output to file with formatted name')),
2713 ('r', 'rev', '', _('print the given revision')),
2717 ('r', 'rev', '', _('print the given revision')),
2714 ('I', 'include', [], _('include names matching the given patterns')),
2718 ('I', 'include', [], _('include names matching the given patterns')),
2715 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2719 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2716 _('hg cat [OPTION]... FILE...')),
2720 _('hg cat [OPTION]... FILE...')),
2717 "^clone":
2721 "^clone":
2718 (clone,
2722 (clone,
2719 [('U', 'noupdate', None, _('do not update the new working directory')),
2723 [('U', 'noupdate', None, _('do not update the new working directory')),
2720 ('r', 'rev', [],
2724 ('r', 'rev', [],
2721 _('a changeset you would like to have after cloning')),
2725 _('a changeset you would like to have after cloning')),
2722 ('', 'pull', None, _('use pull protocol to copy metadata')),
2726 ('', 'pull', None, _('use pull protocol to copy metadata')),
2723 ('e', 'ssh', '', _('specify ssh command to use')),
2727 ('e', 'ssh', '', _('specify ssh command to use')),
2724 ('', 'remotecmd', '',
2728 ('', 'remotecmd', '',
2725 _('specify hg command to run on the remote side'))],
2729 _('specify hg command to run on the remote side'))],
2726 _('hg clone [OPTION]... SOURCE [DEST]')),
2730 _('hg clone [OPTION]... SOURCE [DEST]')),
2727 "^commit|ci":
2731 "^commit|ci":
2728 (commit,
2732 (commit,
2729 [('A', 'addremove', None, _('run addremove during commit')),
2733 [('A', 'addremove', None, _('run addremove during commit')),
2730 ('m', 'message', '', _('use <text> as commit message')),
2734 ('m', 'message', '', _('use <text> as commit message')),
2731 ('l', 'logfile', '', _('read the commit message from <file>')),
2735 ('l', 'logfile', '', _('read the commit message from <file>')),
2732 ('d', 'date', '', _('record datecode as commit date')),
2736 ('d', 'date', '', _('record datecode as commit date')),
2733 ('u', 'user', '', _('record user as commiter')),
2737 ('u', 'user', '', _('record user as commiter')),
2734 ('I', 'include', [], _('include names matching the given patterns')),
2738 ('I', 'include', [], _('include names matching the given patterns')),
2735 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2739 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2736 _('hg commit [OPTION]... [FILE]...')),
2740 _('hg commit [OPTION]... [FILE]...')),
2737 "copy|cp":
2741 "copy|cp":
2738 (copy,
2742 (copy,
2739 [('A', 'after', None, _('record a copy that has already occurred')),
2743 [('A', 'after', None, _('record a copy that has already occurred')),
2740 ('f', 'force', None,
2744 ('f', 'force', None,
2741 _('forcibly copy over an existing managed file')),
2745 _('forcibly copy over an existing managed file')),
2742 ('I', 'include', [], _('include names matching the given patterns')),
2746 ('I', 'include', [], _('include names matching the given patterns')),
2743 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2747 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2744 _('hg copy [OPTION]... [SOURCE]... DEST')),
2748 _('hg copy [OPTION]... [SOURCE]... DEST')),
2745 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2749 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2746 "debugcomplete": (debugcomplete, [], _('debugcomplete CMD')),
2750 "debugcomplete": (debugcomplete, [], _('debugcomplete CMD')),
2747 "debugrebuildstate":
2751 "debugrebuildstate":
2748 (debugrebuildstate,
2752 (debugrebuildstate,
2749 [('r', 'rev', '', _('revision to rebuild to'))],
2753 [('r', 'rev', '', _('revision to rebuild to'))],
2750 _('debugrebuildstate [-r REV] [REV]')),
2754 _('debugrebuildstate [-r REV] [REV]')),
2751 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2755 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2752 "debugconfig": (debugconfig, [], _('debugconfig')),
2756 "debugconfig": (debugconfig, [], _('debugconfig')),
2753 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2757 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2754 "debugstate": (debugstate, [], _('debugstate')),
2758 "debugstate": (debugstate, [], _('debugstate')),
2755 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2759 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2756 "debugindex": (debugindex, [], _('debugindex FILE')),
2760 "debugindex": (debugindex, [], _('debugindex FILE')),
2757 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2761 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2758 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2762 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2759 "debugwalk":
2763 "debugwalk":
2760 (debugwalk,
2764 (debugwalk,
2761 [('I', 'include', [], _('include names matching the given patterns')),
2765 [('I', 'include', [], _('include names matching the given patterns')),
2762 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2766 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2763 _('debugwalk [OPTION]... [FILE]...')),
2767 _('debugwalk [OPTION]... [FILE]...')),
2764 "^diff":
2768 "^diff":
2765 (diff,
2769 (diff,
2766 [('r', 'rev', [], _('revision')),
2770 [('r', 'rev', [], _('revision')),
2767 ('a', 'text', None, _('treat all files as text')),
2771 ('a', 'text', None, _('treat all files as text')),
2768 ('p', 'show-function', None,
2772 ('p', 'show-function', None,
2769 _('show which function each change is in')),
2773 _('show which function each change is in')),
2770 ('w', 'ignore-all-space', None,
2774 ('w', 'ignore-all-space', None,
2771 _('ignore white space when comparing lines')),
2775 _('ignore white space when comparing lines')),
2772 ('I', 'include', [], _('include names matching the given patterns')),
2776 ('I', 'include', [], _('include names matching the given patterns')),
2773 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2777 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2774 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2778 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2775 "^export":
2779 "^export":
2776 (export,
2780 (export,
2777 [('o', 'output', '', _('print output to file with formatted name')),
2781 [('o', 'output', '', _('print output to file with formatted name')),
2778 ('a', 'text', None, _('treat all files as text')),
2782 ('a', 'text', None, _('treat all files as text')),
2779 ('', 'switch-parent', None, _('diff against the second parent'))],
2783 ('', 'switch-parent', None, _('diff against the second parent'))],
2780 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2784 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2781 "forget":
2785 "forget":
2782 (forget,
2786 (forget,
2783 [('I', 'include', [], _('include names matching the given patterns')),
2787 [('I', 'include', [], _('include names matching the given patterns')),
2784 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2788 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2785 _('hg forget [OPTION]... FILE...')),
2789 _('hg forget [OPTION]... FILE...')),
2786 "grep":
2790 "grep":
2787 (grep,
2791 (grep,
2788 [('0', 'print0', None, _('end fields with NUL')),
2792 [('0', 'print0', None, _('end fields with NUL')),
2789 ('', 'all', None, _('print all revisions that match')),
2793 ('', 'all', None, _('print all revisions that match')),
2790 ('i', 'ignore-case', None, _('ignore case when matching')),
2794 ('i', 'ignore-case', None, _('ignore case when matching')),
2791 ('l', 'files-with-matches', None,
2795 ('l', 'files-with-matches', None,
2792 _('print only filenames and revs that match')),
2796 _('print only filenames and revs that match')),
2793 ('n', 'line-number', None, _('print matching line numbers')),
2797 ('n', 'line-number', None, _('print matching line numbers')),
2794 ('r', 'rev', [], _('search in given revision range')),
2798 ('r', 'rev', [], _('search in given revision range')),
2795 ('u', 'user', None, _('print user who committed change')),
2799 ('u', 'user', None, _('print user who committed change')),
2796 ('I', 'include', [], _('include names matching the given patterns')),
2800 ('I', 'include', [], _('include names matching the given patterns')),
2797 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2801 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2798 _('hg grep [OPTION]... PATTERN [FILE]...')),
2802 _('hg grep [OPTION]... PATTERN [FILE]...')),
2799 "heads":
2803 "heads":
2800 (heads,
2804 (heads,
2801 [('b', 'branches', None, _('show branches')),
2805 [('b', 'branches', None, _('show branches')),
2802 ('', 'style', '', _('display using template map file')),
2806 ('', 'style', '', _('display using template map file')),
2803 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2807 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2804 ('', 'template', '', _('display with template'))],
2808 ('', 'template', '', _('display with template'))],
2805 _('hg heads [-b] [-r <rev>]')),
2809 _('hg heads [-b] [-r <rev>]')),
2806 "help": (help_, [], _('hg help [COMMAND]')),
2810 "help": (help_, [], _('hg help [COMMAND]')),
2807 "identify|id": (identify, [], _('hg identify')),
2811 "identify|id": (identify, [], _('hg identify')),
2808 "import|patch":
2812 "import|patch":
2809 (import_,
2813 (import_,
2810 [('p', 'strip', 1,
2814 [('p', 'strip', 1,
2811 _('directory strip option for patch. This has the same\n') +
2815 _('directory strip option for patch. This has the same\n') +
2812 _('meaning as the corresponding patch option')),
2816 _('meaning as the corresponding patch option')),
2813 ('b', 'base', '', _('base path')),
2817 ('b', 'base', '', _('base path')),
2814 ('f', 'force', None,
2818 ('f', 'force', None,
2815 _('skip check for outstanding uncommitted changes'))],
2819 _('skip check for outstanding uncommitted changes'))],
2816 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2820 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2817 "incoming|in": (incoming,
2821 "incoming|in": (incoming,
2818 [('M', 'no-merges', None, _('do not show merges')),
2822 [('M', 'no-merges', None, _('do not show merges')),
2819 ('f', 'force', None,
2823 ('f', 'force', None,
2820 _('run even when remote repository is unrelated')),
2824 _('run even when remote repository is unrelated')),
2821 ('', 'style', '', _('display using template map file')),
2825 ('', 'style', '', _('display using template map file')),
2822 ('n', 'newest-first', None, _('show newest record first')),
2826 ('n', 'newest-first', None, _('show newest record first')),
2823 ('', 'bundle', '', _('file to store the bundles into')),
2827 ('', 'bundle', '', _('file to store the bundles into')),
2824 ('p', 'patch', None, _('show patch')),
2828 ('p', 'patch', None, _('show patch')),
2825 ('', 'template', '', _('display with template')),
2829 ('', 'template', '', _('display with template')),
2826 ('e', 'ssh', '', _('specify ssh command to use')),
2830 ('e', 'ssh', '', _('specify ssh command to use')),
2827 ('', 'remotecmd', '',
2831 ('', 'remotecmd', '',
2828 _('specify hg command to run on the remote side'))],
2832 _('specify hg command to run on the remote side'))],
2829 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
2833 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
2830 "^init": (init, [], _('hg init [DEST]')),
2834 "^init": (init, [], _('hg init [DEST]')),
2831 "locate":
2835 "locate":
2832 (locate,
2836 (locate,
2833 [('r', 'rev', '', _('search the repository as it stood at rev')),
2837 [('r', 'rev', '', _('search the repository as it stood at rev')),
2834 ('0', 'print0', None,
2838 ('0', 'print0', None,
2835 _('end filenames with NUL, for use with xargs')),
2839 _('end filenames with NUL, for use with xargs')),
2836 ('f', 'fullpath', None,
2840 ('f', 'fullpath', None,
2837 _('print complete paths from the filesystem root')),
2841 _('print complete paths from the filesystem root')),
2838 ('I', 'include', [], _('include names matching the given patterns')),
2842 ('I', 'include', [], _('include names matching the given patterns')),
2839 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2843 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2840 _('hg locate [OPTION]... [PATTERN]...')),
2844 _('hg locate [OPTION]... [PATTERN]...')),
2841 "^log|history":
2845 "^log|history":
2842 (log,
2846 (log,
2843 [('b', 'branches', None, _('show branches')),
2847 [('b', 'branches', None, _('show branches')),
2844 ('k', 'keyword', [], _('search for a keyword')),
2848 ('k', 'keyword', [], _('search for a keyword')),
2845 ('l', 'limit', '', _('limit number of changes displayed')),
2849 ('l', 'limit', '', _('limit number of changes displayed')),
2846 ('r', 'rev', [], _('show the specified revision or range')),
2850 ('r', 'rev', [], _('show the specified revision or range')),
2847 ('M', 'no-merges', None, _('do not show merges')),
2851 ('M', 'no-merges', None, _('do not show merges')),
2848 ('', 'style', '', _('display using template map file')),
2852 ('', 'style', '', _('display using template map file')),
2849 ('m', 'only-merges', None, _('show only merges')),
2853 ('m', 'only-merges', None, _('show only merges')),
2850 ('p', 'patch', None, _('show patch')),
2854 ('p', 'patch', None, _('show patch')),
2851 ('', 'template', '', _('display with template')),
2855 ('', 'template', '', _('display with template')),
2852 ('I', 'include', [], _('include names matching the given patterns')),
2856 ('I', 'include', [], _('include names matching the given patterns')),
2853 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2857 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2854 _('hg log [OPTION]... [FILE]')),
2858 _('hg log [OPTION]... [FILE]')),
2855 "manifest": (manifest, [], _('hg manifest [REV]')),
2859 "manifest": (manifest, [], _('hg manifest [REV]')),
2856 "merge":
2860 "merge":
2857 (merge,
2861 (merge,
2858 [('b', 'branch', '', _('merge with head of a specific branch')),
2862 [('b', 'branch', '', _('merge with head of a specific branch')),
2859 ('', 'style', '', _('display using template map file')),
2863 ('', 'style', '', _('display using template map file')),
2860 ('f', 'force', None, _('force a merge with outstanding changes')),
2864 ('f', 'force', None, _('force a merge with outstanding changes')),
2861 ('', 'template', '', _('display with template'))],
2865 ('', 'template', '', _('display with template'))],
2862 _('hg merge [-b TAG] [-f] [REV]')),
2866 _('hg merge [-b TAG] [-f] [REV]')),
2863 "outgoing|out": (outgoing,
2867 "outgoing|out": (outgoing,
2864 [('M', 'no-merges', None, _('do not show merges')),
2868 [('M', 'no-merges', None, _('do not show merges')),
2865 ('f', 'force', None,
2869 ('f', 'force', None,
2866 _('run even when remote repository is unrelated')),
2870 _('run even when remote repository is unrelated')),
2867 ('p', 'patch', None, _('show patch')),
2871 ('p', 'patch', None, _('show patch')),
2868 ('', 'style', '', _('display using template map file')),
2872 ('', 'style', '', _('display using template map file')),
2869 ('n', 'newest-first', None, _('show newest record first')),
2873 ('n', 'newest-first', None, _('show newest record first')),
2870 ('', 'template', '', _('display with template')),
2874 ('', 'template', '', _('display with template')),
2871 ('e', 'ssh', '', _('specify ssh command to use')),
2875 ('e', 'ssh', '', _('specify ssh command to use')),
2872 ('', 'remotecmd', '',
2876 ('', 'remotecmd', '',
2873 _('specify hg command to run on the remote side'))],
2877 _('specify hg command to run on the remote side'))],
2874 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2878 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2875 "^parents":
2879 "^parents":
2876 (parents,
2880 (parents,
2877 [('b', 'branches', None, _('show branches')),
2881 [('b', 'branches', None, _('show branches')),
2878 ('', 'style', '', _('display using template map file')),
2882 ('', 'style', '', _('display using template map file')),
2879 ('', 'template', '', _('display with template'))],
2883 ('', 'template', '', _('display with template'))],
2880 _('hg parents [-b] [REV]')),
2884 _('hg parents [-b] [REV]')),
2881 "paths": (paths, [], _('hg paths [NAME]')),
2885 "paths": (paths, [], _('hg paths [NAME]')),
2882 "^pull":
2886 "^pull":
2883 (pull,
2887 (pull,
2884 [('u', 'update', None,
2888 [('u', 'update', None,
2885 _('update the working directory to tip after pull')),
2889 _('update the working directory to tip after pull')),
2886 ('e', 'ssh', '', _('specify ssh command to use')),
2890 ('e', 'ssh', '', _('specify ssh command to use')),
2887 ('f', 'force', None,
2891 ('f', 'force', None,
2888 _('run even when remote repository is unrelated')),
2892 _('run even when remote repository is unrelated')),
2889 ('r', 'rev', [], _('a specific revision you would like to pull')),
2893 ('r', 'rev', [], _('a specific revision you would like to pull')),
2890 ('', 'remotecmd', '',
2894 ('', 'remotecmd', '',
2891 _('specify hg command to run on the remote side'))],
2895 _('specify hg command to run on the remote side'))],
2892 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
2896 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
2893 "^push":
2897 "^push":
2894 (push,
2898 (push,
2895 [('f', 'force', None, _('force push')),
2899 [('f', 'force', None, _('force push')),
2896 ('e', 'ssh', '', _('specify ssh command to use')),
2900 ('e', 'ssh', '', _('specify ssh command to use')),
2897 ('r', 'rev', [], _('a specific revision you would like to push')),
2901 ('r', 'rev', [], _('a specific revision you would like to push')),
2898 ('', 'remotecmd', '',
2902 ('', 'remotecmd', '',
2899 _('specify hg command to run on the remote side'))],
2903 _('specify hg command to run on the remote side'))],
2900 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
2904 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
2901 "debugrawcommit|rawcommit":
2905 "debugrawcommit|rawcommit":
2902 (rawcommit,
2906 (rawcommit,
2903 [('p', 'parent', [], _('parent')),
2907 [('p', 'parent', [], _('parent')),
2904 ('d', 'date', '', _('date code')),
2908 ('d', 'date', '', _('date code')),
2905 ('u', 'user', '', _('user')),
2909 ('u', 'user', '', _('user')),
2906 ('F', 'files', '', _('file list')),
2910 ('F', 'files', '', _('file list')),
2907 ('m', 'message', '', _('commit message')),
2911 ('m', 'message', '', _('commit message')),
2908 ('l', 'logfile', '', _('commit message file'))],
2912 ('l', 'logfile', '', _('commit message file'))],
2909 _('hg debugrawcommit [OPTION]... [FILE]...')),
2913 _('hg debugrawcommit [OPTION]... [FILE]...')),
2910 "recover": (recover, [], _('hg recover')),
2914 "recover": (recover, [], _('hg recover')),
2911 "^remove|rm":
2915 "^remove|rm":
2912 (remove,
2916 (remove,
2913 [('f', 'force', None, _('remove file even if modified')),
2917 [('f', 'force', None, _('remove file even if modified')),
2914 ('I', 'include', [], _('include names matching the given patterns')),
2918 ('I', 'include', [], _('include names matching the given patterns')),
2915 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2919 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2916 _('hg remove [OPTION]... FILE...')),
2920 _('hg remove [OPTION]... FILE...')),
2917 "rename|mv":
2921 "rename|mv":
2918 (rename,
2922 (rename,
2919 [('A', 'after', None, _('record a rename that has already occurred')),
2923 [('A', 'after', None, _('record a rename that has already occurred')),
2920 ('f', 'force', None,
2924 ('f', 'force', None,
2921 _('forcibly copy over an existing managed file')),
2925 _('forcibly copy over an existing managed file')),
2922 ('I', 'include', [], _('include names matching the given patterns')),
2926 ('I', 'include', [], _('include names matching the given patterns')),
2923 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2927 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2924 _('hg rename [OPTION]... SOURCE... DEST')),
2928 _('hg rename [OPTION]... SOURCE... DEST')),
2925 "^revert":
2929 "^revert":
2926 (revert,
2930 (revert,
2927 [('r', 'rev', '', _('revision to revert to')),
2931 [('r', 'rev', '', _('revision to revert to')),
2928 ('I', 'include', [], _('include names matching the given patterns')),
2932 ('I', 'include', [], _('include names matching the given patterns')),
2929 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2933 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2930 _('hg revert [-r REV] [NAME]...')),
2934 _('hg revert [-r REV] [NAME]...')),
2931 "root": (root, [], _('hg root')),
2935 "root": (root, [], _('hg root')),
2932 "^serve":
2936 "^serve":
2933 (serve,
2937 (serve,
2934 [('A', 'accesslog', '', _('name of access log file to write to')),
2938 [('A', 'accesslog', '', _('name of access log file to write to')),
2935 ('d', 'daemon', None, _('run server in background')),
2939 ('d', 'daemon', None, _('run server in background')),
2936 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2940 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2937 ('E', 'errorlog', '', _('name of error log file to write to')),
2941 ('E', 'errorlog', '', _('name of error log file to write to')),
2938 ('p', 'port', 0, _('port to use (default: 8000)')),
2942 ('p', 'port', 0, _('port to use (default: 8000)')),
2939 ('a', 'address', '', _('address to use')),
2943 ('a', 'address', '', _('address to use')),
2940 ('n', 'name', '',
2944 ('n', 'name', '',
2941 _('name to show in web pages (default: working dir)')),
2945 _('name to show in web pages (default: working dir)')),
2942 ('', 'pid-file', '', _('name of file to write process ID to')),
2946 ('', 'pid-file', '', _('name of file to write process ID to')),
2943 ('', 'stdio', None, _('for remote clients')),
2947 ('', 'stdio', None, _('for remote clients')),
2944 ('t', 'templates', '', _('web templates to use')),
2948 ('t', 'templates', '', _('web templates to use')),
2945 ('', 'style', '', _('template style to use')),
2949 ('', 'style', '', _('template style to use')),
2946 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2950 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2947 _('hg serve [OPTION]...')),
2951 _('hg serve [OPTION]...')),
2948 "^status|st":
2952 "^status|st":
2949 (status,
2953 (status,
2950 [('m', 'modified', None, _('show only modified files')),
2954 [('m', 'modified', None, _('show only modified files')),
2951 ('a', 'added', None, _('show only added files')),
2955 ('a', 'added', None, _('show only added files')),
2952 ('r', 'removed', None, _('show only removed files')),
2956 ('r', 'removed', None, _('show only removed files')),
2953 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2957 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2954 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2958 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2959 ('i', 'ignored', None, _('show ignored files')),
2955 ('n', 'no-status', None, _('hide status prefix')),
2960 ('n', 'no-status', None, _('hide status prefix')),
2956 ('0', 'print0', None,
2961 ('0', 'print0', None,
2957 _('end filenames with NUL, for use with xargs')),
2962 _('end filenames with NUL, for use with xargs')),
2958 ('I', 'include', [], _('include names matching the given patterns')),
2963 ('I', 'include', [], _('include names matching the given patterns')),
2959 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2964 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2960 _('hg status [OPTION]... [FILE]...')),
2965 _('hg status [OPTION]... [FILE]...')),
2961 "tag":
2966 "tag":
2962 (tag,
2967 (tag,
2963 [('l', 'local', None, _('make the tag local')),
2968 [('l', 'local', None, _('make the tag local')),
2964 ('m', 'message', '', _('message for tag commit log entry')),
2969 ('m', 'message', '', _('message for tag commit log entry')),
2965 ('d', 'date', '', _('record datecode as commit date')),
2970 ('d', 'date', '', _('record datecode as commit date')),
2966 ('u', 'user', '', _('record user as commiter')),
2971 ('u', 'user', '', _('record user as commiter')),
2967 ('r', 'rev', '', _('revision to tag'))],
2972 ('r', 'rev', '', _('revision to tag'))],
2968 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2973 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2969 "tags": (tags, [], _('hg tags')),
2974 "tags": (tags, [], _('hg tags')),
2970 "tip":
2975 "tip":
2971 (tip,
2976 (tip,
2972 [('b', 'branches', None, _('show branches')),
2977 [('b', 'branches', None, _('show branches')),
2973 ('', 'style', '', _('display using template map file')),
2978 ('', 'style', '', _('display using template map file')),
2974 ('p', 'patch', None, _('show patch')),
2979 ('p', 'patch', None, _('show patch')),
2975 ('', 'template', '', _('display with template'))],
2980 ('', 'template', '', _('display with template'))],
2976 _('hg tip [-b] [-p]')),
2981 _('hg tip [-b] [-p]')),
2977 "unbundle":
2982 "unbundle":
2978 (unbundle,
2983 (unbundle,
2979 [('u', 'update', None,
2984 [('u', 'update', None,
2980 _('update the working directory to tip after unbundle'))],
2985 _('update the working directory to tip after unbundle'))],
2981 _('hg unbundle [-u] FILE')),
2986 _('hg unbundle [-u] FILE')),
2982 "undo": (undo, [], _('hg undo')),
2987 "undo": (undo, [], _('hg undo')),
2983 "^update|up|checkout|co":
2988 "^update|up|checkout|co":
2984 (update,
2989 (update,
2985 [('b', 'branch', '', _('checkout the head of a specific branch')),
2990 [('b', 'branch', '', _('checkout the head of a specific branch')),
2986 ('', 'style', '', _('display using template map file')),
2991 ('', 'style', '', _('display using template map file')),
2987 ('m', 'merge', None, _('allow merging of branches')),
2992 ('m', 'merge', None, _('allow merging of branches')),
2988 ('C', 'clean', None, _('overwrite locally modified files')),
2993 ('C', 'clean', None, _('overwrite locally modified files')),
2989 ('f', 'force', None, _('force a merge with outstanding changes')),
2994 ('f', 'force', None, _('force a merge with outstanding changes')),
2990 ('', 'template', '', _('display with template'))],
2995 ('', 'template', '', _('display with template'))],
2991 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
2996 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
2992 "verify": (verify, [], _('hg verify')),
2997 "verify": (verify, [], _('hg verify')),
2993 "version": (show_version, [], _('hg version')),
2998 "version": (show_version, [], _('hg version')),
2994 }
2999 }
2995
3000
2996 globalopts = [
3001 globalopts = [
2997 ('R', 'repository', '',
3002 ('R', 'repository', '',
2998 _('repository root directory or symbolic path name')),
3003 _('repository root directory or symbolic path name')),
2999 ('', 'cwd', '', _('change working directory')),
3004 ('', 'cwd', '', _('change working directory')),
3000 ('y', 'noninteractive', None,
3005 ('y', 'noninteractive', None,
3001 _('do not prompt, assume \'yes\' for any required answers')),
3006 _('do not prompt, assume \'yes\' for any required answers')),
3002 ('q', 'quiet', None, _('suppress output')),
3007 ('q', 'quiet', None, _('suppress output')),
3003 ('v', 'verbose', None, _('enable additional output')),
3008 ('v', 'verbose', None, _('enable additional output')),
3004 ('', 'debug', None, _('enable debugging output')),
3009 ('', 'debug', None, _('enable debugging output')),
3005 ('', 'debugger', None, _('start debugger')),
3010 ('', 'debugger', None, _('start debugger')),
3006 ('', 'traceback', None, _('print traceback on exception')),
3011 ('', 'traceback', None, _('print traceback on exception')),
3007 ('', 'time', None, _('time how long the command takes')),
3012 ('', 'time', None, _('time how long the command takes')),
3008 ('', 'profile', None, _('print command execution profile')),
3013 ('', 'profile', None, _('print command execution profile')),
3009 ('', 'version', None, _('output version information and exit')),
3014 ('', 'version', None, _('output version information and exit')),
3010 ('h', 'help', None, _('display help and exit')),
3015 ('h', 'help', None, _('display help and exit')),
3011 ]
3016 ]
3012
3017
3013 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3018 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3014 " debugindex debugindexdot")
3019 " debugindex debugindexdot")
3015 optionalrepo = ("paths debugconfig")
3020 optionalrepo = ("paths debugconfig")
3016
3021
3017 def findpossible(cmd):
3022 def findpossible(cmd):
3018 """
3023 """
3019 Return cmd -> (aliases, command table entry)
3024 Return cmd -> (aliases, command table entry)
3020 for each matching command
3025 for each matching command
3021 """
3026 """
3022 choice = {}
3027 choice = {}
3023 debugchoice = {}
3028 debugchoice = {}
3024 for e in table.keys():
3029 for e in table.keys():
3025 aliases = e.lstrip("^").split("|")
3030 aliases = e.lstrip("^").split("|")
3026 if cmd in aliases:
3031 if cmd in aliases:
3027 choice[cmd] = (aliases, table[e])
3032 choice[cmd] = (aliases, table[e])
3028 continue
3033 continue
3029 for a in aliases:
3034 for a in aliases:
3030 if a.startswith(cmd):
3035 if a.startswith(cmd):
3031 if aliases[0].startswith("debug"):
3036 if aliases[0].startswith("debug"):
3032 debugchoice[a] = (aliases, table[e])
3037 debugchoice[a] = (aliases, table[e])
3033 else:
3038 else:
3034 choice[a] = (aliases, table[e])
3039 choice[a] = (aliases, table[e])
3035 break
3040 break
3036
3041
3037 if not choice and debugchoice:
3042 if not choice and debugchoice:
3038 choice = debugchoice
3043 choice = debugchoice
3039
3044
3040 return choice
3045 return choice
3041
3046
3042 def find(cmd):
3047 def find(cmd):
3043 """Return (aliases, command table entry) for command string."""
3048 """Return (aliases, command table entry) for command string."""
3044 choice = findpossible(cmd)
3049 choice = findpossible(cmd)
3045
3050
3046 if choice.has_key(cmd):
3051 if choice.has_key(cmd):
3047 return choice[cmd]
3052 return choice[cmd]
3048
3053
3049 if len(choice) > 1:
3054 if len(choice) > 1:
3050 clist = choice.keys()
3055 clist = choice.keys()
3051 clist.sort()
3056 clist.sort()
3052 raise AmbiguousCommand(cmd, clist)
3057 raise AmbiguousCommand(cmd, clist)
3053
3058
3054 if choice:
3059 if choice:
3055 return choice.values()[0]
3060 return choice.values()[0]
3056
3061
3057 raise UnknownCommand(cmd)
3062 raise UnknownCommand(cmd)
3058
3063
3059 class SignalInterrupt(Exception):
3064 class SignalInterrupt(Exception):
3060 """Exception raised on SIGTERM and SIGHUP."""
3065 """Exception raised on SIGTERM and SIGHUP."""
3061
3066
3062 def catchterm(*args):
3067 def catchterm(*args):
3063 raise SignalInterrupt
3068 raise SignalInterrupt
3064
3069
3065 def run():
3070 def run():
3066 sys.exit(dispatch(sys.argv[1:]))
3071 sys.exit(dispatch(sys.argv[1:]))
3067
3072
3068 class ParseError(Exception):
3073 class ParseError(Exception):
3069 """Exception raised on errors in parsing the command line."""
3074 """Exception raised on errors in parsing the command line."""
3070
3075
3071 def parse(ui, args):
3076 def parse(ui, args):
3072 options = {}
3077 options = {}
3073 cmdoptions = {}
3078 cmdoptions = {}
3074
3079
3075 try:
3080 try:
3076 args = fancyopts.fancyopts(args, globalopts, options)
3081 args = fancyopts.fancyopts(args, globalopts, options)
3077 except fancyopts.getopt.GetoptError, inst:
3082 except fancyopts.getopt.GetoptError, inst:
3078 raise ParseError(None, inst)
3083 raise ParseError(None, inst)
3079
3084
3080 if args:
3085 if args:
3081 cmd, args = args[0], args[1:]
3086 cmd, args = args[0], args[1:]
3082 aliases, i = find(cmd)
3087 aliases, i = find(cmd)
3083 cmd = aliases[0]
3088 cmd = aliases[0]
3084 defaults = ui.config("defaults", cmd)
3089 defaults = ui.config("defaults", cmd)
3085 if defaults:
3090 if defaults:
3086 args = defaults.split() + args
3091 args = defaults.split() + args
3087 c = list(i[1])
3092 c = list(i[1])
3088 else:
3093 else:
3089 cmd = None
3094 cmd = None
3090 c = []
3095 c = []
3091
3096
3092 # combine global options into local
3097 # combine global options into local
3093 for o in globalopts:
3098 for o in globalopts:
3094 c.append((o[0], o[1], options[o[1]], o[3]))
3099 c.append((o[0], o[1], options[o[1]], o[3]))
3095
3100
3096 try:
3101 try:
3097 args = fancyopts.fancyopts(args, c, cmdoptions)
3102 args = fancyopts.fancyopts(args, c, cmdoptions)
3098 except fancyopts.getopt.GetoptError, inst:
3103 except fancyopts.getopt.GetoptError, inst:
3099 raise ParseError(cmd, inst)
3104 raise ParseError(cmd, inst)
3100
3105
3101 # separate global options back out
3106 # separate global options back out
3102 for o in globalopts:
3107 for o in globalopts:
3103 n = o[1]
3108 n = o[1]
3104 options[n] = cmdoptions[n]
3109 options[n] = cmdoptions[n]
3105 del cmdoptions[n]
3110 del cmdoptions[n]
3106
3111
3107 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3112 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3108
3113
3109 def dispatch(args):
3114 def dispatch(args):
3110 signal.signal(signal.SIGTERM, catchterm)
3115 signal.signal(signal.SIGTERM, catchterm)
3111 try:
3116 try:
3112 signal.signal(signal.SIGHUP, catchterm)
3117 signal.signal(signal.SIGHUP, catchterm)
3113 except AttributeError:
3118 except AttributeError:
3114 pass
3119 pass
3115
3120
3116 try:
3121 try:
3117 u = ui.ui()
3122 u = ui.ui()
3118 except util.Abort, inst:
3123 except util.Abort, inst:
3119 sys.stderr.write(_("abort: %s\n") % inst)
3124 sys.stderr.write(_("abort: %s\n") % inst)
3120 sys.exit(1)
3125 sys.exit(1)
3121
3126
3122 external = []
3127 external = []
3123 for x in u.extensions():
3128 for x in u.extensions():
3124 def on_exception(exc, inst):
3129 def on_exception(exc, inst):
3125 u.warn(_("*** failed to import extension %s\n") % x[1])
3130 u.warn(_("*** failed to import extension %s\n") % x[1])
3126 u.warn("%s\n" % inst)
3131 u.warn("%s\n" % inst)
3127 if "--traceback" in sys.argv[1:]:
3132 if "--traceback" in sys.argv[1:]:
3128 traceback.print_exc()
3133 traceback.print_exc()
3129 if x[1]:
3134 if x[1]:
3130 try:
3135 try:
3131 mod = imp.load_source(x[0], x[1])
3136 mod = imp.load_source(x[0], x[1])
3132 except Exception, inst:
3137 except Exception, inst:
3133 on_exception(Exception, inst)
3138 on_exception(Exception, inst)
3134 continue
3139 continue
3135 else:
3140 else:
3136 def importh(name):
3141 def importh(name):
3137 mod = __import__(name)
3142 mod = __import__(name)
3138 components = name.split('.')
3143 components = name.split('.')
3139 for comp in components[1:]:
3144 for comp in components[1:]:
3140 mod = getattr(mod, comp)
3145 mod = getattr(mod, comp)
3141 return mod
3146 return mod
3142 try:
3147 try:
3143 try:
3148 try:
3144 mod = importh("hgext." + x[0])
3149 mod = importh("hgext." + x[0])
3145 except ImportError:
3150 except ImportError:
3146 mod = importh(x[0])
3151 mod = importh(x[0])
3147 except Exception, inst:
3152 except Exception, inst:
3148 on_exception(Exception, inst)
3153 on_exception(Exception, inst)
3149 continue
3154 continue
3150
3155
3151 external.append(mod)
3156 external.append(mod)
3152 for x in external:
3157 for x in external:
3153 cmdtable = getattr(x, 'cmdtable', {})
3158 cmdtable = getattr(x, 'cmdtable', {})
3154 for t in cmdtable:
3159 for t in cmdtable:
3155 if t in table:
3160 if t in table:
3156 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3161 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3157 table.update(cmdtable)
3162 table.update(cmdtable)
3158
3163
3159 try:
3164 try:
3160 cmd, func, args, options, cmdoptions = parse(u, args)
3165 cmd, func, args, options, cmdoptions = parse(u, args)
3161 if options["time"]:
3166 if options["time"]:
3162 def get_times():
3167 def get_times():
3163 t = os.times()
3168 t = os.times()
3164 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3169 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3165 t = (t[0], t[1], t[2], t[3], time.clock())
3170 t = (t[0], t[1], t[2], t[3], time.clock())
3166 return t
3171 return t
3167 s = get_times()
3172 s = get_times()
3168 def print_time():
3173 def print_time():
3169 t = get_times()
3174 t = get_times()
3170 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3175 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3171 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3176 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3172 atexit.register(print_time)
3177 atexit.register(print_time)
3173
3178
3174 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3179 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3175 not options["noninteractive"])
3180 not options["noninteractive"])
3176
3181
3177 # enter the debugger before command execution
3182 # enter the debugger before command execution
3178 if options['debugger']:
3183 if options['debugger']:
3179 pdb.set_trace()
3184 pdb.set_trace()
3180
3185
3181 try:
3186 try:
3182 if options['cwd']:
3187 if options['cwd']:
3183 try:
3188 try:
3184 os.chdir(options['cwd'])
3189 os.chdir(options['cwd'])
3185 except OSError, inst:
3190 except OSError, inst:
3186 raise util.Abort('%s: %s' %
3191 raise util.Abort('%s: %s' %
3187 (options['cwd'], inst.strerror))
3192 (options['cwd'], inst.strerror))
3188
3193
3189 path = u.expandpath(options["repository"]) or ""
3194 path = u.expandpath(options["repository"]) or ""
3190 repo = path and hg.repository(u, path=path) or None
3195 repo = path and hg.repository(u, path=path) or None
3191
3196
3192 if options['help']:
3197 if options['help']:
3193 help_(u, cmd, options['version'])
3198 help_(u, cmd, options['version'])
3194 sys.exit(0)
3199 sys.exit(0)
3195 elif options['version']:
3200 elif options['version']:
3196 show_version(u)
3201 show_version(u)
3197 sys.exit(0)
3202 sys.exit(0)
3198 elif not cmd:
3203 elif not cmd:
3199 help_(u, 'shortlist')
3204 help_(u, 'shortlist')
3200 sys.exit(0)
3205 sys.exit(0)
3201
3206
3202 if cmd not in norepo.split():
3207 if cmd not in norepo.split():
3203 try:
3208 try:
3204 if not repo:
3209 if not repo:
3205 repo = hg.repository(u, path=path)
3210 repo = hg.repository(u, path=path)
3206 u = repo.ui
3211 u = repo.ui
3207 for x in external:
3212 for x in external:
3208 if hasattr(x, 'reposetup'):
3213 if hasattr(x, 'reposetup'):
3209 x.reposetup(u, repo)
3214 x.reposetup(u, repo)
3210 except hg.RepoError:
3215 except hg.RepoError:
3211 if cmd not in optionalrepo.split():
3216 if cmd not in optionalrepo.split():
3212 raise
3217 raise
3213 d = lambda: func(u, repo, *args, **cmdoptions)
3218 d = lambda: func(u, repo, *args, **cmdoptions)
3214 else:
3219 else:
3215 d = lambda: func(u, *args, **cmdoptions)
3220 d = lambda: func(u, *args, **cmdoptions)
3216
3221
3217 try:
3222 try:
3218 if options['profile']:
3223 if options['profile']:
3219 import hotshot, hotshot.stats
3224 import hotshot, hotshot.stats
3220 prof = hotshot.Profile("hg.prof")
3225 prof = hotshot.Profile("hg.prof")
3221 try:
3226 try:
3222 try:
3227 try:
3223 return prof.runcall(d)
3228 return prof.runcall(d)
3224 except:
3229 except:
3225 try:
3230 try:
3226 u.warn(_('exception raised - generating '
3231 u.warn(_('exception raised - generating '
3227 'profile anyway\n'))
3232 'profile anyway\n'))
3228 except:
3233 except:
3229 pass
3234 pass
3230 raise
3235 raise
3231 finally:
3236 finally:
3232 prof.close()
3237 prof.close()
3233 stats = hotshot.stats.load("hg.prof")
3238 stats = hotshot.stats.load("hg.prof")
3234 stats.strip_dirs()
3239 stats.strip_dirs()
3235 stats.sort_stats('time', 'calls')
3240 stats.sort_stats('time', 'calls')
3236 stats.print_stats(40)
3241 stats.print_stats(40)
3237 else:
3242 else:
3238 return d()
3243 return d()
3239 finally:
3244 finally:
3240 u.flush()
3245 u.flush()
3241 except:
3246 except:
3242 # enter the debugger when we hit an exception
3247 # enter the debugger when we hit an exception
3243 if options['debugger']:
3248 if options['debugger']:
3244 pdb.post_mortem(sys.exc_info()[2])
3249 pdb.post_mortem(sys.exc_info()[2])
3245 if options['traceback']:
3250 if options['traceback']:
3246 traceback.print_exc()
3251 traceback.print_exc()
3247 raise
3252 raise
3248 except ParseError, inst:
3253 except ParseError, inst:
3249 if inst.args[0]:
3254 if inst.args[0]:
3250 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3255 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3251 help_(u, inst.args[0])
3256 help_(u, inst.args[0])
3252 else:
3257 else:
3253 u.warn(_("hg: %s\n") % inst.args[1])
3258 u.warn(_("hg: %s\n") % inst.args[1])
3254 help_(u, 'shortlist')
3259 help_(u, 'shortlist')
3255 sys.exit(-1)
3260 sys.exit(-1)
3256 except AmbiguousCommand, inst:
3261 except AmbiguousCommand, inst:
3257 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3262 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3258 (inst.args[0], " ".join(inst.args[1])))
3263 (inst.args[0], " ".join(inst.args[1])))
3259 sys.exit(1)
3264 sys.exit(1)
3260 except UnknownCommand, inst:
3265 except UnknownCommand, inst:
3261 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3266 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3262 help_(u, 'shortlist')
3267 help_(u, 'shortlist')
3263 sys.exit(1)
3268 sys.exit(1)
3264 except hg.RepoError, inst:
3269 except hg.RepoError, inst:
3265 u.warn(_("abort: "), inst, "!\n")
3270 u.warn(_("abort: "), inst, "!\n")
3266 except lock.LockHeld, inst:
3271 except lock.LockHeld, inst:
3267 if inst.errno == errno.ETIMEDOUT:
3272 if inst.errno == errno.ETIMEDOUT:
3268 reason = _('timed out waiting for lock held by %s') % inst.locker
3273 reason = _('timed out waiting for lock held by %s') % inst.locker
3269 else:
3274 else:
3270 reason = _('lock held by %s') % inst.locker
3275 reason = _('lock held by %s') % inst.locker
3271 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3276 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3272 except lock.LockUnavailable, inst:
3277 except lock.LockUnavailable, inst:
3273 u.warn(_("abort: could not lock %s: %s\n") %
3278 u.warn(_("abort: could not lock %s: %s\n") %
3274 (inst.desc or inst.filename, inst.strerror))
3279 (inst.desc or inst.filename, inst.strerror))
3275 except revlog.RevlogError, inst:
3280 except revlog.RevlogError, inst:
3276 u.warn(_("abort: "), inst, "!\n")
3281 u.warn(_("abort: "), inst, "!\n")
3277 except SignalInterrupt:
3282 except SignalInterrupt:
3278 u.warn(_("killed!\n"))
3283 u.warn(_("killed!\n"))
3279 except KeyboardInterrupt:
3284 except KeyboardInterrupt:
3280 try:
3285 try:
3281 u.warn(_("interrupted!\n"))
3286 u.warn(_("interrupted!\n"))
3282 except IOError, inst:
3287 except IOError, inst:
3283 if inst.errno == errno.EPIPE:
3288 if inst.errno == errno.EPIPE:
3284 if u.debugflag:
3289 if u.debugflag:
3285 u.warn(_("\nbroken pipe\n"))
3290 u.warn(_("\nbroken pipe\n"))
3286 else:
3291 else:
3287 raise
3292 raise
3288 except IOError, inst:
3293 except IOError, inst:
3289 if hasattr(inst, "code"):
3294 if hasattr(inst, "code"):
3290 u.warn(_("abort: %s\n") % inst)
3295 u.warn(_("abort: %s\n") % inst)
3291 elif hasattr(inst, "reason"):
3296 elif hasattr(inst, "reason"):
3292 u.warn(_("abort: error: %s\n") % inst.reason[1])
3297 u.warn(_("abort: error: %s\n") % inst.reason[1])
3293 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3298 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3294 if u.debugflag:
3299 if u.debugflag:
3295 u.warn(_("broken pipe\n"))
3300 u.warn(_("broken pipe\n"))
3296 elif getattr(inst, "strerror", None):
3301 elif getattr(inst, "strerror", None):
3297 if getattr(inst, "filename", None):
3302 if getattr(inst, "filename", None):
3298 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3303 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3299 else:
3304 else:
3300 u.warn(_("abort: %s\n") % inst.strerror)
3305 u.warn(_("abort: %s\n") % inst.strerror)
3301 else:
3306 else:
3302 raise
3307 raise
3303 except OSError, inst:
3308 except OSError, inst:
3304 if hasattr(inst, "filename"):
3309 if hasattr(inst, "filename"):
3305 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3310 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3306 else:
3311 else:
3307 u.warn(_("abort: %s\n") % inst.strerror)
3312 u.warn(_("abort: %s\n") % inst.strerror)
3308 except util.Abort, inst:
3313 except util.Abort, inst:
3309 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3314 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3310 sys.exit(1)
3315 sys.exit(1)
3311 except TypeError, inst:
3316 except TypeError, inst:
3312 # was this an argument error?
3317 # was this an argument error?
3313 tb = traceback.extract_tb(sys.exc_info()[2])
3318 tb = traceback.extract_tb(sys.exc_info()[2])
3314 if len(tb) > 2: # no
3319 if len(tb) > 2: # no
3315 raise
3320 raise
3316 u.debug(inst, "\n")
3321 u.debug(inst, "\n")
3317 u.warn(_("%s: invalid arguments\n") % cmd)
3322 u.warn(_("%s: invalid arguments\n") % cmd)
3318 help_(u, cmd)
3323 help_(u, cmd)
3319 except SystemExit:
3324 except SystemExit:
3320 # don't catch this in the catch-all below
3325 # don't catch this in the catch-all below
3321 raise
3326 raise
3322 except:
3327 except:
3323 u.warn(_("** unknown exception encountered, details follow\n"))
3328 u.warn(_("** unknown exception encountered, details follow\n"))
3324 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3329 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3325 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3330 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3326 % version.get_version())
3331 % version.get_version())
3327 raise
3332 raise
3328
3333
3329 sys.exit(-1)
3334 sys.exit(-1)
@@ -1,456 +1,459
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005 Matt Mackall <mpm@selenic.com>
4 Copyright 2005 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 import struct, os
10 import struct, os
11 from node import *
11 from node import *
12 from i18n import gettext as _
12 from i18n import gettext as _
13 from demandload import *
13 from demandload import *
14 demandload(globals(), "time bisect stat util re errno")
14 demandload(globals(), "time bisect stat util re errno")
15
15
16 class dirstate(object):
16 class dirstate(object):
17 def __init__(self, opener, ui, root):
17 def __init__(self, opener, ui, root):
18 self.opener = opener
18 self.opener = opener
19 self.root = root
19 self.root = root
20 self.dirty = 0
20 self.dirty = 0
21 self.ui = ui
21 self.ui = ui
22 self.map = None
22 self.map = None
23 self.pl = None
23 self.pl = None
24 self.copies = {}
24 self.copies = {}
25 self.ignorefunc = None
25 self.ignorefunc = None
26 self.blockignore = False
26 self.blockignore = False
27
27
28 def wjoin(self, f):
28 def wjoin(self, f):
29 return os.path.join(self.root, f)
29 return os.path.join(self.root, f)
30
30
31 def getcwd(self):
31 def getcwd(self):
32 cwd = os.getcwd()
32 cwd = os.getcwd()
33 if cwd == self.root: return ''
33 if cwd == self.root: return ''
34 return cwd[len(self.root) + 1:]
34 return cwd[len(self.root) + 1:]
35
35
36 def hgignore(self):
36 def hgignore(self):
37 '''return the contents of .hgignore files as a list of patterns.
37 '''return the contents of .hgignore files as a list of patterns.
38
38
39 the files parsed for patterns include:
39 the files parsed for patterns include:
40 .hgignore in the repository root
40 .hgignore in the repository root
41 any additional files specified in the [ui] section of ~/.hgrc
41 any additional files specified in the [ui] section of ~/.hgrc
42
42
43 trailing white space is dropped.
43 trailing white space is dropped.
44 the escape character is backslash.
44 the escape character is backslash.
45 comments start with #.
45 comments start with #.
46 empty lines are skipped.
46 empty lines are skipped.
47
47
48 lines can be of the following formats:
48 lines can be of the following formats:
49
49
50 syntax: regexp # defaults following lines to non-rooted regexps
50 syntax: regexp # defaults following lines to non-rooted regexps
51 syntax: glob # defaults following lines to non-rooted globs
51 syntax: glob # defaults following lines to non-rooted globs
52 re:pattern # non-rooted regular expression
52 re:pattern # non-rooted regular expression
53 glob:pattern # non-rooted glob
53 glob:pattern # non-rooted glob
54 pattern # pattern of the current default type'''
54 pattern # pattern of the current default type'''
55 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
55 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
56 def parselines(fp):
56 def parselines(fp):
57 for line in fp:
57 for line in fp:
58 escape = False
58 escape = False
59 for i in xrange(len(line)):
59 for i in xrange(len(line)):
60 if escape: escape = False
60 if escape: escape = False
61 elif line[i] == '\\': escape = True
61 elif line[i] == '\\': escape = True
62 elif line[i] == '#': break
62 elif line[i] == '#': break
63 line = line[:i].rstrip()
63 line = line[:i].rstrip()
64 if line: yield line
64 if line: yield line
65 repoignore = self.wjoin('.hgignore')
65 repoignore = self.wjoin('.hgignore')
66 files = [repoignore]
66 files = [repoignore]
67 files.extend(self.ui.hgignorefiles())
67 files.extend(self.ui.hgignorefiles())
68 pats = {}
68 pats = {}
69 for f in files:
69 for f in files:
70 try:
70 try:
71 pats[f] = []
71 pats[f] = []
72 fp = open(f)
72 fp = open(f)
73 syntax = 'relre:'
73 syntax = 'relre:'
74 for line in parselines(fp):
74 for line in parselines(fp):
75 if line.startswith('syntax:'):
75 if line.startswith('syntax:'):
76 s = line[7:].strip()
76 s = line[7:].strip()
77 try:
77 try:
78 syntax = syntaxes[s]
78 syntax = syntaxes[s]
79 except KeyError:
79 except KeyError:
80 self.ui.warn(_("%s: ignoring invalid "
80 self.ui.warn(_("%s: ignoring invalid "
81 "syntax '%s'\n") % (f, s))
81 "syntax '%s'\n") % (f, s))
82 continue
82 continue
83 pat = syntax + line
83 pat = syntax + line
84 for s in syntaxes.values():
84 for s in syntaxes.values():
85 if line.startswith(s):
85 if line.startswith(s):
86 pat = line
86 pat = line
87 break
87 break
88 pats[f].append(pat)
88 pats[f].append(pat)
89 except IOError, inst:
89 except IOError, inst:
90 if f != repoignore:
90 if f != repoignore:
91 self.ui.warn(_("skipping unreadable ignore file"
91 self.ui.warn(_("skipping unreadable ignore file"
92 " '%s': %s\n") % (f, inst.strerror))
92 " '%s': %s\n") % (f, inst.strerror))
93 return pats
93 return pats
94
94
95 def ignore(self, fn):
95 def ignore(self, fn):
96 '''default match function used by dirstate and
96 '''default match function used by dirstate and
97 localrepository. this honours the repository .hgignore file
97 localrepository. this honours the repository .hgignore file
98 and any other files specified in the [ui] section of .hgrc.'''
98 and any other files specified in the [ui] section of .hgrc.'''
99 if self.blockignore:
99 if self.blockignore:
100 return False
100 return False
101 if not self.ignorefunc:
101 if not self.ignorefunc:
102 ignore = self.hgignore()
102 ignore = self.hgignore()
103 allpats = []
103 allpats = []
104 [allpats.extend(patlist) for patlist in ignore.values()]
104 [allpats.extend(patlist) for patlist in ignore.values()]
105 if allpats:
105 if allpats:
106 try:
106 try:
107 files, self.ignorefunc, anypats = (
107 files, self.ignorefunc, anypats = (
108 util.matcher(self.root, inc=allpats, src='.hgignore'))
108 util.matcher(self.root, inc=allpats, src='.hgignore'))
109 except util.Abort:
109 except util.Abort:
110 # Re-raise an exception where the src is the right file
110 # Re-raise an exception where the src is the right file
111 for f, patlist in ignore.items():
111 for f, patlist in ignore.items():
112 files, self.ignorefunc, anypats = (
112 files, self.ignorefunc, anypats = (
113 util.matcher(self.root, inc=patlist, src=f))
113 util.matcher(self.root, inc=patlist, src=f))
114 else:
114 else:
115 self.ignorefunc = util.never
115 self.ignorefunc = util.never
116 return self.ignorefunc(fn)
116 return self.ignorefunc(fn)
117
117
118 def __del__(self):
118 def __del__(self):
119 if self.dirty:
119 if self.dirty:
120 self.write()
120 self.write()
121
121
122 def __getitem__(self, key):
122 def __getitem__(self, key):
123 try:
123 try:
124 return self.map[key]
124 return self.map[key]
125 except TypeError:
125 except TypeError:
126 self.lazyread()
126 self.lazyread()
127 return self[key]
127 return self[key]
128
128
129 def __contains__(self, key):
129 def __contains__(self, key):
130 self.lazyread()
130 self.lazyread()
131 return key in self.map
131 return key in self.map
132
132
133 def parents(self):
133 def parents(self):
134 self.lazyread()
134 self.lazyread()
135 return self.pl
135 return self.pl
136
136
137 def markdirty(self):
137 def markdirty(self):
138 if not self.dirty:
138 if not self.dirty:
139 self.dirty = 1
139 self.dirty = 1
140
140
141 def setparents(self, p1, p2=nullid):
141 def setparents(self, p1, p2=nullid):
142 self.lazyread()
142 self.lazyread()
143 self.markdirty()
143 self.markdirty()
144 self.pl = p1, p2
144 self.pl = p1, p2
145
145
146 def state(self, key):
146 def state(self, key):
147 try:
147 try:
148 return self[key][0]
148 return self[key][0]
149 except KeyError:
149 except KeyError:
150 return "?"
150 return "?"
151
151
152 def lazyread(self):
152 def lazyread(self):
153 if self.map is None:
153 if self.map is None:
154 self.read()
154 self.read()
155
155
156 def read(self):
156 def read(self):
157 self.map = {}
157 self.map = {}
158 self.pl = [nullid, nullid]
158 self.pl = [nullid, nullid]
159 try:
159 try:
160 st = self.opener("dirstate").read()
160 st = self.opener("dirstate").read()
161 if not st: return
161 if not st: return
162 except: return
162 except: return
163
163
164 self.pl = [st[:20], st[20: 40]]
164 self.pl = [st[:20], st[20: 40]]
165
165
166 pos = 40
166 pos = 40
167 while pos < len(st):
167 while pos < len(st):
168 e = struct.unpack(">cllll", st[pos:pos+17])
168 e = struct.unpack(">cllll", st[pos:pos+17])
169 l = e[4]
169 l = e[4]
170 pos += 17
170 pos += 17
171 f = st[pos:pos + l]
171 f = st[pos:pos + l]
172 if '\0' in f:
172 if '\0' in f:
173 f, c = f.split('\0')
173 f, c = f.split('\0')
174 self.copies[f] = c
174 self.copies[f] = c
175 self.map[f] = e[:4]
175 self.map[f] = e[:4]
176 pos += l
176 pos += l
177
177
178 def copy(self, source, dest):
178 def copy(self, source, dest):
179 self.lazyread()
179 self.lazyread()
180 self.markdirty()
180 self.markdirty()
181 self.copies[dest] = source
181 self.copies[dest] = source
182
182
183 def copied(self, file):
183 def copied(self, file):
184 return self.copies.get(file, None)
184 return self.copies.get(file, None)
185
185
186 def update(self, files, state, **kw):
186 def update(self, files, state, **kw):
187 ''' current states:
187 ''' current states:
188 n normal
188 n normal
189 m needs merging
189 m needs merging
190 r marked for removal
190 r marked for removal
191 a marked for addition'''
191 a marked for addition'''
192
192
193 if not files: return
193 if not files: return
194 self.lazyread()
194 self.lazyread()
195 self.markdirty()
195 self.markdirty()
196 for f in files:
196 for f in files:
197 if state == "r":
197 if state == "r":
198 self.map[f] = ('r', 0, 0, 0)
198 self.map[f] = ('r', 0, 0, 0)
199 else:
199 else:
200 s = os.lstat(self.wjoin(f))
200 s = os.lstat(self.wjoin(f))
201 st_size = kw.get('st_size', s.st_size)
201 st_size = kw.get('st_size', s.st_size)
202 st_mtime = kw.get('st_mtime', s.st_mtime)
202 st_mtime = kw.get('st_mtime', s.st_mtime)
203 self.map[f] = (state, s.st_mode, st_size, st_mtime)
203 self.map[f] = (state, s.st_mode, st_size, st_mtime)
204 if self.copies.has_key(f):
204 if self.copies.has_key(f):
205 del self.copies[f]
205 del self.copies[f]
206
206
207 def forget(self, files):
207 def forget(self, files):
208 if not files: return
208 if not files: return
209 self.lazyread()
209 self.lazyread()
210 self.markdirty()
210 self.markdirty()
211 for f in files:
211 for f in files:
212 try:
212 try:
213 del self.map[f]
213 del self.map[f]
214 except KeyError:
214 except KeyError:
215 self.ui.warn(_("not in dirstate: %s!\n") % f)
215 self.ui.warn(_("not in dirstate: %s!\n") % f)
216 pass
216 pass
217
217
218 def clear(self):
218 def clear(self):
219 self.map = {}
219 self.map = {}
220 self.copies = {}
220 self.copies = {}
221 self.markdirty()
221 self.markdirty()
222
222
223 def rebuild(self, parent, files):
223 def rebuild(self, parent, files):
224 self.clear()
224 self.clear()
225 umask = os.umask(0)
225 umask = os.umask(0)
226 os.umask(umask)
226 os.umask(umask)
227 for f, mode in files:
227 for f, mode in files:
228 if mode:
228 if mode:
229 self.map[f] = ('n', ~umask, -1, 0)
229 self.map[f] = ('n', ~umask, -1, 0)
230 else:
230 else:
231 self.map[f] = ('n', ~umask & 0666, -1, 0)
231 self.map[f] = ('n', ~umask & 0666, -1, 0)
232 self.pl = (parent, nullid)
232 self.pl = (parent, nullid)
233 self.markdirty()
233 self.markdirty()
234
234
235 def write(self):
235 def write(self):
236 if not self.dirty:
236 if not self.dirty:
237 return
237 return
238 st = self.opener("dirstate", "w", atomic=True)
238 st = self.opener("dirstate", "w", atomic=True)
239 st.write("".join(self.pl))
239 st.write("".join(self.pl))
240 for f, e in self.map.items():
240 for f, e in self.map.items():
241 c = self.copied(f)
241 c = self.copied(f)
242 if c:
242 if c:
243 f = f + "\0" + c
243 f = f + "\0" + c
244 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
244 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
245 st.write(e + f)
245 st.write(e + f)
246 self.dirty = 0
246 self.dirty = 0
247
247
248 def filterfiles(self, files):
248 def filterfiles(self, files):
249 ret = {}
249 ret = {}
250 unknown = []
250 unknown = []
251
251
252 for x in files:
252 for x in files:
253 if x == '.':
253 if x == '.':
254 return self.map.copy()
254 return self.map.copy()
255 if x not in self.map:
255 if x not in self.map:
256 unknown.append(x)
256 unknown.append(x)
257 else:
257 else:
258 ret[x] = self.map[x]
258 ret[x] = self.map[x]
259
259
260 if not unknown:
260 if not unknown:
261 return ret
261 return ret
262
262
263 b = self.map.keys()
263 b = self.map.keys()
264 b.sort()
264 b.sort()
265 blen = len(b)
265 blen = len(b)
266
266
267 for x in unknown:
267 for x in unknown:
268 bs = bisect.bisect(b, x)
268 bs = bisect.bisect(b, x)
269 if bs != 0 and b[bs-1] == x:
269 if bs != 0 and b[bs-1] == x:
270 ret[x] = self.map[x]
270 ret[x] = self.map[x]
271 continue
271 continue
272 while bs < blen:
272 while bs < blen:
273 s = b[bs]
273 s = b[bs]
274 if len(s) > len(x) and s.startswith(x) and s[len(x)] == '/':
274 if len(s) > len(x) and s.startswith(x) and s[len(x)] == '/':
275 ret[s] = self.map[s]
275 ret[s] = self.map[s]
276 else:
276 else:
277 break
277 break
278 bs += 1
278 bs += 1
279 return ret
279 return ret
280
280
281 def supported_type(self, f, st, verbose=False):
281 def supported_type(self, f, st, verbose=False):
282 if stat.S_ISREG(st.st_mode):
282 if stat.S_ISREG(st.st_mode):
283 return True
283 return True
284 if verbose:
284 if verbose:
285 kind = 'unknown'
285 kind = 'unknown'
286 if stat.S_ISCHR(st.st_mode): kind = _('character device')
286 if stat.S_ISCHR(st.st_mode): kind = _('character device')
287 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
287 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
288 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
288 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
289 elif stat.S_ISLNK(st.st_mode): kind = _('symbolic link')
289 elif stat.S_ISLNK(st.st_mode): kind = _('symbolic link')
290 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
290 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
291 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
291 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
292 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
292 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
293 util.pathto(self.getcwd(), f),
293 util.pathto(self.getcwd(), f),
294 kind))
294 kind))
295 return False
295 return False
296
296
297 def statwalk(self, files=None, match=util.always, dc=None):
297 def statwalk(self, files=None, match=util.always, dc=None, ignored=False):
298 self.lazyread()
298 self.lazyread()
299
299
300 # walk all files by default
300 # walk all files by default
301 if not files:
301 if not files:
302 files = [self.root]
302 files = [self.root]
303 if not dc:
303 if not dc:
304 dc = self.map.copy()
304 dc = self.map.copy()
305 elif not dc:
305 elif not dc:
306 dc = self.filterfiles(files)
306 dc = self.filterfiles(files)
307
307
308 def statmatch(file_, stat):
308 def statmatch(file_, stat):
309 file_ = util.pconvert(file_)
309 file_ = util.pconvert(file_)
310 if file_ not in dc and self.ignore(file_):
310 if not ignored and file_ not in dc and self.ignore(file_):
311 return False
311 return False
312 return match(file_)
312 return match(file_)
313
313
314 return self.walkhelper(files=files, statmatch=statmatch, dc=dc)
314 return self.walkhelper(files=files, statmatch=statmatch, dc=dc)
315
315
316 def walk(self, files=None, match=util.always, dc=None):
316 def walk(self, files=None, match=util.always, dc=None):
317 # filter out the stat
317 # filter out the stat
318 for src, f, st in self.statwalk(files, match, dc):
318 for src, f, st in self.statwalk(files, match, dc):
319 yield src, f
319 yield src, f
320
320
321 # walk recursively through the directory tree, finding all files
321 # walk recursively through the directory tree, finding all files
322 # matched by the statmatch function
322 # matched by the statmatch function
323 #
323 #
324 # results are yielded in a tuple (src, filename, st), where src
324 # results are yielded in a tuple (src, filename, st), where src
325 # is one of:
325 # is one of:
326 # 'f' the file was found in the directory tree
326 # 'f' the file was found in the directory tree
327 # 'm' the file was only in the dirstate and not in the tree
327 # 'm' the file was only in the dirstate and not in the tree
328 # and st is the stat result if the file was found in the directory.
328 # and st is the stat result if the file was found in the directory.
329 #
329 #
330 # dc is an optional arg for the current dirstate. dc is not modified
330 # dc is an optional arg for the current dirstate. dc is not modified
331 # directly by this function, but might be modified by your statmatch call.
331 # directly by this function, but might be modified by your statmatch call.
332 #
332 #
333 def walkhelper(self, files, statmatch, dc):
333 def walkhelper(self, files, statmatch, dc):
334 # recursion free walker, faster than os.walk.
334 # recursion free walker, faster than os.walk.
335 def findfiles(s):
335 def findfiles(s):
336 work = [s]
336 work = [s]
337 while work:
337 while work:
338 top = work.pop()
338 top = work.pop()
339 names = os.listdir(top)
339 names = os.listdir(top)
340 names.sort()
340 names.sort()
341 # nd is the top of the repository dir tree
341 # nd is the top of the repository dir tree
342 nd = util.normpath(top[len(self.root) + 1:])
342 nd = util.normpath(top[len(self.root) + 1:])
343 if nd == '.': nd = ''
343 if nd == '.': nd = ''
344 for f in names:
344 for f in names:
345 np = util.pconvert(os.path.join(nd, f))
345 np = util.pconvert(os.path.join(nd, f))
346 if seen(np):
346 if seen(np):
347 continue
347 continue
348 p = os.path.join(top, f)
348 p = os.path.join(top, f)
349 # don't trip over symlinks
349 # don't trip over symlinks
350 st = os.lstat(p)
350 st = os.lstat(p)
351 if stat.S_ISDIR(st.st_mode):
351 if stat.S_ISDIR(st.st_mode):
352 ds = os.path.join(nd, f +'/')
352 ds = os.path.join(nd, f +'/')
353 if statmatch(ds, st):
353 if statmatch(ds, st):
354 work.append(p)
354 work.append(p)
355 if statmatch(np, st) and np in dc:
355 if statmatch(np, st) and np in dc:
356 yield 'm', np, st
356 yield 'm', np, st
357 elif statmatch(np, st):
357 elif statmatch(np, st):
358 if self.supported_type(np, st):
358 if self.supported_type(np, st):
359 yield 'f', np, st
359 yield 'f', np, st
360 elif np in dc:
360 elif np in dc:
361 yield 'm', np, st
361 yield 'm', np, st
362
362
363 known = {'.hg': 1}
363 known = {'.hg': 1}
364 def seen(fn):
364 def seen(fn):
365 if fn in known: return True
365 if fn in known: return True
366 known[fn] = 1
366 known[fn] = 1
367
367
368 # step one, find all files that match our criteria
368 # step one, find all files that match our criteria
369 files.sort()
369 files.sort()
370 for ff in util.unique(files):
370 for ff in util.unique(files):
371 f = self.wjoin(ff)
371 f = self.wjoin(ff)
372 try:
372 try:
373 st = os.lstat(f)
373 st = os.lstat(f)
374 except OSError, inst:
374 except OSError, inst:
375 nf = util.normpath(ff)
375 nf = util.normpath(ff)
376 found = False
376 found = False
377 for fn in dc:
377 for fn in dc:
378 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
378 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
379 found = True
379 found = True
380 break
380 break
381 if not found:
381 if not found:
382 self.ui.warn('%s: %s\n' % (
382 self.ui.warn('%s: %s\n' % (
383 util.pathto(self.getcwd(), ff),
383 util.pathto(self.getcwd(), ff),
384 inst.strerror))
384 inst.strerror))
385 continue
385 continue
386 if stat.S_ISDIR(st.st_mode):
386 if stat.S_ISDIR(st.st_mode):
387 cmp1 = (lambda x, y: cmp(x[1], y[1]))
387 cmp1 = (lambda x, y: cmp(x[1], y[1]))
388 sorted_ = [ x for x in findfiles(f) ]
388 sorted_ = [ x for x in findfiles(f) ]
389 sorted_.sort(cmp1)
389 sorted_.sort(cmp1)
390 for e in sorted_:
390 for e in sorted_:
391 yield e
391 yield e
392 else:
392 else:
393 ff = util.normpath(ff)
393 ff = util.normpath(ff)
394 if seen(ff):
394 if seen(ff):
395 continue
395 continue
396 self.blockignore = True
396 self.blockignore = True
397 if statmatch(ff, st):
397 if statmatch(ff, st):
398 if self.supported_type(ff, st, verbose=True):
398 if self.supported_type(ff, st, verbose=True):
399 yield 'f', ff, st
399 yield 'f', ff, st
400 elif ff in dc:
400 elif ff in dc:
401 yield 'm', ff, st
401 yield 'm', ff, st
402 self.blockignore = False
402 self.blockignore = False
403
403
404 # step two run through anything left in the dc hash and yield
404 # step two run through anything left in the dc hash and yield
405 # if we haven't already seen it
405 # if we haven't already seen it
406 ks = dc.keys()
406 ks = dc.keys()
407 ks.sort()
407 ks.sort()
408 for k in ks:
408 for k in ks:
409 if not seen(k) and (statmatch(k, None)):
409 if not seen(k) and (statmatch(k, None)):
410 yield 'm', k, None
410 yield 'm', k, None
411
411
412 def changes(self, files=None, match=util.always):
412 def changes(self, files=None, match=util.always, show_ignored=None):
413 lookup, modified, added, unknown = [], [], [], []
413 lookup, modified, added, unknown, ignored = [], [], [], [], []
414 removed, deleted = [], []
414 removed, deleted = [], []
415
415
416 for src, fn, st in self.statwalk(files, match):
416 for src, fn, st in self.statwalk(files, match, ignored=show_ignored):
417 try:
417 try:
418 type_, mode, size, time = self[fn]
418 type_, mode, size, time = self[fn]
419 except KeyError:
419 except KeyError:
420 unknown.append(fn)
420 if show_ignored and self.ignore(fn):
421 ignored.append(fn)
422 else:
423 unknown.append(fn)
421 continue
424 continue
422 if src == 'm':
425 if src == 'm':
423 nonexistent = True
426 nonexistent = True
424 if not st:
427 if not st:
425 try:
428 try:
426 f = self.wjoin(fn)
429 f = self.wjoin(fn)
427 st = os.lstat(f)
430 st = os.lstat(f)
428 except OSError, inst:
431 except OSError, inst:
429 if inst.errno != errno.ENOENT:
432 if inst.errno != errno.ENOENT:
430 raise
433 raise
431 st = None
434 st = None
432 # We need to re-check that it is a valid file
435 # We need to re-check that it is a valid file
433 if st and self.supported_type(fn, st):
436 if st and self.supported_type(fn, st):
434 nonexistent = False
437 nonexistent = False
435 # XXX: what to do with file no longer present in the fs
438 # XXX: what to do with file no longer present in the fs
436 # who are not removed in the dirstate ?
439 # who are not removed in the dirstate ?
437 if nonexistent and type_ in "nm":
440 if nonexistent and type_ in "nm":
438 deleted.append(fn)
441 deleted.append(fn)
439 continue
442 continue
440 # check the common case first
443 # check the common case first
441 if type_ == 'n':
444 if type_ == 'n':
442 if not st:
445 if not st:
443 st = os.stat(fn)
446 st = os.stat(fn)
444 if size >= 0 and (size != st.st_size
447 if size >= 0 and (size != st.st_size
445 or (mode ^ st.st_mode) & 0100):
448 or (mode ^ st.st_mode) & 0100):
446 modified.append(fn)
449 modified.append(fn)
447 elif time != st.st_mtime:
450 elif time != st.st_mtime:
448 lookup.append(fn)
451 lookup.append(fn)
449 elif type_ == 'm':
452 elif type_ == 'm':
450 modified.append(fn)
453 modified.append(fn)
451 elif type_ == 'a':
454 elif type_ == 'a':
452 added.append(fn)
455 added.append(fn)
453 elif type_ == 'r':
456 elif type_ == 'r':
454 removed.append(fn)
457 removed.append(fn)
455
458
456 return (lookup, modified, added, removed, deleted, unknown)
459 return (lookup, modified, added, removed, deleted, unknown, ignored)
@@ -1,1949 +1,1952
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 os, util
8 import os, util
9 import filelog, manifest, changelog, dirstate, repo
9 import filelog, manifest, changelog, dirstate, repo
10 from node import *
10 from node import *
11 from i18n import gettext as _
11 from i18n import gettext as _
12 from demandload import *
12 from demandload import *
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
14 demandload(globals(), "appendfile changegroup")
14 demandload(globals(), "appendfile changegroup")
15
15
16 class localrepository(object):
16 class localrepository(object):
17 def __del__(self):
17 def __del__(self):
18 self.transhandle = None
18 self.transhandle = None
19 def __init__(self, parentui, path=None, create=0):
19 def __init__(self, parentui, path=None, create=0):
20 if not path:
20 if not path:
21 p = os.getcwd()
21 p = os.getcwd()
22 while not os.path.isdir(os.path.join(p, ".hg")):
22 while not os.path.isdir(os.path.join(p, ".hg")):
23 oldp = p
23 oldp = p
24 p = os.path.dirname(p)
24 p = os.path.dirname(p)
25 if p == oldp:
25 if p == oldp:
26 raise repo.RepoError(_("no repo found"))
26 raise repo.RepoError(_("no repo found"))
27 path = p
27 path = p
28 self.path = os.path.join(path, ".hg")
28 self.path = os.path.join(path, ".hg")
29
29
30 if not create and not os.path.isdir(self.path):
30 if not create and not os.path.isdir(self.path):
31 raise repo.RepoError(_("repository %s not found") % path)
31 raise repo.RepoError(_("repository %s not found") % path)
32
32
33 self.root = os.path.abspath(path)
33 self.root = os.path.abspath(path)
34 self.origroot = path
34 self.origroot = path
35 self.ui = ui.ui(parentui=parentui)
35 self.ui = ui.ui(parentui=parentui)
36 self.opener = util.opener(self.path)
36 self.opener = util.opener(self.path)
37 self.wopener = util.opener(self.root)
37 self.wopener = util.opener(self.root)
38 self.manifest = manifest.manifest(self.opener)
38 self.manifest = manifest.manifest(self.opener)
39 self.changelog = changelog.changelog(self.opener)
39 self.changelog = changelog.changelog(self.opener)
40 self.tagscache = None
40 self.tagscache = None
41 self.nodetagscache = None
41 self.nodetagscache = None
42 self.encodepats = None
42 self.encodepats = None
43 self.decodepats = None
43 self.decodepats = None
44 self.transhandle = None
44 self.transhandle = None
45
45
46 if create:
46 if create:
47 os.mkdir(self.path)
47 os.mkdir(self.path)
48 os.mkdir(self.join("data"))
48 os.mkdir(self.join("data"))
49
49
50 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
50 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
51 try:
51 try:
52 self.ui.readconfig(self.join("hgrc"), self.root)
52 self.ui.readconfig(self.join("hgrc"), self.root)
53 except IOError:
53 except IOError:
54 pass
54 pass
55
55
56 def hook(self, name, throw=False, **args):
56 def hook(self, name, throw=False, **args):
57 def runhook(name, cmd):
57 def runhook(name, cmd):
58 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
58 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
59 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()] +
59 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()] +
60 [(k.upper(), v) for k, v in args.iteritems()])
60 [(k.upper(), v) for k, v in args.iteritems()])
61 r = util.system(cmd, environ=env, cwd=self.root)
61 r = util.system(cmd, environ=env, cwd=self.root)
62 if r:
62 if r:
63 desc, r = util.explain_exit(r)
63 desc, r = util.explain_exit(r)
64 if throw:
64 if throw:
65 raise util.Abort(_('%s hook %s') % (name, desc))
65 raise util.Abort(_('%s hook %s') % (name, desc))
66 self.ui.warn(_('error: %s hook %s\n') % (name, desc))
66 self.ui.warn(_('error: %s hook %s\n') % (name, desc))
67 return False
67 return False
68 return True
68 return True
69
69
70 r = True
70 r = True
71 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
71 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
72 if hname.split(".", 1)[0] == name and cmd]
72 if hname.split(".", 1)[0] == name and cmd]
73 hooks.sort()
73 hooks.sort()
74 for hname, cmd in hooks:
74 for hname, cmd in hooks:
75 r = runhook(hname, cmd) and r
75 r = runhook(hname, cmd) and r
76 return r
76 return r
77
77
78 def tags(self):
78 def tags(self):
79 '''return a mapping of tag to node'''
79 '''return a mapping of tag to node'''
80 if not self.tagscache:
80 if not self.tagscache:
81 self.tagscache = {}
81 self.tagscache = {}
82
82
83 def parsetag(line, context):
83 def parsetag(line, context):
84 if not line:
84 if not line:
85 return
85 return
86 s = l.split(" ", 1)
86 s = l.split(" ", 1)
87 if len(s) != 2:
87 if len(s) != 2:
88 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
88 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
89 return
89 return
90 node, key = s
90 node, key = s
91 try:
91 try:
92 bin_n = bin(node)
92 bin_n = bin(node)
93 except TypeError:
93 except TypeError:
94 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
94 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
95 return
95 return
96 if bin_n not in self.changelog.nodemap:
96 if bin_n not in self.changelog.nodemap:
97 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
97 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
98 return
98 return
99 self.tagscache[key.strip()] = bin_n
99 self.tagscache[key.strip()] = bin_n
100
100
101 # read each head of the tags file, ending with the tip
101 # read each head of the tags file, ending with the tip
102 # and add each tag found to the map, with "newer" ones
102 # and add each tag found to the map, with "newer" ones
103 # taking precedence
103 # taking precedence
104 fl = self.file(".hgtags")
104 fl = self.file(".hgtags")
105 h = fl.heads()
105 h = fl.heads()
106 h.reverse()
106 h.reverse()
107 for r in h:
107 for r in h:
108 count = 0
108 count = 0
109 for l in fl.read(r).splitlines():
109 for l in fl.read(r).splitlines():
110 count += 1
110 count += 1
111 parsetag(l, ".hgtags:%d" % count)
111 parsetag(l, ".hgtags:%d" % count)
112
112
113 try:
113 try:
114 f = self.opener("localtags")
114 f = self.opener("localtags")
115 count = 0
115 count = 0
116 for l in f:
116 for l in f:
117 count += 1
117 count += 1
118 parsetag(l, "localtags:%d" % count)
118 parsetag(l, "localtags:%d" % count)
119 except IOError:
119 except IOError:
120 pass
120 pass
121
121
122 self.tagscache['tip'] = self.changelog.tip()
122 self.tagscache['tip'] = self.changelog.tip()
123
123
124 return self.tagscache
124 return self.tagscache
125
125
126 def tagslist(self):
126 def tagslist(self):
127 '''return a list of tags ordered by revision'''
127 '''return a list of tags ordered by revision'''
128 l = []
128 l = []
129 for t, n in self.tags().items():
129 for t, n in self.tags().items():
130 try:
130 try:
131 r = self.changelog.rev(n)
131 r = self.changelog.rev(n)
132 except:
132 except:
133 r = -2 # sort to the beginning of the list if unknown
133 r = -2 # sort to the beginning of the list if unknown
134 l.append((r, t, n))
134 l.append((r, t, n))
135 l.sort()
135 l.sort()
136 return [(t, n) for r, t, n in l]
136 return [(t, n) for r, t, n in l]
137
137
138 def nodetags(self, node):
138 def nodetags(self, node):
139 '''return the tags associated with a node'''
139 '''return the tags associated with a node'''
140 if not self.nodetagscache:
140 if not self.nodetagscache:
141 self.nodetagscache = {}
141 self.nodetagscache = {}
142 for t, n in self.tags().items():
142 for t, n in self.tags().items():
143 self.nodetagscache.setdefault(n, []).append(t)
143 self.nodetagscache.setdefault(n, []).append(t)
144 return self.nodetagscache.get(node, [])
144 return self.nodetagscache.get(node, [])
145
145
146 def lookup(self, key):
146 def lookup(self, key):
147 try:
147 try:
148 return self.tags()[key]
148 return self.tags()[key]
149 except KeyError:
149 except KeyError:
150 try:
150 try:
151 return self.changelog.lookup(key)
151 return self.changelog.lookup(key)
152 except:
152 except:
153 raise repo.RepoError(_("unknown revision '%s'") % key)
153 raise repo.RepoError(_("unknown revision '%s'") % key)
154
154
155 def dev(self):
155 def dev(self):
156 return os.stat(self.path).st_dev
156 return os.stat(self.path).st_dev
157
157
158 def local(self):
158 def local(self):
159 return True
159 return True
160
160
161 def join(self, f):
161 def join(self, f):
162 return os.path.join(self.path, f)
162 return os.path.join(self.path, f)
163
163
164 def wjoin(self, f):
164 def wjoin(self, f):
165 return os.path.join(self.root, f)
165 return os.path.join(self.root, f)
166
166
167 def file(self, f):
167 def file(self, f):
168 if f[0] == '/':
168 if f[0] == '/':
169 f = f[1:]
169 f = f[1:]
170 return filelog.filelog(self.opener, f)
170 return filelog.filelog(self.opener, f)
171
171
172 def getcwd(self):
172 def getcwd(self):
173 return self.dirstate.getcwd()
173 return self.dirstate.getcwd()
174
174
175 def wfile(self, f, mode='r'):
175 def wfile(self, f, mode='r'):
176 return self.wopener(f, mode)
176 return self.wopener(f, mode)
177
177
178 def wread(self, filename):
178 def wread(self, filename):
179 if self.encodepats == None:
179 if self.encodepats == None:
180 l = []
180 l = []
181 for pat, cmd in self.ui.configitems("encode"):
181 for pat, cmd in self.ui.configitems("encode"):
182 mf = util.matcher(self.root, "", [pat], [], [])[1]
182 mf = util.matcher(self.root, "", [pat], [], [])[1]
183 l.append((mf, cmd))
183 l.append((mf, cmd))
184 self.encodepats = l
184 self.encodepats = l
185
185
186 data = self.wopener(filename, 'r').read()
186 data = self.wopener(filename, 'r').read()
187
187
188 for mf, cmd in self.encodepats:
188 for mf, cmd in self.encodepats:
189 if mf(filename):
189 if mf(filename):
190 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
190 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
191 data = util.filter(data, cmd)
191 data = util.filter(data, cmd)
192 break
192 break
193
193
194 return data
194 return data
195
195
196 def wwrite(self, filename, data, fd=None):
196 def wwrite(self, filename, data, fd=None):
197 if self.decodepats == None:
197 if self.decodepats == None:
198 l = []
198 l = []
199 for pat, cmd in self.ui.configitems("decode"):
199 for pat, cmd in self.ui.configitems("decode"):
200 mf = util.matcher(self.root, "", [pat], [], [])[1]
200 mf = util.matcher(self.root, "", [pat], [], [])[1]
201 l.append((mf, cmd))
201 l.append((mf, cmd))
202 self.decodepats = l
202 self.decodepats = l
203
203
204 for mf, cmd in self.decodepats:
204 for mf, cmd in self.decodepats:
205 if mf(filename):
205 if mf(filename):
206 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
206 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
207 data = util.filter(data, cmd)
207 data = util.filter(data, cmd)
208 break
208 break
209
209
210 if fd:
210 if fd:
211 return fd.write(data)
211 return fd.write(data)
212 return self.wopener(filename, 'w').write(data)
212 return self.wopener(filename, 'w').write(data)
213
213
214 def transaction(self):
214 def transaction(self):
215 tr = self.transhandle
215 tr = self.transhandle
216 if tr != None and tr.running():
216 if tr != None and tr.running():
217 return tr.nest()
217 return tr.nest()
218
218
219 # save dirstate for undo
219 # save dirstate for undo
220 try:
220 try:
221 ds = self.opener("dirstate").read()
221 ds = self.opener("dirstate").read()
222 except IOError:
222 except IOError:
223 ds = ""
223 ds = ""
224 self.opener("journal.dirstate", "w").write(ds)
224 self.opener("journal.dirstate", "w").write(ds)
225
225
226 tr = transaction.transaction(self.ui.warn, self.opener,
226 tr = transaction.transaction(self.ui.warn, self.opener,
227 self.join("journal"),
227 self.join("journal"),
228 aftertrans(self.path))
228 aftertrans(self.path))
229 self.transhandle = tr
229 self.transhandle = tr
230 return tr
230 return tr
231
231
232 def recover(self):
232 def recover(self):
233 l = self.lock()
233 l = self.lock()
234 if os.path.exists(self.join("journal")):
234 if os.path.exists(self.join("journal")):
235 self.ui.status(_("rolling back interrupted transaction\n"))
235 self.ui.status(_("rolling back interrupted transaction\n"))
236 transaction.rollback(self.opener, self.join("journal"))
236 transaction.rollback(self.opener, self.join("journal"))
237 self.reload()
237 self.reload()
238 return True
238 return True
239 else:
239 else:
240 self.ui.warn(_("no interrupted transaction available\n"))
240 self.ui.warn(_("no interrupted transaction available\n"))
241 return False
241 return False
242
242
243 def undo(self, wlock=None):
243 def undo(self, wlock=None):
244 if not wlock:
244 if not wlock:
245 wlock = self.wlock()
245 wlock = self.wlock()
246 l = self.lock()
246 l = self.lock()
247 if os.path.exists(self.join("undo")):
247 if os.path.exists(self.join("undo")):
248 self.ui.status(_("rolling back last transaction\n"))
248 self.ui.status(_("rolling back last transaction\n"))
249 transaction.rollback(self.opener, self.join("undo"))
249 transaction.rollback(self.opener, self.join("undo"))
250 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
250 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
251 self.reload()
251 self.reload()
252 self.wreload()
252 self.wreload()
253 else:
253 else:
254 self.ui.warn(_("no undo information available\n"))
254 self.ui.warn(_("no undo information available\n"))
255
255
256 def wreload(self):
256 def wreload(self):
257 self.dirstate.read()
257 self.dirstate.read()
258
258
259 def reload(self):
259 def reload(self):
260 self.changelog.load()
260 self.changelog.load()
261 self.manifest.load()
261 self.manifest.load()
262 self.tagscache = None
262 self.tagscache = None
263 self.nodetagscache = None
263 self.nodetagscache = None
264
264
265 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None,
265 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None,
266 desc=None):
266 desc=None):
267 try:
267 try:
268 l = lock.lock(self.join(lockname), 0, releasefn, desc=desc)
268 l = lock.lock(self.join(lockname), 0, releasefn, desc=desc)
269 except lock.LockHeld, inst:
269 except lock.LockHeld, inst:
270 if not wait:
270 if not wait:
271 raise
271 raise
272 self.ui.warn(_("waiting for lock on %s held by %s\n") %
272 self.ui.warn(_("waiting for lock on %s held by %s\n") %
273 (desc, inst.args[0]))
273 (desc, inst.args[0]))
274 # default to 600 seconds timeout
274 # default to 600 seconds timeout
275 l = lock.lock(self.join(lockname),
275 l = lock.lock(self.join(lockname),
276 int(self.ui.config("ui", "timeout") or 600),
276 int(self.ui.config("ui", "timeout") or 600),
277 releasefn, desc=desc)
277 releasefn, desc=desc)
278 if acquirefn:
278 if acquirefn:
279 acquirefn()
279 acquirefn()
280 return l
280 return l
281
281
282 def lock(self, wait=1):
282 def lock(self, wait=1):
283 return self.do_lock("lock", wait, acquirefn=self.reload,
283 return self.do_lock("lock", wait, acquirefn=self.reload,
284 desc=_('repository %s') % self.origroot)
284 desc=_('repository %s') % self.origroot)
285
285
286 def wlock(self, wait=1):
286 def wlock(self, wait=1):
287 return self.do_lock("wlock", wait, self.dirstate.write,
287 return self.do_lock("wlock", wait, self.dirstate.write,
288 self.wreload,
288 self.wreload,
289 desc=_('working directory of %s') % self.origroot)
289 desc=_('working directory of %s') % self.origroot)
290
290
291 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
291 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
292 "determine whether a new filenode is needed"
292 "determine whether a new filenode is needed"
293 fp1 = manifest1.get(filename, nullid)
293 fp1 = manifest1.get(filename, nullid)
294 fp2 = manifest2.get(filename, nullid)
294 fp2 = manifest2.get(filename, nullid)
295
295
296 if fp2 != nullid:
296 if fp2 != nullid:
297 # is one parent an ancestor of the other?
297 # is one parent an ancestor of the other?
298 fpa = filelog.ancestor(fp1, fp2)
298 fpa = filelog.ancestor(fp1, fp2)
299 if fpa == fp1:
299 if fpa == fp1:
300 fp1, fp2 = fp2, nullid
300 fp1, fp2 = fp2, nullid
301 elif fpa == fp2:
301 elif fpa == fp2:
302 fp2 = nullid
302 fp2 = nullid
303
303
304 # is the file unmodified from the parent? report existing entry
304 # is the file unmodified from the parent? report existing entry
305 if fp2 == nullid and text == filelog.read(fp1):
305 if fp2 == nullid and text == filelog.read(fp1):
306 return (fp1, None, None)
306 return (fp1, None, None)
307
307
308 return (None, fp1, fp2)
308 return (None, fp1, fp2)
309
309
310 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
310 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
311 orig_parent = self.dirstate.parents()[0] or nullid
311 orig_parent = self.dirstate.parents()[0] or nullid
312 p1 = p1 or self.dirstate.parents()[0] or nullid
312 p1 = p1 or self.dirstate.parents()[0] or nullid
313 p2 = p2 or self.dirstate.parents()[1] or nullid
313 p2 = p2 or self.dirstate.parents()[1] or nullid
314 c1 = self.changelog.read(p1)
314 c1 = self.changelog.read(p1)
315 c2 = self.changelog.read(p2)
315 c2 = self.changelog.read(p2)
316 m1 = self.manifest.read(c1[0])
316 m1 = self.manifest.read(c1[0])
317 mf1 = self.manifest.readflags(c1[0])
317 mf1 = self.manifest.readflags(c1[0])
318 m2 = self.manifest.read(c2[0])
318 m2 = self.manifest.read(c2[0])
319 changed = []
319 changed = []
320
320
321 if orig_parent == p1:
321 if orig_parent == p1:
322 update_dirstate = 1
322 update_dirstate = 1
323 else:
323 else:
324 update_dirstate = 0
324 update_dirstate = 0
325
325
326 if not wlock:
326 if not wlock:
327 wlock = self.wlock()
327 wlock = self.wlock()
328 l = self.lock()
328 l = self.lock()
329 tr = self.transaction()
329 tr = self.transaction()
330 mm = m1.copy()
330 mm = m1.copy()
331 mfm = mf1.copy()
331 mfm = mf1.copy()
332 linkrev = self.changelog.count()
332 linkrev = self.changelog.count()
333 for f in files:
333 for f in files:
334 try:
334 try:
335 t = self.wread(f)
335 t = self.wread(f)
336 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
336 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
337 r = self.file(f)
337 r = self.file(f)
338 mfm[f] = tm
338 mfm[f] = tm
339
339
340 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
340 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
341 if entry:
341 if entry:
342 mm[f] = entry
342 mm[f] = entry
343 continue
343 continue
344
344
345 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
345 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
346 changed.append(f)
346 changed.append(f)
347 if update_dirstate:
347 if update_dirstate:
348 self.dirstate.update([f], "n")
348 self.dirstate.update([f], "n")
349 except IOError:
349 except IOError:
350 try:
350 try:
351 del mm[f]
351 del mm[f]
352 del mfm[f]
352 del mfm[f]
353 if update_dirstate:
353 if update_dirstate:
354 self.dirstate.forget([f])
354 self.dirstate.forget([f])
355 except:
355 except:
356 # deleted from p2?
356 # deleted from p2?
357 pass
357 pass
358
358
359 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
359 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
360 user = user or self.ui.username()
360 user = user or self.ui.username()
361 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
361 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
362 tr.close()
362 tr.close()
363 if update_dirstate:
363 if update_dirstate:
364 self.dirstate.setparents(n, nullid)
364 self.dirstate.setparents(n, nullid)
365
365
366 def commit(self, files=None, text="", user=None, date=None,
366 def commit(self, files=None, text="", user=None, date=None,
367 match=util.always, force=False, lock=None, wlock=None):
367 match=util.always, force=False, lock=None, wlock=None):
368 commit = []
368 commit = []
369 remove = []
369 remove = []
370 changed = []
370 changed = []
371
371
372 if files:
372 if files:
373 for f in files:
373 for f in files:
374 s = self.dirstate.state(f)
374 s = self.dirstate.state(f)
375 if s in 'nmai':
375 if s in 'nmai':
376 commit.append(f)
376 commit.append(f)
377 elif s == 'r':
377 elif s == 'r':
378 remove.append(f)
378 remove.append(f)
379 else:
379 else:
380 self.ui.warn(_("%s not tracked!\n") % f)
380 self.ui.warn(_("%s not tracked!\n") % f)
381 else:
381 else:
382 modified, added, removed, deleted, unknown = self.changes(match=match)
382 modified, added, removed, deleted, unknown = self.changes(match=match)
383 commit = modified + added
383 commit = modified + added
384 remove = removed
384 remove = removed
385
385
386 p1, p2 = self.dirstate.parents()
386 p1, p2 = self.dirstate.parents()
387 c1 = self.changelog.read(p1)
387 c1 = self.changelog.read(p1)
388 c2 = self.changelog.read(p2)
388 c2 = self.changelog.read(p2)
389 m1 = self.manifest.read(c1[0])
389 m1 = self.manifest.read(c1[0])
390 mf1 = self.manifest.readflags(c1[0])
390 mf1 = self.manifest.readflags(c1[0])
391 m2 = self.manifest.read(c2[0])
391 m2 = self.manifest.read(c2[0])
392
392
393 if not commit and not remove and not force and p2 == nullid:
393 if not commit and not remove and not force and p2 == nullid:
394 self.ui.status(_("nothing changed\n"))
394 self.ui.status(_("nothing changed\n"))
395 return None
395 return None
396
396
397 xp1 = hex(p1)
397 xp1 = hex(p1)
398 if p2 == nullid: xp2 = ''
398 if p2 == nullid: xp2 = ''
399 else: xp2 = hex(p2)
399 else: xp2 = hex(p2)
400
400
401 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
401 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
402
402
403 if not wlock:
403 if not wlock:
404 wlock = self.wlock()
404 wlock = self.wlock()
405 if not lock:
405 if not lock:
406 lock = self.lock()
406 lock = self.lock()
407 tr = self.transaction()
407 tr = self.transaction()
408
408
409 # check in files
409 # check in files
410 new = {}
410 new = {}
411 linkrev = self.changelog.count()
411 linkrev = self.changelog.count()
412 commit.sort()
412 commit.sort()
413 for f in commit:
413 for f in commit:
414 self.ui.note(f + "\n")
414 self.ui.note(f + "\n")
415 try:
415 try:
416 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
416 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
417 t = self.wread(f)
417 t = self.wread(f)
418 except IOError:
418 except IOError:
419 self.ui.warn(_("trouble committing %s!\n") % f)
419 self.ui.warn(_("trouble committing %s!\n") % f)
420 raise
420 raise
421
421
422 r = self.file(f)
422 r = self.file(f)
423
423
424 meta = {}
424 meta = {}
425 cp = self.dirstate.copied(f)
425 cp = self.dirstate.copied(f)
426 if cp:
426 if cp:
427 meta["copy"] = cp
427 meta["copy"] = cp
428 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
428 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
429 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
429 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
430 fp1, fp2 = nullid, nullid
430 fp1, fp2 = nullid, nullid
431 else:
431 else:
432 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
432 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
433 if entry:
433 if entry:
434 new[f] = entry
434 new[f] = entry
435 continue
435 continue
436
436
437 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
437 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
438 # remember what we've added so that we can later calculate
438 # remember what we've added so that we can later calculate
439 # the files to pull from a set of changesets
439 # the files to pull from a set of changesets
440 changed.append(f)
440 changed.append(f)
441
441
442 # update manifest
442 # update manifest
443 m1 = m1.copy()
443 m1 = m1.copy()
444 m1.update(new)
444 m1.update(new)
445 for f in remove:
445 for f in remove:
446 if f in m1:
446 if f in m1:
447 del m1[f]
447 del m1[f]
448 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
448 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
449 (new, remove))
449 (new, remove))
450
450
451 # add changeset
451 # add changeset
452 new = new.keys()
452 new = new.keys()
453 new.sort()
453 new.sort()
454
454
455 user = user or self.ui.username()
455 user = user or self.ui.username()
456 if not text:
456 if not text:
457 edittext = [""]
457 edittext = [""]
458 if p2 != nullid:
458 if p2 != nullid:
459 edittext.append("HG: branch merge")
459 edittext.append("HG: branch merge")
460 edittext.extend(["HG: changed %s" % f for f in changed])
460 edittext.extend(["HG: changed %s" % f for f in changed])
461 edittext.extend(["HG: removed %s" % f for f in remove])
461 edittext.extend(["HG: removed %s" % f for f in remove])
462 if not changed and not remove:
462 if not changed and not remove:
463 edittext.append("HG: no files changed")
463 edittext.append("HG: no files changed")
464 edittext.append("")
464 edittext.append("")
465 # run editor in the repository root
465 # run editor in the repository root
466 olddir = os.getcwd()
466 olddir = os.getcwd()
467 os.chdir(self.root)
467 os.chdir(self.root)
468 edittext = self.ui.edit("\n".join(edittext), user)
468 edittext = self.ui.edit("\n".join(edittext), user)
469 os.chdir(olddir)
469 os.chdir(olddir)
470 if not edittext.rstrip():
470 if not edittext.rstrip():
471 return None
471 return None
472 text = edittext
472 text = edittext
473
473
474 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
474 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
475 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
475 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
476 parent2=xp2)
476 parent2=xp2)
477 tr.close()
477 tr.close()
478
478
479 self.dirstate.setparents(n)
479 self.dirstate.setparents(n)
480 self.dirstate.update(new, "n")
480 self.dirstate.update(new, "n")
481 self.dirstate.forget(remove)
481 self.dirstate.forget(remove)
482
482
483 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
483 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
484 return n
484 return n
485
485
486 def walk(self, node=None, files=[], match=util.always):
486 def walk(self, node=None, files=[], match=util.always):
487 if node:
487 if node:
488 fdict = dict.fromkeys(files)
488 fdict = dict.fromkeys(files)
489 for fn in self.manifest.read(self.changelog.read(node)[0]):
489 for fn in self.manifest.read(self.changelog.read(node)[0]):
490 fdict.pop(fn, None)
490 fdict.pop(fn, None)
491 if match(fn):
491 if match(fn):
492 yield 'm', fn
492 yield 'm', fn
493 for fn in fdict:
493 for fn in fdict:
494 self.ui.warn(_('%s: No such file in rev %s\n') % (
494 self.ui.warn(_('%s: No such file in rev %s\n') % (
495 util.pathto(self.getcwd(), fn), short(node)))
495 util.pathto(self.getcwd(), fn), short(node)))
496 else:
496 else:
497 for src, fn in self.dirstate.walk(files, match):
497 for src, fn in self.dirstate.walk(files, match):
498 yield src, fn
498 yield src, fn
499
499
500 def changes(self, node1=None, node2=None, files=[], match=util.always,
500 def changes(self, node1=None, node2=None, files=[], match=util.always,
501 wlock=None):
501 wlock=None, show_ignored=None):
502 """return changes between two nodes or node and working directory
502 """return changes between two nodes or node and working directory
503
503
504 If node1 is None, use the first dirstate parent instead.
504 If node1 is None, use the first dirstate parent instead.
505 If node2 is None, compare node1 with working directory.
505 If node2 is None, compare node1 with working directory.
506 """
506 """
507
507
508 def fcmp(fn, mf):
508 def fcmp(fn, mf):
509 t1 = self.wread(fn)
509 t1 = self.wread(fn)
510 t2 = self.file(fn).read(mf.get(fn, nullid))
510 t2 = self.file(fn).read(mf.get(fn, nullid))
511 return cmp(t1, t2)
511 return cmp(t1, t2)
512
512
513 def mfmatches(node):
513 def mfmatches(node):
514 change = self.changelog.read(node)
514 change = self.changelog.read(node)
515 mf = dict(self.manifest.read(change[0]))
515 mf = dict(self.manifest.read(change[0]))
516 for fn in mf.keys():
516 for fn in mf.keys():
517 if not match(fn):
517 if not match(fn):
518 del mf[fn]
518 del mf[fn]
519 return mf
519 return mf
520
520
521 if node1:
521 if node1:
522 # read the manifest from node1 before the manifest from node2,
522 # read the manifest from node1 before the manifest from node2,
523 # so that we'll hit the manifest cache if we're going through
523 # so that we'll hit the manifest cache if we're going through
524 # all the revisions in parent->child order.
524 # all the revisions in parent->child order.
525 mf1 = mfmatches(node1)
525 mf1 = mfmatches(node1)
526
526
527 # are we comparing the working directory?
527 # are we comparing the working directory?
528 if not node2:
528 if not node2:
529 if not wlock:
529 if not wlock:
530 try:
530 try:
531 wlock = self.wlock(wait=0)
531 wlock = self.wlock(wait=0)
532 except lock.LockException:
532 except lock.LockException:
533 wlock = None
533 wlock = None
534 lookup, modified, added, removed, deleted, unknown = (
534 lookup, modified, added, removed, deleted, unknown, ignored = (
535 self.dirstate.changes(files, match))
535 self.dirstate.changes(files, match, show_ignored))
536
536
537 # are we comparing working dir against its parent?
537 # are we comparing working dir against its parent?
538 if not node1:
538 if not node1:
539 if lookup:
539 if lookup:
540 # do a full compare of any files that might have changed
540 # do a full compare of any files that might have changed
541 mf2 = mfmatches(self.dirstate.parents()[0])
541 mf2 = mfmatches(self.dirstate.parents()[0])
542 for f in lookup:
542 for f in lookup:
543 if fcmp(f, mf2):
543 if fcmp(f, mf2):
544 modified.append(f)
544 modified.append(f)
545 elif wlock is not None:
545 elif wlock is not None:
546 self.dirstate.update([f], "n")
546 self.dirstate.update([f], "n")
547 else:
547 else:
548 # we are comparing working dir against non-parent
548 # we are comparing working dir against non-parent
549 # generate a pseudo-manifest for the working dir
549 # generate a pseudo-manifest for the working dir
550 mf2 = mfmatches(self.dirstate.parents()[0])
550 mf2 = mfmatches(self.dirstate.parents()[0])
551 for f in lookup + modified + added:
551 for f in lookup + modified + added:
552 mf2[f] = ""
552 mf2[f] = ""
553 for f in removed:
553 for f in removed:
554 if f in mf2:
554 if f in mf2:
555 del mf2[f]
555 del mf2[f]
556 else:
556 else:
557 # we are comparing two revisions
557 # we are comparing two revisions
558 deleted, unknown = [], []
558 deleted, unknown, ignored = [], [], []
559 mf2 = mfmatches(node2)
559 mf2 = mfmatches(node2)
560
560
561 if node1:
561 if node1:
562 # flush lists from dirstate before comparing manifests
562 # flush lists from dirstate before comparing manifests
563 modified, added = [], []
563 modified, added = [], []
564
564
565 for fn in mf2:
565 for fn in mf2:
566 if mf1.has_key(fn):
566 if mf1.has_key(fn):
567 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
567 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
568 modified.append(fn)
568 modified.append(fn)
569 del mf1[fn]
569 del mf1[fn]
570 else:
570 else:
571 added.append(fn)
571 added.append(fn)
572
572
573 removed = mf1.keys()
573 removed = mf1.keys()
574
574
575 # sort and return results:
575 # sort and return results:
576 for l in modified, added, removed, deleted, unknown:
576 for l in modified, added, removed, deleted, unknown, ignored:
577 l.sort()
577 l.sort()
578 return (modified, added, removed, deleted, unknown)
578 if show_ignored is None:
579 return (modified, added, removed, deleted, unknown)
580 else:
581 return (modified, added, removed, deleted, unknown, ignored)
579
582
580 def add(self, list, wlock=None):
583 def add(self, list, wlock=None):
581 if not wlock:
584 if not wlock:
582 wlock = self.wlock()
585 wlock = self.wlock()
583 for f in list:
586 for f in list:
584 p = self.wjoin(f)
587 p = self.wjoin(f)
585 if not os.path.exists(p):
588 if not os.path.exists(p):
586 self.ui.warn(_("%s does not exist!\n") % f)
589 self.ui.warn(_("%s does not exist!\n") % f)
587 elif not os.path.isfile(p):
590 elif not os.path.isfile(p):
588 self.ui.warn(_("%s not added: only files supported currently\n")
591 self.ui.warn(_("%s not added: only files supported currently\n")
589 % f)
592 % f)
590 elif self.dirstate.state(f) in 'an':
593 elif self.dirstate.state(f) in 'an':
591 self.ui.warn(_("%s already tracked!\n") % f)
594 self.ui.warn(_("%s already tracked!\n") % f)
592 else:
595 else:
593 self.dirstate.update([f], "a")
596 self.dirstate.update([f], "a")
594
597
595 def forget(self, list, wlock=None):
598 def forget(self, list, wlock=None):
596 if not wlock:
599 if not wlock:
597 wlock = self.wlock()
600 wlock = self.wlock()
598 for f in list:
601 for f in list:
599 if self.dirstate.state(f) not in 'ai':
602 if self.dirstate.state(f) not in 'ai':
600 self.ui.warn(_("%s not added!\n") % f)
603 self.ui.warn(_("%s not added!\n") % f)
601 else:
604 else:
602 self.dirstate.forget([f])
605 self.dirstate.forget([f])
603
606
604 def remove(self, list, unlink=False, wlock=None):
607 def remove(self, list, unlink=False, wlock=None):
605 if unlink:
608 if unlink:
606 for f in list:
609 for f in list:
607 try:
610 try:
608 util.unlink(self.wjoin(f))
611 util.unlink(self.wjoin(f))
609 except OSError, inst:
612 except OSError, inst:
610 if inst.errno != errno.ENOENT:
613 if inst.errno != errno.ENOENT:
611 raise
614 raise
612 if not wlock:
615 if not wlock:
613 wlock = self.wlock()
616 wlock = self.wlock()
614 for f in list:
617 for f in list:
615 p = self.wjoin(f)
618 p = self.wjoin(f)
616 if os.path.exists(p):
619 if os.path.exists(p):
617 self.ui.warn(_("%s still exists!\n") % f)
620 self.ui.warn(_("%s still exists!\n") % f)
618 elif self.dirstate.state(f) == 'a':
621 elif self.dirstate.state(f) == 'a':
619 self.dirstate.forget([f])
622 self.dirstate.forget([f])
620 elif f not in self.dirstate:
623 elif f not in self.dirstate:
621 self.ui.warn(_("%s not tracked!\n") % f)
624 self.ui.warn(_("%s not tracked!\n") % f)
622 else:
625 else:
623 self.dirstate.update([f], "r")
626 self.dirstate.update([f], "r")
624
627
625 def undelete(self, list, wlock=None):
628 def undelete(self, list, wlock=None):
626 p = self.dirstate.parents()[0]
629 p = self.dirstate.parents()[0]
627 mn = self.changelog.read(p)[0]
630 mn = self.changelog.read(p)[0]
628 mf = self.manifest.readflags(mn)
631 mf = self.manifest.readflags(mn)
629 m = self.manifest.read(mn)
632 m = self.manifest.read(mn)
630 if not wlock:
633 if not wlock:
631 wlock = self.wlock()
634 wlock = self.wlock()
632 for f in list:
635 for f in list:
633 if self.dirstate.state(f) not in "r":
636 if self.dirstate.state(f) not in "r":
634 self.ui.warn("%s not removed!\n" % f)
637 self.ui.warn("%s not removed!\n" % f)
635 else:
638 else:
636 t = self.file(f).read(m[f])
639 t = self.file(f).read(m[f])
637 self.wwrite(f, t)
640 self.wwrite(f, t)
638 util.set_exec(self.wjoin(f), mf[f])
641 util.set_exec(self.wjoin(f), mf[f])
639 self.dirstate.update([f], "n")
642 self.dirstate.update([f], "n")
640
643
641 def copy(self, source, dest, wlock=None):
644 def copy(self, source, dest, wlock=None):
642 p = self.wjoin(dest)
645 p = self.wjoin(dest)
643 if not os.path.exists(p):
646 if not os.path.exists(p):
644 self.ui.warn(_("%s does not exist!\n") % dest)
647 self.ui.warn(_("%s does not exist!\n") % dest)
645 elif not os.path.isfile(p):
648 elif not os.path.isfile(p):
646 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
649 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
647 else:
650 else:
648 if not wlock:
651 if not wlock:
649 wlock = self.wlock()
652 wlock = self.wlock()
650 if self.dirstate.state(dest) == '?':
653 if self.dirstate.state(dest) == '?':
651 self.dirstate.update([dest], "a")
654 self.dirstate.update([dest], "a")
652 self.dirstate.copy(source, dest)
655 self.dirstate.copy(source, dest)
653
656
654 def heads(self, start=None):
657 def heads(self, start=None):
655 heads = self.changelog.heads(start)
658 heads = self.changelog.heads(start)
656 # sort the output in rev descending order
659 # sort the output in rev descending order
657 heads = [(-self.changelog.rev(h), h) for h in heads]
660 heads = [(-self.changelog.rev(h), h) for h in heads]
658 heads.sort()
661 heads.sort()
659 return [n for (r, n) in heads]
662 return [n for (r, n) in heads]
660
663
661 # branchlookup returns a dict giving a list of branches for
664 # branchlookup returns a dict giving a list of branches for
662 # each head. A branch is defined as the tag of a node or
665 # each head. A branch is defined as the tag of a node or
663 # the branch of the node's parents. If a node has multiple
666 # the branch of the node's parents. If a node has multiple
664 # branch tags, tags are eliminated if they are visible from other
667 # branch tags, tags are eliminated if they are visible from other
665 # branch tags.
668 # branch tags.
666 #
669 #
667 # So, for this graph: a->b->c->d->e
670 # So, for this graph: a->b->c->d->e
668 # \ /
671 # \ /
669 # aa -----/
672 # aa -----/
670 # a has tag 2.6.12
673 # a has tag 2.6.12
671 # d has tag 2.6.13
674 # d has tag 2.6.13
672 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
675 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
673 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
676 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
674 # from the list.
677 # from the list.
675 #
678 #
676 # It is possible that more than one head will have the same branch tag.
679 # It is possible that more than one head will have the same branch tag.
677 # callers need to check the result for multiple heads under the same
680 # callers need to check the result for multiple heads under the same
678 # branch tag if that is a problem for them (ie checkout of a specific
681 # branch tag if that is a problem for them (ie checkout of a specific
679 # branch).
682 # branch).
680 #
683 #
681 # passing in a specific branch will limit the depth of the search
684 # passing in a specific branch will limit the depth of the search
682 # through the parents. It won't limit the branches returned in the
685 # through the parents. It won't limit the branches returned in the
683 # result though.
686 # result though.
684 def branchlookup(self, heads=None, branch=None):
687 def branchlookup(self, heads=None, branch=None):
685 if not heads:
688 if not heads:
686 heads = self.heads()
689 heads = self.heads()
687 headt = [ h for h in heads ]
690 headt = [ h for h in heads ]
688 chlog = self.changelog
691 chlog = self.changelog
689 branches = {}
692 branches = {}
690 merges = []
693 merges = []
691 seenmerge = {}
694 seenmerge = {}
692
695
693 # traverse the tree once for each head, recording in the branches
696 # traverse the tree once for each head, recording in the branches
694 # dict which tags are visible from this head. The branches
697 # dict which tags are visible from this head. The branches
695 # dict also records which tags are visible from each tag
698 # dict also records which tags are visible from each tag
696 # while we traverse.
699 # while we traverse.
697 while headt or merges:
700 while headt or merges:
698 if merges:
701 if merges:
699 n, found = merges.pop()
702 n, found = merges.pop()
700 visit = [n]
703 visit = [n]
701 else:
704 else:
702 h = headt.pop()
705 h = headt.pop()
703 visit = [h]
706 visit = [h]
704 found = [h]
707 found = [h]
705 seen = {}
708 seen = {}
706 while visit:
709 while visit:
707 n = visit.pop()
710 n = visit.pop()
708 if n in seen:
711 if n in seen:
709 continue
712 continue
710 pp = chlog.parents(n)
713 pp = chlog.parents(n)
711 tags = self.nodetags(n)
714 tags = self.nodetags(n)
712 if tags:
715 if tags:
713 for x in tags:
716 for x in tags:
714 if x == 'tip':
717 if x == 'tip':
715 continue
718 continue
716 for f in found:
719 for f in found:
717 branches.setdefault(f, {})[n] = 1
720 branches.setdefault(f, {})[n] = 1
718 branches.setdefault(n, {})[n] = 1
721 branches.setdefault(n, {})[n] = 1
719 break
722 break
720 if n not in found:
723 if n not in found:
721 found.append(n)
724 found.append(n)
722 if branch in tags:
725 if branch in tags:
723 continue
726 continue
724 seen[n] = 1
727 seen[n] = 1
725 if pp[1] != nullid and n not in seenmerge:
728 if pp[1] != nullid and n not in seenmerge:
726 merges.append((pp[1], [x for x in found]))
729 merges.append((pp[1], [x for x in found]))
727 seenmerge[n] = 1
730 seenmerge[n] = 1
728 if pp[0] != nullid:
731 if pp[0] != nullid:
729 visit.append(pp[0])
732 visit.append(pp[0])
730 # traverse the branches dict, eliminating branch tags from each
733 # traverse the branches dict, eliminating branch tags from each
731 # head that are visible from another branch tag for that head.
734 # head that are visible from another branch tag for that head.
732 out = {}
735 out = {}
733 viscache = {}
736 viscache = {}
734 for h in heads:
737 for h in heads:
735 def visible(node):
738 def visible(node):
736 if node in viscache:
739 if node in viscache:
737 return viscache[node]
740 return viscache[node]
738 ret = {}
741 ret = {}
739 visit = [node]
742 visit = [node]
740 while visit:
743 while visit:
741 x = visit.pop()
744 x = visit.pop()
742 if x in viscache:
745 if x in viscache:
743 ret.update(viscache[x])
746 ret.update(viscache[x])
744 elif x not in ret:
747 elif x not in ret:
745 ret[x] = 1
748 ret[x] = 1
746 if x in branches:
749 if x in branches:
747 visit[len(visit):] = branches[x].keys()
750 visit[len(visit):] = branches[x].keys()
748 viscache[node] = ret
751 viscache[node] = ret
749 return ret
752 return ret
750 if h not in branches:
753 if h not in branches:
751 continue
754 continue
752 # O(n^2), but somewhat limited. This only searches the
755 # O(n^2), but somewhat limited. This only searches the
753 # tags visible from a specific head, not all the tags in the
756 # tags visible from a specific head, not all the tags in the
754 # whole repo.
757 # whole repo.
755 for b in branches[h]:
758 for b in branches[h]:
756 vis = False
759 vis = False
757 for bb in branches[h].keys():
760 for bb in branches[h].keys():
758 if b != bb:
761 if b != bb:
759 if b in visible(bb):
762 if b in visible(bb):
760 vis = True
763 vis = True
761 break
764 break
762 if not vis:
765 if not vis:
763 l = out.setdefault(h, [])
766 l = out.setdefault(h, [])
764 l[len(l):] = self.nodetags(b)
767 l[len(l):] = self.nodetags(b)
765 return out
768 return out
766
769
767 def branches(self, nodes):
770 def branches(self, nodes):
768 if not nodes:
771 if not nodes:
769 nodes = [self.changelog.tip()]
772 nodes = [self.changelog.tip()]
770 b = []
773 b = []
771 for n in nodes:
774 for n in nodes:
772 t = n
775 t = n
773 while n:
776 while n:
774 p = self.changelog.parents(n)
777 p = self.changelog.parents(n)
775 if p[1] != nullid or p[0] == nullid:
778 if p[1] != nullid or p[0] == nullid:
776 b.append((t, n, p[0], p[1]))
779 b.append((t, n, p[0], p[1]))
777 break
780 break
778 n = p[0]
781 n = p[0]
779 return b
782 return b
780
783
781 def between(self, pairs):
784 def between(self, pairs):
782 r = []
785 r = []
783
786
784 for top, bottom in pairs:
787 for top, bottom in pairs:
785 n, l, i = top, [], 0
788 n, l, i = top, [], 0
786 f = 1
789 f = 1
787
790
788 while n != bottom:
791 while n != bottom:
789 p = self.changelog.parents(n)[0]
792 p = self.changelog.parents(n)[0]
790 if i == f:
793 if i == f:
791 l.append(n)
794 l.append(n)
792 f = f * 2
795 f = f * 2
793 n = p
796 n = p
794 i += 1
797 i += 1
795
798
796 r.append(l)
799 r.append(l)
797
800
798 return r
801 return r
799
802
800 def findincoming(self, remote, base=None, heads=None, force=False):
803 def findincoming(self, remote, base=None, heads=None, force=False):
801 m = self.changelog.nodemap
804 m = self.changelog.nodemap
802 search = []
805 search = []
803 fetch = {}
806 fetch = {}
804 seen = {}
807 seen = {}
805 seenbranch = {}
808 seenbranch = {}
806 if base == None:
809 if base == None:
807 base = {}
810 base = {}
808
811
809 # assume we're closer to the tip than the root
812 # assume we're closer to the tip than the root
810 # and start by examining the heads
813 # and start by examining the heads
811 self.ui.status(_("searching for changes\n"))
814 self.ui.status(_("searching for changes\n"))
812
815
813 if not heads:
816 if not heads:
814 heads = remote.heads()
817 heads = remote.heads()
815
818
816 unknown = []
819 unknown = []
817 for h in heads:
820 for h in heads:
818 if h not in m:
821 if h not in m:
819 unknown.append(h)
822 unknown.append(h)
820 else:
823 else:
821 base[h] = 1
824 base[h] = 1
822
825
823 if not unknown:
826 if not unknown:
824 return []
827 return []
825
828
826 rep = {}
829 rep = {}
827 reqcnt = 0
830 reqcnt = 0
828
831
829 # search through remote branches
832 # search through remote branches
830 # a 'branch' here is a linear segment of history, with four parts:
833 # a 'branch' here is a linear segment of history, with four parts:
831 # head, root, first parent, second parent
834 # head, root, first parent, second parent
832 # (a branch always has two parents (or none) by definition)
835 # (a branch always has two parents (or none) by definition)
833 unknown = remote.branches(unknown)
836 unknown = remote.branches(unknown)
834 while unknown:
837 while unknown:
835 r = []
838 r = []
836 while unknown:
839 while unknown:
837 n = unknown.pop(0)
840 n = unknown.pop(0)
838 if n[0] in seen:
841 if n[0] in seen:
839 continue
842 continue
840
843
841 self.ui.debug(_("examining %s:%s\n")
844 self.ui.debug(_("examining %s:%s\n")
842 % (short(n[0]), short(n[1])))
845 % (short(n[0]), short(n[1])))
843 if n[0] == nullid:
846 if n[0] == nullid:
844 break
847 break
845 if n in seenbranch:
848 if n in seenbranch:
846 self.ui.debug(_("branch already found\n"))
849 self.ui.debug(_("branch already found\n"))
847 continue
850 continue
848 if n[1] and n[1] in m: # do we know the base?
851 if n[1] and n[1] in m: # do we know the base?
849 self.ui.debug(_("found incomplete branch %s:%s\n")
852 self.ui.debug(_("found incomplete branch %s:%s\n")
850 % (short(n[0]), short(n[1])))
853 % (short(n[0]), short(n[1])))
851 search.append(n) # schedule branch range for scanning
854 search.append(n) # schedule branch range for scanning
852 seenbranch[n] = 1
855 seenbranch[n] = 1
853 else:
856 else:
854 if n[1] not in seen and n[1] not in fetch:
857 if n[1] not in seen and n[1] not in fetch:
855 if n[2] in m and n[3] in m:
858 if n[2] in m and n[3] in m:
856 self.ui.debug(_("found new changeset %s\n") %
859 self.ui.debug(_("found new changeset %s\n") %
857 short(n[1]))
860 short(n[1]))
858 fetch[n[1]] = 1 # earliest unknown
861 fetch[n[1]] = 1 # earliest unknown
859 base[n[2]] = 1 # latest known
862 base[n[2]] = 1 # latest known
860 continue
863 continue
861
864
862 for a in n[2:4]:
865 for a in n[2:4]:
863 if a not in rep:
866 if a not in rep:
864 r.append(a)
867 r.append(a)
865 rep[a] = 1
868 rep[a] = 1
866
869
867 seen[n[0]] = 1
870 seen[n[0]] = 1
868
871
869 if r:
872 if r:
870 reqcnt += 1
873 reqcnt += 1
871 self.ui.debug(_("request %d: %s\n") %
874 self.ui.debug(_("request %d: %s\n") %
872 (reqcnt, " ".join(map(short, r))))
875 (reqcnt, " ".join(map(short, r))))
873 for p in range(0, len(r), 10):
876 for p in range(0, len(r), 10):
874 for b in remote.branches(r[p:p+10]):
877 for b in remote.branches(r[p:p+10]):
875 self.ui.debug(_("received %s:%s\n") %
878 self.ui.debug(_("received %s:%s\n") %
876 (short(b[0]), short(b[1])))
879 (short(b[0]), short(b[1])))
877 if b[0] in m:
880 if b[0] in m:
878 self.ui.debug(_("found base node %s\n")
881 self.ui.debug(_("found base node %s\n")
879 % short(b[0]))
882 % short(b[0]))
880 base[b[0]] = 1
883 base[b[0]] = 1
881 elif b[0] not in seen:
884 elif b[0] not in seen:
882 unknown.append(b)
885 unknown.append(b)
883
886
884 # do binary search on the branches we found
887 # do binary search on the branches we found
885 while search:
888 while search:
886 n = search.pop(0)
889 n = search.pop(0)
887 reqcnt += 1
890 reqcnt += 1
888 l = remote.between([(n[0], n[1])])[0]
891 l = remote.between([(n[0], n[1])])[0]
889 l.append(n[1])
892 l.append(n[1])
890 p = n[0]
893 p = n[0]
891 f = 1
894 f = 1
892 for i in l:
895 for i in l:
893 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
896 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
894 if i in m:
897 if i in m:
895 if f <= 2:
898 if f <= 2:
896 self.ui.debug(_("found new branch changeset %s\n") %
899 self.ui.debug(_("found new branch changeset %s\n") %
897 short(p))
900 short(p))
898 fetch[p] = 1
901 fetch[p] = 1
899 base[i] = 1
902 base[i] = 1
900 else:
903 else:
901 self.ui.debug(_("narrowed branch search to %s:%s\n")
904 self.ui.debug(_("narrowed branch search to %s:%s\n")
902 % (short(p), short(i)))
905 % (short(p), short(i)))
903 search.append((p, i))
906 search.append((p, i))
904 break
907 break
905 p, f = i, f * 2
908 p, f = i, f * 2
906
909
907 # sanity check our fetch list
910 # sanity check our fetch list
908 for f in fetch.keys():
911 for f in fetch.keys():
909 if f in m:
912 if f in m:
910 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
913 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
911
914
912 if base.keys() == [nullid]:
915 if base.keys() == [nullid]:
913 if force:
916 if force:
914 self.ui.warn(_("warning: repository is unrelated\n"))
917 self.ui.warn(_("warning: repository is unrelated\n"))
915 else:
918 else:
916 raise util.Abort(_("repository is unrelated"))
919 raise util.Abort(_("repository is unrelated"))
917
920
918 self.ui.note(_("found new changesets starting at ") +
921 self.ui.note(_("found new changesets starting at ") +
919 " ".join([short(f) for f in fetch]) + "\n")
922 " ".join([short(f) for f in fetch]) + "\n")
920
923
921 self.ui.debug(_("%d total queries\n") % reqcnt)
924 self.ui.debug(_("%d total queries\n") % reqcnt)
922
925
923 return fetch.keys()
926 return fetch.keys()
924
927
925 def findoutgoing(self, remote, base=None, heads=None, force=False):
928 def findoutgoing(self, remote, base=None, heads=None, force=False):
926 """Return list of nodes that are roots of subsets not in remote
929 """Return list of nodes that are roots of subsets not in remote
927
930
928 If base dict is specified, assume that these nodes and their parents
931 If base dict is specified, assume that these nodes and their parents
929 exist on the remote side.
932 exist on the remote side.
930 If a list of heads is specified, return only nodes which are heads
933 If a list of heads is specified, return only nodes which are heads
931 or ancestors of these heads, and return a second element which
934 or ancestors of these heads, and return a second element which
932 contains all remote heads which get new children.
935 contains all remote heads which get new children.
933 """
936 """
934 if base == None:
937 if base == None:
935 base = {}
938 base = {}
936 self.findincoming(remote, base, heads, force=force)
939 self.findincoming(remote, base, heads, force=force)
937
940
938 self.ui.debug(_("common changesets up to ")
941 self.ui.debug(_("common changesets up to ")
939 + " ".join(map(short, base.keys())) + "\n")
942 + " ".join(map(short, base.keys())) + "\n")
940
943
941 remain = dict.fromkeys(self.changelog.nodemap)
944 remain = dict.fromkeys(self.changelog.nodemap)
942
945
943 # prune everything remote has from the tree
946 # prune everything remote has from the tree
944 del remain[nullid]
947 del remain[nullid]
945 remove = base.keys()
948 remove = base.keys()
946 while remove:
949 while remove:
947 n = remove.pop(0)
950 n = remove.pop(0)
948 if n in remain:
951 if n in remain:
949 del remain[n]
952 del remain[n]
950 for p in self.changelog.parents(n):
953 for p in self.changelog.parents(n):
951 remove.append(p)
954 remove.append(p)
952
955
953 # find every node whose parents have been pruned
956 # find every node whose parents have been pruned
954 subset = []
957 subset = []
955 # find every remote head that will get new children
958 # find every remote head that will get new children
956 updated_heads = {}
959 updated_heads = {}
957 for n in remain:
960 for n in remain:
958 p1, p2 = self.changelog.parents(n)
961 p1, p2 = self.changelog.parents(n)
959 if p1 not in remain and p2 not in remain:
962 if p1 not in remain and p2 not in remain:
960 subset.append(n)
963 subset.append(n)
961 if heads:
964 if heads:
962 if p1 in heads:
965 if p1 in heads:
963 updated_heads[p1] = True
966 updated_heads[p1] = True
964 if p2 in heads:
967 if p2 in heads:
965 updated_heads[p2] = True
968 updated_heads[p2] = True
966
969
967 # this is the set of all roots we have to push
970 # this is the set of all roots we have to push
968 if heads:
971 if heads:
969 return subset, updated_heads.keys()
972 return subset, updated_heads.keys()
970 else:
973 else:
971 return subset
974 return subset
972
975
973 def pull(self, remote, heads=None, force=False):
976 def pull(self, remote, heads=None, force=False):
974 l = self.lock()
977 l = self.lock()
975
978
976 # if we have an empty repo, fetch everything
979 # if we have an empty repo, fetch everything
977 if self.changelog.tip() == nullid:
980 if self.changelog.tip() == nullid:
978 self.ui.status(_("requesting all changes\n"))
981 self.ui.status(_("requesting all changes\n"))
979 fetch = [nullid]
982 fetch = [nullid]
980 else:
983 else:
981 fetch = self.findincoming(remote, force=force)
984 fetch = self.findincoming(remote, force=force)
982
985
983 if not fetch:
986 if not fetch:
984 self.ui.status(_("no changes found\n"))
987 self.ui.status(_("no changes found\n"))
985 return 0
988 return 0
986
989
987 if heads is None:
990 if heads is None:
988 cg = remote.changegroup(fetch, 'pull')
991 cg = remote.changegroup(fetch, 'pull')
989 else:
992 else:
990 cg = remote.changegroupsubset(fetch, heads, 'pull')
993 cg = remote.changegroupsubset(fetch, heads, 'pull')
991 return self.addchangegroup(cg)
994 return self.addchangegroup(cg)
992
995
993 def push(self, remote, force=False, revs=None):
996 def push(self, remote, force=False, revs=None):
994 lock = remote.lock()
997 lock = remote.lock()
995
998
996 base = {}
999 base = {}
997 remote_heads = remote.heads()
1000 remote_heads = remote.heads()
998 inc = self.findincoming(remote, base, remote_heads, force=force)
1001 inc = self.findincoming(remote, base, remote_heads, force=force)
999 if not force and inc:
1002 if not force and inc:
1000 self.ui.warn(_("abort: unsynced remote changes!\n"))
1003 self.ui.warn(_("abort: unsynced remote changes!\n"))
1001 self.ui.status(_("(did you forget to sync?"
1004 self.ui.status(_("(did you forget to sync?"
1002 " use push -f to force)\n"))
1005 " use push -f to force)\n"))
1003 return 1
1006 return 1
1004
1007
1005 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1008 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1006 if revs is not None:
1009 if revs is not None:
1007 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1010 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1008 else:
1011 else:
1009 bases, heads = update, self.changelog.heads()
1012 bases, heads = update, self.changelog.heads()
1010
1013
1011 if not bases:
1014 if not bases:
1012 self.ui.status(_("no changes found\n"))
1015 self.ui.status(_("no changes found\n"))
1013 return 1
1016 return 1
1014 elif not force:
1017 elif not force:
1015 if revs is not None:
1018 if revs is not None:
1016 updated_heads = {}
1019 updated_heads = {}
1017 for base in msng_cl:
1020 for base in msng_cl:
1018 for parent in self.changelog.parents(base):
1021 for parent in self.changelog.parents(base):
1019 if parent in remote_heads:
1022 if parent in remote_heads:
1020 updated_heads[parent] = True
1023 updated_heads[parent] = True
1021 updated_heads = updated_heads.keys()
1024 updated_heads = updated_heads.keys()
1022 if len(updated_heads) < len(heads):
1025 if len(updated_heads) < len(heads):
1023 self.ui.warn(_("abort: push creates new remote branches!\n"))
1026 self.ui.warn(_("abort: push creates new remote branches!\n"))
1024 self.ui.status(_("(did you forget to merge?"
1027 self.ui.status(_("(did you forget to merge?"
1025 " use push -f to force)\n"))
1028 " use push -f to force)\n"))
1026 return 1
1029 return 1
1027
1030
1028 if revs is None:
1031 if revs is None:
1029 cg = self.changegroup(update, 'push')
1032 cg = self.changegroup(update, 'push')
1030 else:
1033 else:
1031 cg = self.changegroupsubset(update, revs, 'push')
1034 cg = self.changegroupsubset(update, revs, 'push')
1032 return remote.addchangegroup(cg)
1035 return remote.addchangegroup(cg)
1033
1036
1034 def changegroupsubset(self, bases, heads, source):
1037 def changegroupsubset(self, bases, heads, source):
1035 """This function generates a changegroup consisting of all the nodes
1038 """This function generates a changegroup consisting of all the nodes
1036 that are descendents of any of the bases, and ancestors of any of
1039 that are descendents of any of the bases, and ancestors of any of
1037 the heads.
1040 the heads.
1038
1041
1039 It is fairly complex as determining which filenodes and which
1042 It is fairly complex as determining which filenodes and which
1040 manifest nodes need to be included for the changeset to be complete
1043 manifest nodes need to be included for the changeset to be complete
1041 is non-trivial.
1044 is non-trivial.
1042
1045
1043 Another wrinkle is doing the reverse, figuring out which changeset in
1046 Another wrinkle is doing the reverse, figuring out which changeset in
1044 the changegroup a particular filenode or manifestnode belongs to."""
1047 the changegroup a particular filenode or manifestnode belongs to."""
1045
1048
1046 self.hook('preoutgoing', throw=True, source=source)
1049 self.hook('preoutgoing', throw=True, source=source)
1047
1050
1048 # Set up some initial variables
1051 # Set up some initial variables
1049 # Make it easy to refer to self.changelog
1052 # Make it easy to refer to self.changelog
1050 cl = self.changelog
1053 cl = self.changelog
1051 # msng is short for missing - compute the list of changesets in this
1054 # msng is short for missing - compute the list of changesets in this
1052 # changegroup.
1055 # changegroup.
1053 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1056 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1054 # Some bases may turn out to be superfluous, and some heads may be
1057 # Some bases may turn out to be superfluous, and some heads may be
1055 # too. nodesbetween will return the minimal set of bases and heads
1058 # too. nodesbetween will return the minimal set of bases and heads
1056 # necessary to re-create the changegroup.
1059 # necessary to re-create the changegroup.
1057
1060
1058 # Known heads are the list of heads that it is assumed the recipient
1061 # Known heads are the list of heads that it is assumed the recipient
1059 # of this changegroup will know about.
1062 # of this changegroup will know about.
1060 knownheads = {}
1063 knownheads = {}
1061 # We assume that all parents of bases are known heads.
1064 # We assume that all parents of bases are known heads.
1062 for n in bases:
1065 for n in bases:
1063 for p in cl.parents(n):
1066 for p in cl.parents(n):
1064 if p != nullid:
1067 if p != nullid:
1065 knownheads[p] = 1
1068 knownheads[p] = 1
1066 knownheads = knownheads.keys()
1069 knownheads = knownheads.keys()
1067 if knownheads:
1070 if knownheads:
1068 # Now that we know what heads are known, we can compute which
1071 # Now that we know what heads are known, we can compute which
1069 # changesets are known. The recipient must know about all
1072 # changesets are known. The recipient must know about all
1070 # changesets required to reach the known heads from the null
1073 # changesets required to reach the known heads from the null
1071 # changeset.
1074 # changeset.
1072 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1075 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1073 junk = None
1076 junk = None
1074 # Transform the list into an ersatz set.
1077 # Transform the list into an ersatz set.
1075 has_cl_set = dict.fromkeys(has_cl_set)
1078 has_cl_set = dict.fromkeys(has_cl_set)
1076 else:
1079 else:
1077 # If there were no known heads, the recipient cannot be assumed to
1080 # If there were no known heads, the recipient cannot be assumed to
1078 # know about any changesets.
1081 # know about any changesets.
1079 has_cl_set = {}
1082 has_cl_set = {}
1080
1083
1081 # Make it easy to refer to self.manifest
1084 # Make it easy to refer to self.manifest
1082 mnfst = self.manifest
1085 mnfst = self.manifest
1083 # We don't know which manifests are missing yet
1086 # We don't know which manifests are missing yet
1084 msng_mnfst_set = {}
1087 msng_mnfst_set = {}
1085 # Nor do we know which filenodes are missing.
1088 # Nor do we know which filenodes are missing.
1086 msng_filenode_set = {}
1089 msng_filenode_set = {}
1087
1090
1088 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1091 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1089 junk = None
1092 junk = None
1090
1093
1091 # A changeset always belongs to itself, so the changenode lookup
1094 # A changeset always belongs to itself, so the changenode lookup
1092 # function for a changenode is identity.
1095 # function for a changenode is identity.
1093 def identity(x):
1096 def identity(x):
1094 return x
1097 return x
1095
1098
1096 # A function generating function. Sets up an environment for the
1099 # A function generating function. Sets up an environment for the
1097 # inner function.
1100 # inner function.
1098 def cmp_by_rev_func(revlog):
1101 def cmp_by_rev_func(revlog):
1099 # Compare two nodes by their revision number in the environment's
1102 # Compare two nodes by their revision number in the environment's
1100 # revision history. Since the revision number both represents the
1103 # revision history. Since the revision number both represents the
1101 # most efficient order to read the nodes in, and represents a
1104 # most efficient order to read the nodes in, and represents a
1102 # topological sorting of the nodes, this function is often useful.
1105 # topological sorting of the nodes, this function is often useful.
1103 def cmp_by_rev(a, b):
1106 def cmp_by_rev(a, b):
1104 return cmp(revlog.rev(a), revlog.rev(b))
1107 return cmp(revlog.rev(a), revlog.rev(b))
1105 return cmp_by_rev
1108 return cmp_by_rev
1106
1109
1107 # If we determine that a particular file or manifest node must be a
1110 # If we determine that a particular file or manifest node must be a
1108 # node that the recipient of the changegroup will already have, we can
1111 # node that the recipient of the changegroup will already have, we can
1109 # also assume the recipient will have all the parents. This function
1112 # also assume the recipient will have all the parents. This function
1110 # prunes them from the set of missing nodes.
1113 # prunes them from the set of missing nodes.
1111 def prune_parents(revlog, hasset, msngset):
1114 def prune_parents(revlog, hasset, msngset):
1112 haslst = hasset.keys()
1115 haslst = hasset.keys()
1113 haslst.sort(cmp_by_rev_func(revlog))
1116 haslst.sort(cmp_by_rev_func(revlog))
1114 for node in haslst:
1117 for node in haslst:
1115 parentlst = [p for p in revlog.parents(node) if p != nullid]
1118 parentlst = [p for p in revlog.parents(node) if p != nullid]
1116 while parentlst:
1119 while parentlst:
1117 n = parentlst.pop()
1120 n = parentlst.pop()
1118 if n not in hasset:
1121 if n not in hasset:
1119 hasset[n] = 1
1122 hasset[n] = 1
1120 p = [p for p in revlog.parents(n) if p != nullid]
1123 p = [p for p in revlog.parents(n) if p != nullid]
1121 parentlst.extend(p)
1124 parentlst.extend(p)
1122 for n in hasset:
1125 for n in hasset:
1123 msngset.pop(n, None)
1126 msngset.pop(n, None)
1124
1127
1125 # This is a function generating function used to set up an environment
1128 # This is a function generating function used to set up an environment
1126 # for the inner function to execute in.
1129 # for the inner function to execute in.
1127 def manifest_and_file_collector(changedfileset):
1130 def manifest_and_file_collector(changedfileset):
1128 # This is an information gathering function that gathers
1131 # This is an information gathering function that gathers
1129 # information from each changeset node that goes out as part of
1132 # information from each changeset node that goes out as part of
1130 # the changegroup. The information gathered is a list of which
1133 # the changegroup. The information gathered is a list of which
1131 # manifest nodes are potentially required (the recipient may
1134 # manifest nodes are potentially required (the recipient may
1132 # already have them) and total list of all files which were
1135 # already have them) and total list of all files which were
1133 # changed in any changeset in the changegroup.
1136 # changed in any changeset in the changegroup.
1134 #
1137 #
1135 # We also remember the first changenode we saw any manifest
1138 # We also remember the first changenode we saw any manifest
1136 # referenced by so we can later determine which changenode 'owns'
1139 # referenced by so we can later determine which changenode 'owns'
1137 # the manifest.
1140 # the manifest.
1138 def collect_manifests_and_files(clnode):
1141 def collect_manifests_and_files(clnode):
1139 c = cl.read(clnode)
1142 c = cl.read(clnode)
1140 for f in c[3]:
1143 for f in c[3]:
1141 # This is to make sure we only have one instance of each
1144 # This is to make sure we only have one instance of each
1142 # filename string for each filename.
1145 # filename string for each filename.
1143 changedfileset.setdefault(f, f)
1146 changedfileset.setdefault(f, f)
1144 msng_mnfst_set.setdefault(c[0], clnode)
1147 msng_mnfst_set.setdefault(c[0], clnode)
1145 return collect_manifests_and_files
1148 return collect_manifests_and_files
1146
1149
1147 # Figure out which manifest nodes (of the ones we think might be part
1150 # Figure out which manifest nodes (of the ones we think might be part
1148 # of the changegroup) the recipient must know about and remove them
1151 # of the changegroup) the recipient must know about and remove them
1149 # from the changegroup.
1152 # from the changegroup.
1150 def prune_manifests():
1153 def prune_manifests():
1151 has_mnfst_set = {}
1154 has_mnfst_set = {}
1152 for n in msng_mnfst_set:
1155 for n in msng_mnfst_set:
1153 # If a 'missing' manifest thinks it belongs to a changenode
1156 # If a 'missing' manifest thinks it belongs to a changenode
1154 # the recipient is assumed to have, obviously the recipient
1157 # the recipient is assumed to have, obviously the recipient
1155 # must have that manifest.
1158 # must have that manifest.
1156 linknode = cl.node(mnfst.linkrev(n))
1159 linknode = cl.node(mnfst.linkrev(n))
1157 if linknode in has_cl_set:
1160 if linknode in has_cl_set:
1158 has_mnfst_set[n] = 1
1161 has_mnfst_set[n] = 1
1159 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1162 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1160
1163
1161 # Use the information collected in collect_manifests_and_files to say
1164 # Use the information collected in collect_manifests_and_files to say
1162 # which changenode any manifestnode belongs to.
1165 # which changenode any manifestnode belongs to.
1163 def lookup_manifest_link(mnfstnode):
1166 def lookup_manifest_link(mnfstnode):
1164 return msng_mnfst_set[mnfstnode]
1167 return msng_mnfst_set[mnfstnode]
1165
1168
1166 # A function generating function that sets up the initial environment
1169 # A function generating function that sets up the initial environment
1167 # the inner function.
1170 # the inner function.
1168 def filenode_collector(changedfiles):
1171 def filenode_collector(changedfiles):
1169 next_rev = [0]
1172 next_rev = [0]
1170 # This gathers information from each manifestnode included in the
1173 # This gathers information from each manifestnode included in the
1171 # changegroup about which filenodes the manifest node references
1174 # changegroup about which filenodes the manifest node references
1172 # so we can include those in the changegroup too.
1175 # so we can include those in the changegroup too.
1173 #
1176 #
1174 # It also remembers which changenode each filenode belongs to. It
1177 # It also remembers which changenode each filenode belongs to. It
1175 # does this by assuming the a filenode belongs to the changenode
1178 # does this by assuming the a filenode belongs to the changenode
1176 # the first manifest that references it belongs to.
1179 # the first manifest that references it belongs to.
1177 def collect_msng_filenodes(mnfstnode):
1180 def collect_msng_filenodes(mnfstnode):
1178 r = mnfst.rev(mnfstnode)
1181 r = mnfst.rev(mnfstnode)
1179 if r == next_rev[0]:
1182 if r == next_rev[0]:
1180 # If the last rev we looked at was the one just previous,
1183 # If the last rev we looked at was the one just previous,
1181 # we only need to see a diff.
1184 # we only need to see a diff.
1182 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1185 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1183 # For each line in the delta
1186 # For each line in the delta
1184 for dline in delta.splitlines():
1187 for dline in delta.splitlines():
1185 # get the filename and filenode for that line
1188 # get the filename and filenode for that line
1186 f, fnode = dline.split('\0')
1189 f, fnode = dline.split('\0')
1187 fnode = bin(fnode[:40])
1190 fnode = bin(fnode[:40])
1188 f = changedfiles.get(f, None)
1191 f = changedfiles.get(f, None)
1189 # And if the file is in the list of files we care
1192 # And if the file is in the list of files we care
1190 # about.
1193 # about.
1191 if f is not None:
1194 if f is not None:
1192 # Get the changenode this manifest belongs to
1195 # Get the changenode this manifest belongs to
1193 clnode = msng_mnfst_set[mnfstnode]
1196 clnode = msng_mnfst_set[mnfstnode]
1194 # Create the set of filenodes for the file if
1197 # Create the set of filenodes for the file if
1195 # there isn't one already.
1198 # there isn't one already.
1196 ndset = msng_filenode_set.setdefault(f, {})
1199 ndset = msng_filenode_set.setdefault(f, {})
1197 # And set the filenode's changelog node to the
1200 # And set the filenode's changelog node to the
1198 # manifest's if it hasn't been set already.
1201 # manifest's if it hasn't been set already.
1199 ndset.setdefault(fnode, clnode)
1202 ndset.setdefault(fnode, clnode)
1200 else:
1203 else:
1201 # Otherwise we need a full manifest.
1204 # Otherwise we need a full manifest.
1202 m = mnfst.read(mnfstnode)
1205 m = mnfst.read(mnfstnode)
1203 # For every file in we care about.
1206 # For every file in we care about.
1204 for f in changedfiles:
1207 for f in changedfiles:
1205 fnode = m.get(f, None)
1208 fnode = m.get(f, None)
1206 # If it's in the manifest
1209 # If it's in the manifest
1207 if fnode is not None:
1210 if fnode is not None:
1208 # See comments above.
1211 # See comments above.
1209 clnode = msng_mnfst_set[mnfstnode]
1212 clnode = msng_mnfst_set[mnfstnode]
1210 ndset = msng_filenode_set.setdefault(f, {})
1213 ndset = msng_filenode_set.setdefault(f, {})
1211 ndset.setdefault(fnode, clnode)
1214 ndset.setdefault(fnode, clnode)
1212 # Remember the revision we hope to see next.
1215 # Remember the revision we hope to see next.
1213 next_rev[0] = r + 1
1216 next_rev[0] = r + 1
1214 return collect_msng_filenodes
1217 return collect_msng_filenodes
1215
1218
1216 # We have a list of filenodes we think we need for a file, lets remove
1219 # We have a list of filenodes we think we need for a file, lets remove
1217 # all those we now the recipient must have.
1220 # all those we now the recipient must have.
1218 def prune_filenodes(f, filerevlog):
1221 def prune_filenodes(f, filerevlog):
1219 msngset = msng_filenode_set[f]
1222 msngset = msng_filenode_set[f]
1220 hasset = {}
1223 hasset = {}
1221 # If a 'missing' filenode thinks it belongs to a changenode we
1224 # If a 'missing' filenode thinks it belongs to a changenode we
1222 # assume the recipient must have, then the recipient must have
1225 # assume the recipient must have, then the recipient must have
1223 # that filenode.
1226 # that filenode.
1224 for n in msngset:
1227 for n in msngset:
1225 clnode = cl.node(filerevlog.linkrev(n))
1228 clnode = cl.node(filerevlog.linkrev(n))
1226 if clnode in has_cl_set:
1229 if clnode in has_cl_set:
1227 hasset[n] = 1
1230 hasset[n] = 1
1228 prune_parents(filerevlog, hasset, msngset)
1231 prune_parents(filerevlog, hasset, msngset)
1229
1232
1230 # A function generator function that sets up the a context for the
1233 # A function generator function that sets up the a context for the
1231 # inner function.
1234 # inner function.
1232 def lookup_filenode_link_func(fname):
1235 def lookup_filenode_link_func(fname):
1233 msngset = msng_filenode_set[fname]
1236 msngset = msng_filenode_set[fname]
1234 # Lookup the changenode the filenode belongs to.
1237 # Lookup the changenode the filenode belongs to.
1235 def lookup_filenode_link(fnode):
1238 def lookup_filenode_link(fnode):
1236 return msngset[fnode]
1239 return msngset[fnode]
1237 return lookup_filenode_link
1240 return lookup_filenode_link
1238
1241
1239 # Now that we have all theses utility functions to help out and
1242 # Now that we have all theses utility functions to help out and
1240 # logically divide up the task, generate the group.
1243 # logically divide up the task, generate the group.
1241 def gengroup():
1244 def gengroup():
1242 # The set of changed files starts empty.
1245 # The set of changed files starts empty.
1243 changedfiles = {}
1246 changedfiles = {}
1244 # Create a changenode group generator that will call our functions
1247 # Create a changenode group generator that will call our functions
1245 # back to lookup the owning changenode and collect information.
1248 # back to lookup the owning changenode and collect information.
1246 group = cl.group(msng_cl_lst, identity,
1249 group = cl.group(msng_cl_lst, identity,
1247 manifest_and_file_collector(changedfiles))
1250 manifest_and_file_collector(changedfiles))
1248 for chnk in group:
1251 for chnk in group:
1249 yield chnk
1252 yield chnk
1250
1253
1251 # The list of manifests has been collected by the generator
1254 # The list of manifests has been collected by the generator
1252 # calling our functions back.
1255 # calling our functions back.
1253 prune_manifests()
1256 prune_manifests()
1254 msng_mnfst_lst = msng_mnfst_set.keys()
1257 msng_mnfst_lst = msng_mnfst_set.keys()
1255 # Sort the manifestnodes by revision number.
1258 # Sort the manifestnodes by revision number.
1256 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1259 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1257 # Create a generator for the manifestnodes that calls our lookup
1260 # Create a generator for the manifestnodes that calls our lookup
1258 # and data collection functions back.
1261 # and data collection functions back.
1259 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1262 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1260 filenode_collector(changedfiles))
1263 filenode_collector(changedfiles))
1261 for chnk in group:
1264 for chnk in group:
1262 yield chnk
1265 yield chnk
1263
1266
1264 # These are no longer needed, dereference and toss the memory for
1267 # These are no longer needed, dereference and toss the memory for
1265 # them.
1268 # them.
1266 msng_mnfst_lst = None
1269 msng_mnfst_lst = None
1267 msng_mnfst_set.clear()
1270 msng_mnfst_set.clear()
1268
1271
1269 changedfiles = changedfiles.keys()
1272 changedfiles = changedfiles.keys()
1270 changedfiles.sort()
1273 changedfiles.sort()
1271 # Go through all our files in order sorted by name.
1274 # Go through all our files in order sorted by name.
1272 for fname in changedfiles:
1275 for fname in changedfiles:
1273 filerevlog = self.file(fname)
1276 filerevlog = self.file(fname)
1274 # Toss out the filenodes that the recipient isn't really
1277 # Toss out the filenodes that the recipient isn't really
1275 # missing.
1278 # missing.
1276 if msng_filenode_set.has_key(fname):
1279 if msng_filenode_set.has_key(fname):
1277 prune_filenodes(fname, filerevlog)
1280 prune_filenodes(fname, filerevlog)
1278 msng_filenode_lst = msng_filenode_set[fname].keys()
1281 msng_filenode_lst = msng_filenode_set[fname].keys()
1279 else:
1282 else:
1280 msng_filenode_lst = []
1283 msng_filenode_lst = []
1281 # If any filenodes are left, generate the group for them,
1284 # If any filenodes are left, generate the group for them,
1282 # otherwise don't bother.
1285 # otherwise don't bother.
1283 if len(msng_filenode_lst) > 0:
1286 if len(msng_filenode_lst) > 0:
1284 yield changegroup.genchunk(fname)
1287 yield changegroup.genchunk(fname)
1285 # Sort the filenodes by their revision #
1288 # Sort the filenodes by their revision #
1286 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1289 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1287 # Create a group generator and only pass in a changenode
1290 # Create a group generator and only pass in a changenode
1288 # lookup function as we need to collect no information
1291 # lookup function as we need to collect no information
1289 # from filenodes.
1292 # from filenodes.
1290 group = filerevlog.group(msng_filenode_lst,
1293 group = filerevlog.group(msng_filenode_lst,
1291 lookup_filenode_link_func(fname))
1294 lookup_filenode_link_func(fname))
1292 for chnk in group:
1295 for chnk in group:
1293 yield chnk
1296 yield chnk
1294 if msng_filenode_set.has_key(fname):
1297 if msng_filenode_set.has_key(fname):
1295 # Don't need this anymore, toss it to free memory.
1298 # Don't need this anymore, toss it to free memory.
1296 del msng_filenode_set[fname]
1299 del msng_filenode_set[fname]
1297 # Signal that no more groups are left.
1300 # Signal that no more groups are left.
1298 yield changegroup.closechunk()
1301 yield changegroup.closechunk()
1299
1302
1300 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1303 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1301
1304
1302 return util.chunkbuffer(gengroup())
1305 return util.chunkbuffer(gengroup())
1303
1306
1304 def changegroup(self, basenodes, source):
1307 def changegroup(self, basenodes, source):
1305 """Generate a changegroup of all nodes that we have that a recipient
1308 """Generate a changegroup of all nodes that we have that a recipient
1306 doesn't.
1309 doesn't.
1307
1310
1308 This is much easier than the previous function as we can assume that
1311 This is much easier than the previous function as we can assume that
1309 the recipient has any changenode we aren't sending them."""
1312 the recipient has any changenode we aren't sending them."""
1310
1313
1311 self.hook('preoutgoing', throw=True, source=source)
1314 self.hook('preoutgoing', throw=True, source=source)
1312
1315
1313 cl = self.changelog
1316 cl = self.changelog
1314 nodes = cl.nodesbetween(basenodes, None)[0]
1317 nodes = cl.nodesbetween(basenodes, None)[0]
1315 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1318 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1316
1319
1317 def identity(x):
1320 def identity(x):
1318 return x
1321 return x
1319
1322
1320 def gennodelst(revlog):
1323 def gennodelst(revlog):
1321 for r in xrange(0, revlog.count()):
1324 for r in xrange(0, revlog.count()):
1322 n = revlog.node(r)
1325 n = revlog.node(r)
1323 if revlog.linkrev(n) in revset:
1326 if revlog.linkrev(n) in revset:
1324 yield n
1327 yield n
1325
1328
1326 def changed_file_collector(changedfileset):
1329 def changed_file_collector(changedfileset):
1327 def collect_changed_files(clnode):
1330 def collect_changed_files(clnode):
1328 c = cl.read(clnode)
1331 c = cl.read(clnode)
1329 for fname in c[3]:
1332 for fname in c[3]:
1330 changedfileset[fname] = 1
1333 changedfileset[fname] = 1
1331 return collect_changed_files
1334 return collect_changed_files
1332
1335
1333 def lookuprevlink_func(revlog):
1336 def lookuprevlink_func(revlog):
1334 def lookuprevlink(n):
1337 def lookuprevlink(n):
1335 return cl.node(revlog.linkrev(n))
1338 return cl.node(revlog.linkrev(n))
1336 return lookuprevlink
1339 return lookuprevlink
1337
1340
1338 def gengroup():
1341 def gengroup():
1339 # construct a list of all changed files
1342 # construct a list of all changed files
1340 changedfiles = {}
1343 changedfiles = {}
1341
1344
1342 for chnk in cl.group(nodes, identity,
1345 for chnk in cl.group(nodes, identity,
1343 changed_file_collector(changedfiles)):
1346 changed_file_collector(changedfiles)):
1344 yield chnk
1347 yield chnk
1345 changedfiles = changedfiles.keys()
1348 changedfiles = changedfiles.keys()
1346 changedfiles.sort()
1349 changedfiles.sort()
1347
1350
1348 mnfst = self.manifest
1351 mnfst = self.manifest
1349 nodeiter = gennodelst(mnfst)
1352 nodeiter = gennodelst(mnfst)
1350 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1353 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1351 yield chnk
1354 yield chnk
1352
1355
1353 for fname in changedfiles:
1356 for fname in changedfiles:
1354 filerevlog = self.file(fname)
1357 filerevlog = self.file(fname)
1355 nodeiter = gennodelst(filerevlog)
1358 nodeiter = gennodelst(filerevlog)
1356 nodeiter = list(nodeiter)
1359 nodeiter = list(nodeiter)
1357 if nodeiter:
1360 if nodeiter:
1358 yield changegroup.genchunk(fname)
1361 yield changegroup.genchunk(fname)
1359 lookup = lookuprevlink_func(filerevlog)
1362 lookup = lookuprevlink_func(filerevlog)
1360 for chnk in filerevlog.group(nodeiter, lookup):
1363 for chnk in filerevlog.group(nodeiter, lookup):
1361 yield chnk
1364 yield chnk
1362
1365
1363 yield changegroup.closechunk()
1366 yield changegroup.closechunk()
1364 self.hook('outgoing', node=hex(nodes[0]), source=source)
1367 self.hook('outgoing', node=hex(nodes[0]), source=source)
1365
1368
1366 return util.chunkbuffer(gengroup())
1369 return util.chunkbuffer(gengroup())
1367
1370
1368 def addchangegroup(self, source):
1371 def addchangegroup(self, source):
1369 """add changegroup to repo.
1372 """add changegroup to repo.
1370 returns number of heads modified or added + 1."""
1373 returns number of heads modified or added + 1."""
1371
1374
1372 def csmap(x):
1375 def csmap(x):
1373 self.ui.debug(_("add changeset %s\n") % short(x))
1376 self.ui.debug(_("add changeset %s\n") % short(x))
1374 return cl.count()
1377 return cl.count()
1375
1378
1376 def revmap(x):
1379 def revmap(x):
1377 return cl.rev(x)
1380 return cl.rev(x)
1378
1381
1379 if not source:
1382 if not source:
1380 return 0
1383 return 0
1381
1384
1382 self.hook('prechangegroup', throw=True)
1385 self.hook('prechangegroup', throw=True)
1383
1386
1384 changesets = files = revisions = 0
1387 changesets = files = revisions = 0
1385
1388
1386 tr = self.transaction()
1389 tr = self.transaction()
1387
1390
1388 # write changelog and manifest data to temp files so
1391 # write changelog and manifest data to temp files so
1389 # concurrent readers will not see inconsistent view
1392 # concurrent readers will not see inconsistent view
1390 cl = appendfile.appendchangelog(self.opener)
1393 cl = appendfile.appendchangelog(self.opener)
1391
1394
1392 oldheads = len(cl.heads())
1395 oldheads = len(cl.heads())
1393
1396
1394 # pull off the changeset group
1397 # pull off the changeset group
1395 self.ui.status(_("adding changesets\n"))
1398 self.ui.status(_("adding changesets\n"))
1396 co = cl.tip()
1399 co = cl.tip()
1397 chunkiter = changegroup.chunkiter(source)
1400 chunkiter = changegroup.chunkiter(source)
1398 cn = cl.addgroup(chunkiter, csmap, tr, 1) # unique
1401 cn = cl.addgroup(chunkiter, csmap, tr, 1) # unique
1399 cnr, cor = map(cl.rev, (cn, co))
1402 cnr, cor = map(cl.rev, (cn, co))
1400 if cn == nullid:
1403 if cn == nullid:
1401 cnr = cor
1404 cnr = cor
1402 changesets = cnr - cor
1405 changesets = cnr - cor
1403
1406
1404 mf = appendfile.appendmanifest(self.opener)
1407 mf = appendfile.appendmanifest(self.opener)
1405
1408
1406 # pull off the manifest group
1409 # pull off the manifest group
1407 self.ui.status(_("adding manifests\n"))
1410 self.ui.status(_("adding manifests\n"))
1408 mm = mf.tip()
1411 mm = mf.tip()
1409 chunkiter = changegroup.chunkiter(source)
1412 chunkiter = changegroup.chunkiter(source)
1410 mo = mf.addgroup(chunkiter, revmap, tr)
1413 mo = mf.addgroup(chunkiter, revmap, tr)
1411
1414
1412 # process the files
1415 # process the files
1413 self.ui.status(_("adding file changes\n"))
1416 self.ui.status(_("adding file changes\n"))
1414 while 1:
1417 while 1:
1415 f = changegroup.getchunk(source)
1418 f = changegroup.getchunk(source)
1416 if not f:
1419 if not f:
1417 break
1420 break
1418 self.ui.debug(_("adding %s revisions\n") % f)
1421 self.ui.debug(_("adding %s revisions\n") % f)
1419 fl = self.file(f)
1422 fl = self.file(f)
1420 o = fl.count()
1423 o = fl.count()
1421 chunkiter = changegroup.chunkiter(source)
1424 chunkiter = changegroup.chunkiter(source)
1422 n = fl.addgroup(chunkiter, revmap, tr)
1425 n = fl.addgroup(chunkiter, revmap, tr)
1423 revisions += fl.count() - o
1426 revisions += fl.count() - o
1424 files += 1
1427 files += 1
1425
1428
1426 # write order here is important so concurrent readers will see
1429 # write order here is important so concurrent readers will see
1427 # consistent view of repo
1430 # consistent view of repo
1428 mf.writedata()
1431 mf.writedata()
1429 cl.writedata()
1432 cl.writedata()
1430
1433
1431 # make changelog and manifest see real files again
1434 # make changelog and manifest see real files again
1432 self.changelog = changelog.changelog(self.opener)
1435 self.changelog = changelog.changelog(self.opener)
1433 self.manifest = manifest.manifest(self.opener)
1436 self.manifest = manifest.manifest(self.opener)
1434
1437
1435 newheads = len(self.changelog.heads())
1438 newheads = len(self.changelog.heads())
1436 heads = ""
1439 heads = ""
1437 if oldheads and newheads > oldheads:
1440 if oldheads and newheads > oldheads:
1438 heads = _(" (+%d heads)") % (newheads - oldheads)
1441 heads = _(" (+%d heads)") % (newheads - oldheads)
1439
1442
1440 self.ui.status(_("added %d changesets"
1443 self.ui.status(_("added %d changesets"
1441 " with %d changes to %d files%s\n")
1444 " with %d changes to %d files%s\n")
1442 % (changesets, revisions, files, heads))
1445 % (changesets, revisions, files, heads))
1443
1446
1444 self.hook('pretxnchangegroup', throw=True,
1447 self.hook('pretxnchangegroup', throw=True,
1445 node=hex(self.changelog.node(cor+1)))
1448 node=hex(self.changelog.node(cor+1)))
1446
1449
1447 tr.close()
1450 tr.close()
1448
1451
1449 if changesets > 0:
1452 if changesets > 0:
1450 self.hook("changegroup", node=hex(self.changelog.node(cor+1)))
1453 self.hook("changegroup", node=hex(self.changelog.node(cor+1)))
1451
1454
1452 for i in range(cor + 1, cnr + 1):
1455 for i in range(cor + 1, cnr + 1):
1453 self.hook("incoming", node=hex(self.changelog.node(i)))
1456 self.hook("incoming", node=hex(self.changelog.node(i)))
1454
1457
1455 return newheads - oldheads + 1
1458 return newheads - oldheads + 1
1456
1459
1457 def update(self, node, allow=False, force=False, choose=None,
1460 def update(self, node, allow=False, force=False, choose=None,
1458 moddirstate=True, forcemerge=False, wlock=None):
1461 moddirstate=True, forcemerge=False, wlock=None):
1459 pl = self.dirstate.parents()
1462 pl = self.dirstate.parents()
1460 if not force and pl[1] != nullid:
1463 if not force and pl[1] != nullid:
1461 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1464 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1462 return 1
1465 return 1
1463
1466
1464 err = False
1467 err = False
1465
1468
1466 p1, p2 = pl[0], node
1469 p1, p2 = pl[0], node
1467 pa = self.changelog.ancestor(p1, p2)
1470 pa = self.changelog.ancestor(p1, p2)
1468 m1n = self.changelog.read(p1)[0]
1471 m1n = self.changelog.read(p1)[0]
1469 m2n = self.changelog.read(p2)[0]
1472 m2n = self.changelog.read(p2)[0]
1470 man = self.manifest.ancestor(m1n, m2n)
1473 man = self.manifest.ancestor(m1n, m2n)
1471 m1 = self.manifest.read(m1n)
1474 m1 = self.manifest.read(m1n)
1472 mf1 = self.manifest.readflags(m1n)
1475 mf1 = self.manifest.readflags(m1n)
1473 m2 = self.manifest.read(m2n).copy()
1476 m2 = self.manifest.read(m2n).copy()
1474 mf2 = self.manifest.readflags(m2n)
1477 mf2 = self.manifest.readflags(m2n)
1475 ma = self.manifest.read(man)
1478 ma = self.manifest.read(man)
1476 mfa = self.manifest.readflags(man)
1479 mfa = self.manifest.readflags(man)
1477
1480
1478 modified, added, removed, deleted, unknown = self.changes()
1481 modified, added, removed, deleted, unknown = self.changes()
1479
1482
1480 # is this a jump, or a merge? i.e. is there a linear path
1483 # is this a jump, or a merge? i.e. is there a linear path
1481 # from p1 to p2?
1484 # from p1 to p2?
1482 linear_path = (pa == p1 or pa == p2)
1485 linear_path = (pa == p1 or pa == p2)
1483
1486
1484 if allow and linear_path:
1487 if allow and linear_path:
1485 raise util.Abort(_("there is nothing to merge, "
1488 raise util.Abort(_("there is nothing to merge, "
1486 "just use 'hg update'"))
1489 "just use 'hg update'"))
1487 if allow and not forcemerge:
1490 if allow and not forcemerge:
1488 if modified or added or removed:
1491 if modified or added or removed:
1489 raise util.Abort(_("outstanding uncommitted changes"))
1492 raise util.Abort(_("outstanding uncommitted changes"))
1490 if not forcemerge and not force:
1493 if not forcemerge and not force:
1491 for f in unknown:
1494 for f in unknown:
1492 if f in m2:
1495 if f in m2:
1493 t1 = self.wread(f)
1496 t1 = self.wread(f)
1494 t2 = self.file(f).read(m2[f])
1497 t2 = self.file(f).read(m2[f])
1495 if cmp(t1, t2) != 0:
1498 if cmp(t1, t2) != 0:
1496 raise util.Abort(_("'%s' already exists in the working"
1499 raise util.Abort(_("'%s' already exists in the working"
1497 " dir and differs from remote") % f)
1500 " dir and differs from remote") % f)
1498
1501
1499 # resolve the manifest to determine which files
1502 # resolve the manifest to determine which files
1500 # we care about merging
1503 # we care about merging
1501 self.ui.note(_("resolving manifests\n"))
1504 self.ui.note(_("resolving manifests\n"))
1502 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1505 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1503 (force, allow, moddirstate, linear_path))
1506 (force, allow, moddirstate, linear_path))
1504 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1507 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1505 (short(man), short(m1n), short(m2n)))
1508 (short(man), short(m1n), short(m2n)))
1506
1509
1507 merge = {}
1510 merge = {}
1508 get = {}
1511 get = {}
1509 remove = []
1512 remove = []
1510
1513
1511 # construct a working dir manifest
1514 # construct a working dir manifest
1512 mw = m1.copy()
1515 mw = m1.copy()
1513 mfw = mf1.copy()
1516 mfw = mf1.copy()
1514 umap = dict.fromkeys(unknown)
1517 umap = dict.fromkeys(unknown)
1515
1518
1516 for f in added + modified + unknown:
1519 for f in added + modified + unknown:
1517 mw[f] = ""
1520 mw[f] = ""
1518 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1521 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1519
1522
1520 if moddirstate and not wlock:
1523 if moddirstate and not wlock:
1521 wlock = self.wlock()
1524 wlock = self.wlock()
1522
1525
1523 for f in deleted + removed:
1526 for f in deleted + removed:
1524 if f in mw:
1527 if f in mw:
1525 del mw[f]
1528 del mw[f]
1526
1529
1527 # If we're jumping between revisions (as opposed to merging),
1530 # If we're jumping between revisions (as opposed to merging),
1528 # and if neither the working directory nor the target rev has
1531 # and if neither the working directory nor the target rev has
1529 # the file, then we need to remove it from the dirstate, to
1532 # the file, then we need to remove it from the dirstate, to
1530 # prevent the dirstate from listing the file when it is no
1533 # prevent the dirstate from listing the file when it is no
1531 # longer in the manifest.
1534 # longer in the manifest.
1532 if moddirstate and linear_path and f not in m2:
1535 if moddirstate and linear_path and f not in m2:
1533 self.dirstate.forget((f,))
1536 self.dirstate.forget((f,))
1534
1537
1535 # Compare manifests
1538 # Compare manifests
1536 for f, n in mw.iteritems():
1539 for f, n in mw.iteritems():
1537 if choose and not choose(f):
1540 if choose and not choose(f):
1538 continue
1541 continue
1539 if f in m2:
1542 if f in m2:
1540 s = 0
1543 s = 0
1541
1544
1542 # is the wfile new since m1, and match m2?
1545 # is the wfile new since m1, and match m2?
1543 if f not in m1:
1546 if f not in m1:
1544 t1 = self.wread(f)
1547 t1 = self.wread(f)
1545 t2 = self.file(f).read(m2[f])
1548 t2 = self.file(f).read(m2[f])
1546 if cmp(t1, t2) == 0:
1549 if cmp(t1, t2) == 0:
1547 n = m2[f]
1550 n = m2[f]
1548 del t1, t2
1551 del t1, t2
1549
1552
1550 # are files different?
1553 # are files different?
1551 if n != m2[f]:
1554 if n != m2[f]:
1552 a = ma.get(f, nullid)
1555 a = ma.get(f, nullid)
1553 # are both different from the ancestor?
1556 # are both different from the ancestor?
1554 if n != a and m2[f] != a:
1557 if n != a and m2[f] != a:
1555 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1558 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1556 # merge executable bits
1559 # merge executable bits
1557 # "if we changed or they changed, change in merge"
1560 # "if we changed or they changed, change in merge"
1558 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1561 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1559 mode = ((a^b) | (a^c)) ^ a
1562 mode = ((a^b) | (a^c)) ^ a
1560 merge[f] = (m1.get(f, nullid), m2[f], mode)
1563 merge[f] = (m1.get(f, nullid), m2[f], mode)
1561 s = 1
1564 s = 1
1562 # are we clobbering?
1565 # are we clobbering?
1563 # is remote's version newer?
1566 # is remote's version newer?
1564 # or are we going back in time?
1567 # or are we going back in time?
1565 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1568 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1566 self.ui.debug(_(" remote %s is newer, get\n") % f)
1569 self.ui.debug(_(" remote %s is newer, get\n") % f)
1567 get[f] = m2[f]
1570 get[f] = m2[f]
1568 s = 1
1571 s = 1
1569 elif f in umap:
1572 elif f in umap:
1570 # this unknown file is the same as the checkout
1573 # this unknown file is the same as the checkout
1571 get[f] = m2[f]
1574 get[f] = m2[f]
1572
1575
1573 if not s and mfw[f] != mf2[f]:
1576 if not s and mfw[f] != mf2[f]:
1574 if force:
1577 if force:
1575 self.ui.debug(_(" updating permissions for %s\n") % f)
1578 self.ui.debug(_(" updating permissions for %s\n") % f)
1576 util.set_exec(self.wjoin(f), mf2[f])
1579 util.set_exec(self.wjoin(f), mf2[f])
1577 else:
1580 else:
1578 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1581 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1579 mode = ((a^b) | (a^c)) ^ a
1582 mode = ((a^b) | (a^c)) ^ a
1580 if mode != b:
1583 if mode != b:
1581 self.ui.debug(_(" updating permissions for %s\n")
1584 self.ui.debug(_(" updating permissions for %s\n")
1582 % f)
1585 % f)
1583 util.set_exec(self.wjoin(f), mode)
1586 util.set_exec(self.wjoin(f), mode)
1584 del m2[f]
1587 del m2[f]
1585 elif f in ma:
1588 elif f in ma:
1586 if n != ma[f]:
1589 if n != ma[f]:
1587 r = _("d")
1590 r = _("d")
1588 if not force and (linear_path or allow):
1591 if not force and (linear_path or allow):
1589 r = self.ui.prompt(
1592 r = self.ui.prompt(
1590 (_(" local changed %s which remote deleted\n") % f) +
1593 (_(" local changed %s which remote deleted\n") % f) +
1591 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1594 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1592 if r == _("d"):
1595 if r == _("d"):
1593 remove.append(f)
1596 remove.append(f)
1594 else:
1597 else:
1595 self.ui.debug(_("other deleted %s\n") % f)
1598 self.ui.debug(_("other deleted %s\n") % f)
1596 remove.append(f) # other deleted it
1599 remove.append(f) # other deleted it
1597 else:
1600 else:
1598 # file is created on branch or in working directory
1601 # file is created on branch or in working directory
1599 if force and f not in umap:
1602 if force and f not in umap:
1600 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1603 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1601 remove.append(f)
1604 remove.append(f)
1602 elif n == m1.get(f, nullid): # same as parent
1605 elif n == m1.get(f, nullid): # same as parent
1603 if p2 == pa: # going backwards?
1606 if p2 == pa: # going backwards?
1604 self.ui.debug(_("remote deleted %s\n") % f)
1607 self.ui.debug(_("remote deleted %s\n") % f)
1605 remove.append(f)
1608 remove.append(f)
1606 else:
1609 else:
1607 self.ui.debug(_("local modified %s, keeping\n") % f)
1610 self.ui.debug(_("local modified %s, keeping\n") % f)
1608 else:
1611 else:
1609 self.ui.debug(_("working dir created %s, keeping\n") % f)
1612 self.ui.debug(_("working dir created %s, keeping\n") % f)
1610
1613
1611 for f, n in m2.iteritems():
1614 for f, n in m2.iteritems():
1612 if choose and not choose(f):
1615 if choose and not choose(f):
1613 continue
1616 continue
1614 if f[0] == "/":
1617 if f[0] == "/":
1615 continue
1618 continue
1616 if f in ma and n != ma[f]:
1619 if f in ma and n != ma[f]:
1617 r = _("k")
1620 r = _("k")
1618 if not force and (linear_path or allow):
1621 if not force and (linear_path or allow):
1619 r = self.ui.prompt(
1622 r = self.ui.prompt(
1620 (_("remote changed %s which local deleted\n") % f) +
1623 (_("remote changed %s which local deleted\n") % f) +
1621 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1624 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1622 if r == _("k"):
1625 if r == _("k"):
1623 get[f] = n
1626 get[f] = n
1624 elif f not in ma:
1627 elif f not in ma:
1625 self.ui.debug(_("remote created %s\n") % f)
1628 self.ui.debug(_("remote created %s\n") % f)
1626 get[f] = n
1629 get[f] = n
1627 else:
1630 else:
1628 if force or p2 == pa: # going backwards?
1631 if force or p2 == pa: # going backwards?
1629 self.ui.debug(_("local deleted %s, recreating\n") % f)
1632 self.ui.debug(_("local deleted %s, recreating\n") % f)
1630 get[f] = n
1633 get[f] = n
1631 else:
1634 else:
1632 self.ui.debug(_("local deleted %s\n") % f)
1635 self.ui.debug(_("local deleted %s\n") % f)
1633
1636
1634 del mw, m1, m2, ma
1637 del mw, m1, m2, ma
1635
1638
1636 if force:
1639 if force:
1637 for f in merge:
1640 for f in merge:
1638 get[f] = merge[f][1]
1641 get[f] = merge[f][1]
1639 merge = {}
1642 merge = {}
1640
1643
1641 if linear_path or force:
1644 if linear_path or force:
1642 # we don't need to do any magic, just jump to the new rev
1645 # we don't need to do any magic, just jump to the new rev
1643 branch_merge = False
1646 branch_merge = False
1644 p1, p2 = p2, nullid
1647 p1, p2 = p2, nullid
1645 else:
1648 else:
1646 if not allow:
1649 if not allow:
1647 self.ui.status(_("this update spans a branch"
1650 self.ui.status(_("this update spans a branch"
1648 " affecting the following files:\n"))
1651 " affecting the following files:\n"))
1649 fl = merge.keys() + get.keys()
1652 fl = merge.keys() + get.keys()
1650 fl.sort()
1653 fl.sort()
1651 for f in fl:
1654 for f in fl:
1652 cf = ""
1655 cf = ""
1653 if f in merge:
1656 if f in merge:
1654 cf = _(" (resolve)")
1657 cf = _(" (resolve)")
1655 self.ui.status(" %s%s\n" % (f, cf))
1658 self.ui.status(" %s%s\n" % (f, cf))
1656 self.ui.warn(_("aborting update spanning branches!\n"))
1659 self.ui.warn(_("aborting update spanning branches!\n"))
1657 self.ui.status(_("(use 'hg merge' to merge across branches"
1660 self.ui.status(_("(use 'hg merge' to merge across branches"
1658 " or '-C' to lose changes)\n"))
1661 " or '-C' to lose changes)\n"))
1659 return 1
1662 return 1
1660 branch_merge = True
1663 branch_merge = True
1661
1664
1662 # get the files we don't need to change
1665 # get the files we don't need to change
1663 files = get.keys()
1666 files = get.keys()
1664 files.sort()
1667 files.sort()
1665 for f in files:
1668 for f in files:
1666 if f[0] == "/":
1669 if f[0] == "/":
1667 continue
1670 continue
1668 self.ui.note(_("getting %s\n") % f)
1671 self.ui.note(_("getting %s\n") % f)
1669 t = self.file(f).read(get[f])
1672 t = self.file(f).read(get[f])
1670 self.wwrite(f, t)
1673 self.wwrite(f, t)
1671 util.set_exec(self.wjoin(f), mf2[f])
1674 util.set_exec(self.wjoin(f), mf2[f])
1672 if moddirstate:
1675 if moddirstate:
1673 if branch_merge:
1676 if branch_merge:
1674 self.dirstate.update([f], 'n', st_mtime=-1)
1677 self.dirstate.update([f], 'n', st_mtime=-1)
1675 else:
1678 else:
1676 self.dirstate.update([f], 'n')
1679 self.dirstate.update([f], 'n')
1677
1680
1678 # merge the tricky bits
1681 # merge the tricky bits
1679 failedmerge = []
1682 failedmerge = []
1680 files = merge.keys()
1683 files = merge.keys()
1681 files.sort()
1684 files.sort()
1682 xp1 = hex(p1)
1685 xp1 = hex(p1)
1683 xp2 = hex(p2)
1686 xp2 = hex(p2)
1684 for f in files:
1687 for f in files:
1685 self.ui.status(_("merging %s\n") % f)
1688 self.ui.status(_("merging %s\n") % f)
1686 my, other, flag = merge[f]
1689 my, other, flag = merge[f]
1687 ret = self.merge3(f, my, other, xp1, xp2)
1690 ret = self.merge3(f, my, other, xp1, xp2)
1688 if ret:
1691 if ret:
1689 err = True
1692 err = True
1690 failedmerge.append(f)
1693 failedmerge.append(f)
1691 util.set_exec(self.wjoin(f), flag)
1694 util.set_exec(self.wjoin(f), flag)
1692 if moddirstate:
1695 if moddirstate:
1693 if branch_merge:
1696 if branch_merge:
1694 # We've done a branch merge, mark this file as merged
1697 # We've done a branch merge, mark this file as merged
1695 # so that we properly record the merger later
1698 # so that we properly record the merger later
1696 self.dirstate.update([f], 'm')
1699 self.dirstate.update([f], 'm')
1697 else:
1700 else:
1698 # We've update-merged a locally modified file, so
1701 # We've update-merged a locally modified file, so
1699 # we set the dirstate to emulate a normal checkout
1702 # we set the dirstate to emulate a normal checkout
1700 # of that file some time in the past. Thus our
1703 # of that file some time in the past. Thus our
1701 # merge will appear as a normal local file
1704 # merge will appear as a normal local file
1702 # modification.
1705 # modification.
1703 f_len = len(self.file(f).read(other))
1706 f_len = len(self.file(f).read(other))
1704 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1707 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1705
1708
1706 remove.sort()
1709 remove.sort()
1707 for f in remove:
1710 for f in remove:
1708 self.ui.note(_("removing %s\n") % f)
1711 self.ui.note(_("removing %s\n") % f)
1709 util.audit_path(f)
1712 util.audit_path(f)
1710 try:
1713 try:
1711 util.unlink(self.wjoin(f))
1714 util.unlink(self.wjoin(f))
1712 except OSError, inst:
1715 except OSError, inst:
1713 if inst.errno != errno.ENOENT:
1716 if inst.errno != errno.ENOENT:
1714 self.ui.warn(_("update failed to remove %s: %s!\n") %
1717 self.ui.warn(_("update failed to remove %s: %s!\n") %
1715 (f, inst.strerror))
1718 (f, inst.strerror))
1716 if moddirstate:
1719 if moddirstate:
1717 if branch_merge:
1720 if branch_merge:
1718 self.dirstate.update(remove, 'r')
1721 self.dirstate.update(remove, 'r')
1719 else:
1722 else:
1720 self.dirstate.forget(remove)
1723 self.dirstate.forget(remove)
1721
1724
1722 if moddirstate:
1725 if moddirstate:
1723 self.dirstate.setparents(p1, p2)
1726 self.dirstate.setparents(p1, p2)
1724
1727
1725 stat = ((len(get), _("updated")),
1728 stat = ((len(get), _("updated")),
1726 (len(merge) - len(failedmerge), _("merged")),
1729 (len(merge) - len(failedmerge), _("merged")),
1727 (len(remove), _("removed")),
1730 (len(remove), _("removed")),
1728 (len(failedmerge), _("unresolved")))
1731 (len(failedmerge), _("unresolved")))
1729 note = ", ".join([_("%d files %s") % s for s in stat])
1732 note = ", ".join([_("%d files %s") % s for s in stat])
1730 self.ui.note("%s\n" % note)
1733 self.ui.note("%s\n" % note)
1731 if moddirstate and branch_merge:
1734 if moddirstate and branch_merge:
1732 self.ui.note(_("(branch merge, don't forget to commit)\n"))
1735 self.ui.note(_("(branch merge, don't forget to commit)\n"))
1733
1736
1734 return err
1737 return err
1735
1738
1736 def merge3(self, fn, my, other, p1, p2):
1739 def merge3(self, fn, my, other, p1, p2):
1737 """perform a 3-way merge in the working directory"""
1740 """perform a 3-way merge in the working directory"""
1738
1741
1739 def temp(prefix, node):
1742 def temp(prefix, node):
1740 pre = "%s~%s." % (os.path.basename(fn), prefix)
1743 pre = "%s~%s." % (os.path.basename(fn), prefix)
1741 (fd, name) = tempfile.mkstemp("", pre)
1744 (fd, name) = tempfile.mkstemp("", pre)
1742 f = os.fdopen(fd, "wb")
1745 f = os.fdopen(fd, "wb")
1743 self.wwrite(fn, fl.read(node), f)
1746 self.wwrite(fn, fl.read(node), f)
1744 f.close()
1747 f.close()
1745 return name
1748 return name
1746
1749
1747 fl = self.file(fn)
1750 fl = self.file(fn)
1748 base = fl.ancestor(my, other)
1751 base = fl.ancestor(my, other)
1749 a = self.wjoin(fn)
1752 a = self.wjoin(fn)
1750 b = temp("base", base)
1753 b = temp("base", base)
1751 c = temp("other", other)
1754 c = temp("other", other)
1752
1755
1753 self.ui.note(_("resolving %s\n") % fn)
1756 self.ui.note(_("resolving %s\n") % fn)
1754 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1757 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1755 (fn, short(my), short(other), short(base)))
1758 (fn, short(my), short(other), short(base)))
1756
1759
1757 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1760 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1758 or "hgmerge")
1761 or "hgmerge")
1759 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
1762 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
1760 environ={'HG_FILE': fn,
1763 environ={'HG_FILE': fn,
1761 'HG_MY_NODE': p1,
1764 'HG_MY_NODE': p1,
1762 'HG_OTHER_NODE': p2,
1765 'HG_OTHER_NODE': p2,
1763 'HG_FILE_MY_NODE': hex(my),
1766 'HG_FILE_MY_NODE': hex(my),
1764 'HG_FILE_OTHER_NODE': hex(other),
1767 'HG_FILE_OTHER_NODE': hex(other),
1765 'HG_FILE_BASE_NODE': hex(base)})
1768 'HG_FILE_BASE_NODE': hex(base)})
1766 if r:
1769 if r:
1767 self.ui.warn(_("merging %s failed!\n") % fn)
1770 self.ui.warn(_("merging %s failed!\n") % fn)
1768
1771
1769 os.unlink(b)
1772 os.unlink(b)
1770 os.unlink(c)
1773 os.unlink(c)
1771 return r
1774 return r
1772
1775
1773 def verify(self):
1776 def verify(self):
1774 filelinkrevs = {}
1777 filelinkrevs = {}
1775 filenodes = {}
1778 filenodes = {}
1776 changesets = revisions = files = 0
1779 changesets = revisions = files = 0
1777 errors = [0]
1780 errors = [0]
1778 neededmanifests = {}
1781 neededmanifests = {}
1779
1782
1780 def err(msg):
1783 def err(msg):
1781 self.ui.warn(msg + "\n")
1784 self.ui.warn(msg + "\n")
1782 errors[0] += 1
1785 errors[0] += 1
1783
1786
1784 def checksize(obj, name):
1787 def checksize(obj, name):
1785 d = obj.checksize()
1788 d = obj.checksize()
1786 if d[0]:
1789 if d[0]:
1787 err(_("%s data length off by %d bytes") % (name, d[0]))
1790 err(_("%s data length off by %d bytes") % (name, d[0]))
1788 if d[1]:
1791 if d[1]:
1789 err(_("%s index contains %d extra bytes") % (name, d[1]))
1792 err(_("%s index contains %d extra bytes") % (name, d[1]))
1790
1793
1791 seen = {}
1794 seen = {}
1792 self.ui.status(_("checking changesets\n"))
1795 self.ui.status(_("checking changesets\n"))
1793 checksize(self.changelog, "changelog")
1796 checksize(self.changelog, "changelog")
1794
1797
1795 for i in range(self.changelog.count()):
1798 for i in range(self.changelog.count()):
1796 changesets += 1
1799 changesets += 1
1797 n = self.changelog.node(i)
1800 n = self.changelog.node(i)
1798 l = self.changelog.linkrev(n)
1801 l = self.changelog.linkrev(n)
1799 if l != i:
1802 if l != i:
1800 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
1803 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
1801 if n in seen:
1804 if n in seen:
1802 err(_("duplicate changeset at revision %d") % i)
1805 err(_("duplicate changeset at revision %d") % i)
1803 seen[n] = 1
1806 seen[n] = 1
1804
1807
1805 for p in self.changelog.parents(n):
1808 for p in self.changelog.parents(n):
1806 if p not in self.changelog.nodemap:
1809 if p not in self.changelog.nodemap:
1807 err(_("changeset %s has unknown parent %s") %
1810 err(_("changeset %s has unknown parent %s") %
1808 (short(n), short(p)))
1811 (short(n), short(p)))
1809 try:
1812 try:
1810 changes = self.changelog.read(n)
1813 changes = self.changelog.read(n)
1811 except KeyboardInterrupt:
1814 except KeyboardInterrupt:
1812 self.ui.warn(_("interrupted"))
1815 self.ui.warn(_("interrupted"))
1813 raise
1816 raise
1814 except Exception, inst:
1817 except Exception, inst:
1815 err(_("unpacking changeset %s: %s") % (short(n), inst))
1818 err(_("unpacking changeset %s: %s") % (short(n), inst))
1816 continue
1819 continue
1817
1820
1818 neededmanifests[changes[0]] = n
1821 neededmanifests[changes[0]] = n
1819
1822
1820 for f in changes[3]:
1823 for f in changes[3]:
1821 filelinkrevs.setdefault(f, []).append(i)
1824 filelinkrevs.setdefault(f, []).append(i)
1822
1825
1823 seen = {}
1826 seen = {}
1824 self.ui.status(_("checking manifests\n"))
1827 self.ui.status(_("checking manifests\n"))
1825 checksize(self.manifest, "manifest")
1828 checksize(self.manifest, "manifest")
1826
1829
1827 for i in range(self.manifest.count()):
1830 for i in range(self.manifest.count()):
1828 n = self.manifest.node(i)
1831 n = self.manifest.node(i)
1829 l = self.manifest.linkrev(n)
1832 l = self.manifest.linkrev(n)
1830
1833
1831 if l < 0 or l >= self.changelog.count():
1834 if l < 0 or l >= self.changelog.count():
1832 err(_("bad manifest link (%d) at revision %d") % (l, i))
1835 err(_("bad manifest link (%d) at revision %d") % (l, i))
1833
1836
1834 if n in neededmanifests:
1837 if n in neededmanifests:
1835 del neededmanifests[n]
1838 del neededmanifests[n]
1836
1839
1837 if n in seen:
1840 if n in seen:
1838 err(_("duplicate manifest at revision %d") % i)
1841 err(_("duplicate manifest at revision %d") % i)
1839
1842
1840 seen[n] = 1
1843 seen[n] = 1
1841
1844
1842 for p in self.manifest.parents(n):
1845 for p in self.manifest.parents(n):
1843 if p not in self.manifest.nodemap:
1846 if p not in self.manifest.nodemap:
1844 err(_("manifest %s has unknown parent %s") %
1847 err(_("manifest %s has unknown parent %s") %
1845 (short(n), short(p)))
1848 (short(n), short(p)))
1846
1849
1847 try:
1850 try:
1848 delta = mdiff.patchtext(self.manifest.delta(n))
1851 delta = mdiff.patchtext(self.manifest.delta(n))
1849 except KeyboardInterrupt:
1852 except KeyboardInterrupt:
1850 self.ui.warn(_("interrupted"))
1853 self.ui.warn(_("interrupted"))
1851 raise
1854 raise
1852 except Exception, inst:
1855 except Exception, inst:
1853 err(_("unpacking manifest %s: %s") % (short(n), inst))
1856 err(_("unpacking manifest %s: %s") % (short(n), inst))
1854 continue
1857 continue
1855
1858
1856 try:
1859 try:
1857 ff = [ l.split('\0') for l in delta.splitlines() ]
1860 ff = [ l.split('\0') for l in delta.splitlines() ]
1858 for f, fn in ff:
1861 for f, fn in ff:
1859 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1862 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1860 except (ValueError, TypeError), inst:
1863 except (ValueError, TypeError), inst:
1861 err(_("broken delta in manifest %s: %s") % (short(n), inst))
1864 err(_("broken delta in manifest %s: %s") % (short(n), inst))
1862
1865
1863 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1866 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1864
1867
1865 for m, c in neededmanifests.items():
1868 for m, c in neededmanifests.items():
1866 err(_("Changeset %s refers to unknown manifest %s") %
1869 err(_("Changeset %s refers to unknown manifest %s") %
1867 (short(m), short(c)))
1870 (short(m), short(c)))
1868 del neededmanifests
1871 del neededmanifests
1869
1872
1870 for f in filenodes:
1873 for f in filenodes:
1871 if f not in filelinkrevs:
1874 if f not in filelinkrevs:
1872 err(_("file %s in manifest but not in changesets") % f)
1875 err(_("file %s in manifest but not in changesets") % f)
1873
1876
1874 for f in filelinkrevs:
1877 for f in filelinkrevs:
1875 if f not in filenodes:
1878 if f not in filenodes:
1876 err(_("file %s in changeset but not in manifest") % f)
1879 err(_("file %s in changeset but not in manifest") % f)
1877
1880
1878 self.ui.status(_("checking files\n"))
1881 self.ui.status(_("checking files\n"))
1879 ff = filenodes.keys()
1882 ff = filenodes.keys()
1880 ff.sort()
1883 ff.sort()
1881 for f in ff:
1884 for f in ff:
1882 if f == "/dev/null":
1885 if f == "/dev/null":
1883 continue
1886 continue
1884 files += 1
1887 files += 1
1885 if not f:
1888 if not f:
1886 err(_("file without name in manifest %s") % short(n))
1889 err(_("file without name in manifest %s") % short(n))
1887 continue
1890 continue
1888 fl = self.file(f)
1891 fl = self.file(f)
1889 checksize(fl, f)
1892 checksize(fl, f)
1890
1893
1891 nodes = {nullid: 1}
1894 nodes = {nullid: 1}
1892 seen = {}
1895 seen = {}
1893 for i in range(fl.count()):
1896 for i in range(fl.count()):
1894 revisions += 1
1897 revisions += 1
1895 n = fl.node(i)
1898 n = fl.node(i)
1896
1899
1897 if n in seen:
1900 if n in seen:
1898 err(_("%s: duplicate revision %d") % (f, i))
1901 err(_("%s: duplicate revision %d") % (f, i))
1899 if n not in filenodes[f]:
1902 if n not in filenodes[f]:
1900 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
1903 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
1901 else:
1904 else:
1902 del filenodes[f][n]
1905 del filenodes[f][n]
1903
1906
1904 flr = fl.linkrev(n)
1907 flr = fl.linkrev(n)
1905 if flr not in filelinkrevs.get(f, []):
1908 if flr not in filelinkrevs.get(f, []):
1906 err(_("%s:%s points to unexpected changeset %d")
1909 err(_("%s:%s points to unexpected changeset %d")
1907 % (f, short(n), flr))
1910 % (f, short(n), flr))
1908 else:
1911 else:
1909 filelinkrevs[f].remove(flr)
1912 filelinkrevs[f].remove(flr)
1910
1913
1911 # verify contents
1914 # verify contents
1912 try:
1915 try:
1913 t = fl.read(n)
1916 t = fl.read(n)
1914 except KeyboardInterrupt:
1917 except KeyboardInterrupt:
1915 self.ui.warn(_("interrupted"))
1918 self.ui.warn(_("interrupted"))
1916 raise
1919 raise
1917 except Exception, inst:
1920 except Exception, inst:
1918 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
1921 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
1919
1922
1920 # verify parents
1923 # verify parents
1921 (p1, p2) = fl.parents(n)
1924 (p1, p2) = fl.parents(n)
1922 if p1 not in nodes:
1925 if p1 not in nodes:
1923 err(_("file %s:%s unknown parent 1 %s") %
1926 err(_("file %s:%s unknown parent 1 %s") %
1924 (f, short(n), short(p1)))
1927 (f, short(n), short(p1)))
1925 if p2 not in nodes:
1928 if p2 not in nodes:
1926 err(_("file %s:%s unknown parent 2 %s") %
1929 err(_("file %s:%s unknown parent 2 %s") %
1927 (f, short(n), short(p1)))
1930 (f, short(n), short(p1)))
1928 nodes[n] = 1
1931 nodes[n] = 1
1929
1932
1930 # cross-check
1933 # cross-check
1931 for node in filenodes[f]:
1934 for node in filenodes[f]:
1932 err(_("node %s in manifests not in %s") % (hex(node), f))
1935 err(_("node %s in manifests not in %s") % (hex(node), f))
1933
1936
1934 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
1937 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
1935 (files, changesets, revisions))
1938 (files, changesets, revisions))
1936
1939
1937 if errors[0]:
1940 if errors[0]:
1938 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1941 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1939 return 1
1942 return 1
1940
1943
1941 # used to avoid circular references so destructors work
1944 # used to avoid circular references so destructors work
1942 def aftertrans(base):
1945 def aftertrans(base):
1943 p = base
1946 p = base
1944 def a():
1947 def a():
1945 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
1948 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
1946 util.rename(os.path.join(p, "journal.dirstate"),
1949 util.rename(os.path.join(p, "journal.dirstate"),
1947 os.path.join(p, "undo.dirstate"))
1950 os.path.join(p, "undo.dirstate"))
1948 return a
1951 return a
1949
1952
@@ -1,251 +1,253
1 Mercurial Distributed SCM
1 Mercurial Distributed SCM
2
2
3 basic commands (use "hg help" for the full list or option "-v" for details):
3 basic commands (use "hg help" for the full list or option "-v" for details):
4
4
5 add add the specified files on the next commit
5 add add the specified files on the next commit
6 annotate show changeset information per file line
6 annotate show changeset information per file line
7 clone make a copy of an existing repository
7 clone make a copy of an existing repository
8 commit commit the specified files or all outstanding changes
8 commit commit the specified files or all outstanding changes
9 diff diff repository (or selected files)
9 diff diff repository (or selected files)
10 export dump the header and diffs for one or more changesets
10 export dump the header and diffs for one or more changesets
11 init create a new repository in the given directory
11 init create a new repository in the given directory
12 log show revision history of entire repository or files
12 log show revision history of entire repository or files
13 parents show the parents of the working dir or revision
13 parents show the parents of the working dir or revision
14 pull pull changes from the specified source
14 pull pull changes from the specified source
15 push push changes to the specified destination
15 push push changes to the specified destination
16 remove remove the specified files on the next commit
16 remove remove the specified files on the next commit
17 revert revert modified files or dirs back to their unmodified states
17 revert revert modified files or dirs back to their unmodified states
18 serve export the repository via HTTP
18 serve export the repository via HTTP
19 status show changed files in the working directory
19 status show changed files in the working directory
20 update update or merge working directory
20 update update or merge working directory
21 add add the specified files on the next commit
21 add add the specified files on the next commit
22 annotate show changeset information per file line
22 annotate show changeset information per file line
23 clone make a copy of an existing repository
23 clone make a copy of an existing repository
24 commit commit the specified files or all outstanding changes
24 commit commit the specified files or all outstanding changes
25 diff diff repository (or selected files)
25 diff diff repository (or selected files)
26 export dump the header and diffs for one or more changesets
26 export dump the header and diffs for one or more changesets
27 init create a new repository in the given directory
27 init create a new repository in the given directory
28 log show revision history of entire repository or files
28 log show revision history of entire repository or files
29 parents show the parents of the working dir or revision
29 parents show the parents of the working dir or revision
30 pull pull changes from the specified source
30 pull pull changes from the specified source
31 push push changes to the specified destination
31 push push changes to the specified destination
32 remove remove the specified files on the next commit
32 remove remove the specified files on the next commit
33 revert revert modified files or dirs back to their unmodified states
33 revert revert modified files or dirs back to their unmodified states
34 serve export the repository via HTTP
34 serve export the repository via HTTP
35 status show changed files in the working directory
35 status show changed files in the working directory
36 update update or merge working directory
36 update update or merge working directory
37 Mercurial Distributed SCM
37 Mercurial Distributed SCM
38
38
39 list of commands (use "hg help -v" to show aliases and global options):
39 list of commands (use "hg help -v" to show aliases and global options):
40
40
41 add add the specified files on the next commit
41 add add the specified files on the next commit
42 addremove add all new files, delete all missing files
42 addremove add all new files, delete all missing files
43 annotate show changeset information per file line
43 annotate show changeset information per file line
44 bundle create a changegroup file
44 bundle create a changegroup file
45 cat output the latest or given revisions of files
45 cat output the latest or given revisions of files
46 clone make a copy of an existing repository
46 clone make a copy of an existing repository
47 commit commit the specified files or all outstanding changes
47 commit commit the specified files or all outstanding changes
48 copy mark files as copied for the next commit
48 copy mark files as copied for the next commit
49 diff diff repository (or selected files)
49 diff diff repository (or selected files)
50 export dump the header and diffs for one or more changesets
50 export dump the header and diffs for one or more changesets
51 forget don't add the specified files on the next commit
51 forget don't add the specified files on the next commit
52 grep search for a pattern in specified files and revisions
52 grep search for a pattern in specified files and revisions
53 heads show current repository heads
53 heads show current repository heads
54 help show help for a given command or all commands
54 help show help for a given command or all commands
55 identify print information about the working copy
55 identify print information about the working copy
56 import import an ordered set of patches
56 import import an ordered set of patches
57 incoming show new changesets found in source
57 incoming show new changesets found in source
58 init create a new repository in the given directory
58 init create a new repository in the given directory
59 locate locate files matching specific patterns
59 locate locate files matching specific patterns
60 log show revision history of entire repository or files
60 log show revision history of entire repository or files
61 manifest output the latest or given revision of the project manifest
61 manifest output the latest or given revision of the project manifest
62 merge Merge working directory with another revision
62 merge Merge working directory with another revision
63 outgoing show changesets not found in destination
63 outgoing show changesets not found in destination
64 parents show the parents of the working dir or revision
64 parents show the parents of the working dir or revision
65 paths show definition of symbolic path names
65 paths show definition of symbolic path names
66 pull pull changes from the specified source
66 pull pull changes from the specified source
67 push push changes to the specified destination
67 push push changes to the specified destination
68 recover roll back an interrupted transaction
68 recover roll back an interrupted transaction
69 remove remove the specified files on the next commit
69 remove remove the specified files on the next commit
70 rename rename files; equivalent of copy + remove
70 rename rename files; equivalent of copy + remove
71 revert revert modified files or dirs back to their unmodified states
71 revert revert modified files or dirs back to their unmodified states
72 root print the root (top) of the current working dir
72 root print the root (top) of the current working dir
73 serve export the repository via HTTP
73 serve export the repository via HTTP
74 status show changed files in the working directory
74 status show changed files in the working directory
75 tag add a tag for the current tip or a given revision
75 tag add a tag for the current tip or a given revision
76 tags list repository tags
76 tags list repository tags
77 tip show the tip revision
77 tip show the tip revision
78 unbundle apply a changegroup file
78 unbundle apply a changegroup file
79 undo undo the last commit or pull
79 undo undo the last commit or pull
80 update update or merge working directory
80 update update or merge working directory
81 verify verify the integrity of the repository
81 verify verify the integrity of the repository
82 version output version and copyright information
82 version output version and copyright information
83 add add the specified files on the next commit
83 add add the specified files on the next commit
84 addremove add all new files, delete all missing files
84 addremove add all new files, delete all missing files
85 annotate show changeset information per file line
85 annotate show changeset information per file line
86 bundle create a changegroup file
86 bundle create a changegroup file
87 cat output the latest or given revisions of files
87 cat output the latest or given revisions of files
88 clone make a copy of an existing repository
88 clone make a copy of an existing repository
89 commit commit the specified files or all outstanding changes
89 commit commit the specified files or all outstanding changes
90 copy mark files as copied for the next commit
90 copy mark files as copied for the next commit
91 diff diff repository (or selected files)
91 diff diff repository (or selected files)
92 export dump the header and diffs for one or more changesets
92 export dump the header and diffs for one or more changesets
93 forget don't add the specified files on the next commit
93 forget don't add the specified files on the next commit
94 grep search for a pattern in specified files and revisions
94 grep search for a pattern in specified files and revisions
95 heads show current repository heads
95 heads show current repository heads
96 help show help for a given command or all commands
96 help show help for a given command or all commands
97 identify print information about the working copy
97 identify print information about the working copy
98 import import an ordered set of patches
98 import import an ordered set of patches
99 incoming show new changesets found in source
99 incoming show new changesets found in source
100 init create a new repository in the given directory
100 init create a new repository in the given directory
101 locate locate files matching specific patterns
101 locate locate files matching specific patterns
102 log show revision history of entire repository or files
102 log show revision history of entire repository or files
103 manifest output the latest or given revision of the project manifest
103 manifest output the latest or given revision of the project manifest
104 merge Merge working directory with another revision
104 merge Merge working directory with another revision
105 outgoing show changesets not found in destination
105 outgoing show changesets not found in destination
106 parents show the parents of the working dir or revision
106 parents show the parents of the working dir or revision
107 paths show definition of symbolic path names
107 paths show definition of symbolic path names
108 pull pull changes from the specified source
108 pull pull changes from the specified source
109 push push changes to the specified destination
109 push push changes to the specified destination
110 recover roll back an interrupted transaction
110 recover roll back an interrupted transaction
111 remove remove the specified files on the next commit
111 remove remove the specified files on the next commit
112 rename rename files; equivalent of copy + remove
112 rename rename files; equivalent of copy + remove
113 revert revert modified files or dirs back to their unmodified states
113 revert revert modified files or dirs back to their unmodified states
114 root print the root (top) of the current working dir
114 root print the root (top) of the current working dir
115 serve export the repository via HTTP
115 serve export the repository via HTTP
116 status show changed files in the working directory
116 status show changed files in the working directory
117 tag add a tag for the current tip or a given revision
117 tag add a tag for the current tip or a given revision
118 tags list repository tags
118 tags list repository tags
119 tip show the tip revision
119 tip show the tip revision
120 unbundle apply a changegroup file
120 unbundle apply a changegroup file
121 undo undo the last commit or pull
121 undo undo the last commit or pull
122 update update or merge working directory
122 update update or merge working directory
123 verify verify the integrity of the repository
123 verify verify the integrity of the repository
124 version output version and copyright information
124 version output version and copyright information
125 hg add [OPTION]... [FILE]...
125 hg add [OPTION]... [FILE]...
126
126
127 add the specified files on the next commit
127 add the specified files on the next commit
128
128
129 Schedule files to be version controlled and added to the repository.
129 Schedule files to be version controlled and added to the repository.
130
130
131 The files will be added to the repository at the next commit.
131 The files will be added to the repository at the next commit.
132
132
133 If no names are given, add all files in the repository.
133 If no names are given, add all files in the repository.
134
134
135 options:
135 options:
136
136
137 -I --include include names matching the given patterns
137 -I --include include names matching the given patterns
138 -X --exclude exclude names matching the given patterns
138 -X --exclude exclude names matching the given patterns
139 hg add: option --skjdfks not recognized
139 hg add: option --skjdfks not recognized
140 hg add [OPTION]... [FILE]...
140 hg add [OPTION]... [FILE]...
141
141
142 add the specified files on the next commit
142 add the specified files on the next commit
143
143
144 Schedule files to be version controlled and added to the repository.
144 Schedule files to be version controlled and added to the repository.
145
145
146 The files will be added to the repository at the next commit.
146 The files will be added to the repository at the next commit.
147
147
148 If no names are given, add all files in the repository.
148 If no names are given, add all files in the repository.
149
149
150 options:
150 options:
151
151
152 -I --include include names matching the given patterns
152 -I --include include names matching the given patterns
153 -X --exclude exclude names matching the given patterns
153 -X --exclude exclude names matching the given patterns
154 hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...
154 hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...
155
155
156 diff repository (or selected files)
156 diff repository (or selected files)
157
157
158 Show differences between revisions for the specified files.
158 Show differences between revisions for the specified files.
159
159
160 Differences between files are shown using the unified diff format.
160 Differences between files are shown using the unified diff format.
161
161
162 When two revision arguments are given, then changes are shown
162 When two revision arguments are given, then changes are shown
163 between those revisions. If only one revision is specified then
163 between those revisions. If only one revision is specified then
164 that revision is compared to the working directory, and, when no
164 that revision is compared to the working directory, and, when no
165 revisions are specified, the working directory files are compared
165 revisions are specified, the working directory files are compared
166 to its parent.
166 to its parent.
167
167
168 Without the -a option, diff will avoid generating diffs of files
168 Without the -a option, diff will avoid generating diffs of files
169 it detects as binary. With -a, diff will generate a diff anyway,
169 it detects as binary. With -a, diff will generate a diff anyway,
170 probably with undesirable results.
170 probably with undesirable results.
171
171
172 options:
172 options:
173
173
174 -r --rev revision
174 -r --rev revision
175 -a --text treat all files as text
175 -a --text treat all files as text
176 -p --show-function show which function each change is in
176 -p --show-function show which function each change is in
177 -w --ignore-all-space ignore white space when comparing lines
177 -w --ignore-all-space ignore white space when comparing lines
178 -I --include include names matching the given patterns
178 -I --include include names matching the given patterns
179 -X --exclude exclude names matching the given patterns
179 -X --exclude exclude names matching the given patterns
180 hg status [OPTION]... [FILE]...
180 hg status [OPTION]... [FILE]...
181
181
182 show changed files in the working directory
182 show changed files in the working directory
183
183
184 Show changed files in the repository. If names are
184 Show changed files in the repository. If names are
185 given, only files that match are shown.
185 given, only files that match are shown.
186
186
187 The codes used to show the status of files are:
187 The codes used to show the status of files are:
188 M = modified
188 M = modified
189 A = added
189 A = added
190 R = removed
190 R = removed
191 ! = deleted, but still tracked
191 ! = deleted, but still tracked
192 ? = not tracked
192 ? = not tracked
193 I = ignored (not shown by default)
193
194
194 aliases: st
195 aliases: st
195
196
196 options:
197 options:
197
198
198 -m --modified show only modified files
199 -m --modified show only modified files
199 -a --added show only added files
200 -a --added show only added files
200 -r --removed show only removed files
201 -r --removed show only removed files
201 -d --deleted show only deleted (but tracked) files
202 -d --deleted show only deleted (but tracked) files
202 -u --unknown show only unknown (not tracked) files
203 -u --unknown show only unknown (not tracked) files
204 -i --ignored show ignored files
203 -n --no-status hide status prefix
205 -n --no-status hide status prefix
204 -0 --print0 end filenames with NUL, for use with xargs
206 -0 --print0 end filenames with NUL, for use with xargs
205 -I --include include names matching the given patterns
207 -I --include include names matching the given patterns
206 -X --exclude exclude names matching the given patterns
208 -X --exclude exclude names matching the given patterns
207 hg status [OPTION]... [FILE]...
209 hg status [OPTION]... [FILE]...
208
210
209 show changed files in the working directory
211 show changed files in the working directory
210 hg: unknown command 'foo'
212 hg: unknown command 'foo'
211 Mercurial Distributed SCM
213 Mercurial Distributed SCM
212
214
213 basic commands (use "hg help" for the full list or option "-v" for details):
215 basic commands (use "hg help" for the full list or option "-v" for details):
214
216
215 add add the specified files on the next commit
217 add add the specified files on the next commit
216 annotate show changeset information per file line
218 annotate show changeset information per file line
217 clone make a copy of an existing repository
219 clone make a copy of an existing repository
218 commit commit the specified files or all outstanding changes
220 commit commit the specified files or all outstanding changes
219 diff diff repository (or selected files)
221 diff diff repository (or selected files)
220 export dump the header and diffs for one or more changesets
222 export dump the header and diffs for one or more changesets
221 init create a new repository in the given directory
223 init create a new repository in the given directory
222 log show revision history of entire repository or files
224 log show revision history of entire repository or files
223 parents show the parents of the working dir or revision
225 parents show the parents of the working dir or revision
224 pull pull changes from the specified source
226 pull pull changes from the specified source
225 push push changes to the specified destination
227 push push changes to the specified destination
226 remove remove the specified files on the next commit
228 remove remove the specified files on the next commit
227 revert revert modified files or dirs back to their unmodified states
229 revert revert modified files or dirs back to their unmodified states
228 serve export the repository via HTTP
230 serve export the repository via HTTP
229 status show changed files in the working directory
231 status show changed files in the working directory
230 update update or merge working directory
232 update update or merge working directory
231 hg: unknown command 'skjdfks'
233 hg: unknown command 'skjdfks'
232 Mercurial Distributed SCM
234 Mercurial Distributed SCM
233
235
234 basic commands (use "hg help" for the full list or option "-v" for details):
236 basic commands (use "hg help" for the full list or option "-v" for details):
235
237
236 add add the specified files on the next commit
238 add add the specified files on the next commit
237 annotate show changeset information per file line
239 annotate show changeset information per file line
238 clone make a copy of an existing repository
240 clone make a copy of an existing repository
239 commit commit the specified files or all outstanding changes
241 commit commit the specified files or all outstanding changes
240 diff diff repository (or selected files)
242 diff diff repository (or selected files)
241 export dump the header and diffs for one or more changesets
243 export dump the header and diffs for one or more changesets
242 init create a new repository in the given directory
244 init create a new repository in the given directory
243 log show revision history of entire repository or files
245 log show revision history of entire repository or files
244 parents show the parents of the working dir or revision
246 parents show the parents of the working dir or revision
245 pull pull changes from the specified source
247 pull pull changes from the specified source
246 push push changes to the specified destination
248 push push changes to the specified destination
247 remove remove the specified files on the next commit
249 remove remove the specified files on the next commit
248 revert revert modified files or dirs back to their unmodified states
250 revert revert modified files or dirs back to their unmodified states
249 serve export the repository via HTTP
251 serve export the repository via HTTP
250 status show changed files in the working directory
252 status show changed files in the working directory
251 update update or merge working directory
253 update update or merge working directory
General Comments 0
You need to be logged in to leave comments. Login now