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