##// END OF EJS Templates
Force editor to appear for an 'hg backout' if a message or logfile aren't...
john.levon@sun.com -
r2267:d812d91c default
parent child Browse files
Show More
@@ -1,3438 +1,3444 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 demandload(globals(), "fancyopts ui hg util lock revlog 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(), "archival changegroup")
15 demandload(globals(), "archival 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 bail_if_changed(repo):
22 def bail_if_changed(repo):
23 modified, added, removed, deleted, unknown = repo.changes()
23 modified, added, removed, deleted, unknown = repo.changes()
24 if modified or added or removed or deleted:
24 if modified or added or removed or deleted:
25 raise util.Abort(_("outstanding uncommitted changes"))
25 raise util.Abort(_("outstanding uncommitted changes"))
26
26
27 def filterfiles(filters, files):
27 def filterfiles(filters, files):
28 l = [x for x in files if x in filters]
28 l = [x for x in files if x in filters]
29
29
30 for t in filters:
30 for t in filters:
31 if t and t[-1] != "/":
31 if t and t[-1] != "/":
32 t += "/"
32 t += "/"
33 l += [x for x in files if x.startswith(t)]
33 l += [x for x in files if x.startswith(t)]
34 return l
34 return l
35
35
36 def relpath(repo, args):
36 def relpath(repo, args):
37 cwd = repo.getcwd()
37 cwd = repo.getcwd()
38 if cwd:
38 if cwd:
39 return [util.normpath(os.path.join(cwd, x)) for x in args]
39 return [util.normpath(os.path.join(cwd, x)) for x in args]
40 return args
40 return args
41
41
42 def matchpats(repo, pats=[], opts={}, head=''):
42 def matchpats(repo, pats=[], opts={}, head=''):
43 cwd = repo.getcwd()
43 cwd = repo.getcwd()
44 if not pats and cwd:
44 if not pats and cwd:
45 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
45 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
46 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
46 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
47 cwd = ''
47 cwd = ''
48 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
48 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
49 opts.get('exclude'), head)
49 opts.get('exclude'), head)
50
50
51 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
51 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
52 files, matchfn, anypats = matchpats(repo, pats, opts, head)
52 files, matchfn, anypats = matchpats(repo, pats, opts, head)
53 exact = dict(zip(files, files))
53 exact = dict(zip(files, files))
54 def walk():
54 def walk():
55 for src, fn in repo.walk(node=node, files=files, match=matchfn,
55 for src, fn in repo.walk(node=node, files=files, match=matchfn,
56 badmatch=badmatch):
56 badmatch=badmatch):
57 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
57 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
58 return files, matchfn, walk()
58 return files, matchfn, walk()
59
59
60 def walk(repo, pats, opts, node=None, head='', badmatch=None):
60 def walk(repo, pats, opts, node=None, head='', badmatch=None):
61 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
61 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
62 for r in results:
62 for r in results:
63 yield r
63 yield r
64
64
65 def walkchangerevs(ui, repo, pats, opts):
65 def walkchangerevs(ui, repo, pats, opts):
66 '''Iterate over files and the revs they changed in.
66 '''Iterate over files and the revs they changed in.
67
67
68 Callers most commonly need to iterate backwards over the history
68 Callers most commonly need to iterate backwards over the history
69 it is interested in. Doing so has awful (quadratic-looking)
69 it is interested in. Doing so has awful (quadratic-looking)
70 performance, so we use iterators in a "windowed" way.
70 performance, so we use iterators in a "windowed" way.
71
71
72 We walk a window of revisions in the desired order. Within the
72 We walk a window of revisions in the desired order. Within the
73 window, we first walk forwards to gather data, then in the desired
73 window, we first walk forwards to gather data, then in the desired
74 order (usually backwards) to display it.
74 order (usually backwards) to display it.
75
75
76 This function returns an (iterator, getchange, matchfn) tuple. The
76 This function returns an (iterator, getchange, matchfn) tuple. The
77 getchange function returns the changelog entry for a numeric
77 getchange function returns the changelog entry for a numeric
78 revision. The iterator yields 3-tuples. They will be of one of
78 revision. The iterator yields 3-tuples. They will be of one of
79 the following forms:
79 the following forms:
80
80
81 "window", incrementing, lastrev: stepping through a window,
81 "window", incrementing, lastrev: stepping through a window,
82 positive if walking forwards through revs, last rev in the
82 positive if walking forwards through revs, last rev in the
83 sequence iterated over - use to reset state for the current window
83 sequence iterated over - use to reset state for the current window
84
84
85 "add", rev, fns: out-of-order traversal of the given file names
85 "add", rev, fns: out-of-order traversal of the given file names
86 fns, which changed during revision rev - use to gather data for
86 fns, which changed during revision rev - use to gather data for
87 possible display
87 possible display
88
88
89 "iter", rev, None: in-order traversal of the revs earlier iterated
89 "iter", rev, None: in-order traversal of the revs earlier iterated
90 over with "add" - use to display data'''
90 over with "add" - use to display data'''
91
91
92 def increasing_windows(start, end, windowsize=8, sizelimit=512):
92 def increasing_windows(start, end, windowsize=8, sizelimit=512):
93 if start < end:
93 if start < end:
94 while start < end:
94 while start < end:
95 yield start, min(windowsize, end-start)
95 yield start, min(windowsize, end-start)
96 start += windowsize
96 start += windowsize
97 if windowsize < sizelimit:
97 if windowsize < sizelimit:
98 windowsize *= 2
98 windowsize *= 2
99 else:
99 else:
100 while start > end:
100 while start > end:
101 yield start, min(windowsize, start-end-1)
101 yield start, min(windowsize, start-end-1)
102 start -= windowsize
102 start -= windowsize
103 if windowsize < sizelimit:
103 if windowsize < sizelimit:
104 windowsize *= 2
104 windowsize *= 2
105
105
106
106
107 files, matchfn, anypats = matchpats(repo, pats, opts)
107 files, matchfn, anypats = matchpats(repo, pats, opts)
108
108
109 if repo.changelog.count() == 0:
109 if repo.changelog.count() == 0:
110 return [], False, matchfn
110 return [], False, matchfn
111
111
112 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
112 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
113 wanted = {}
113 wanted = {}
114 slowpath = anypats
114 slowpath = anypats
115 fncache = {}
115 fncache = {}
116
116
117 chcache = {}
117 chcache = {}
118 def getchange(rev):
118 def getchange(rev):
119 ch = chcache.get(rev)
119 ch = chcache.get(rev)
120 if ch is None:
120 if ch is None:
121 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
121 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
122 return ch
122 return ch
123
123
124 if not slowpath and not files:
124 if not slowpath and not files:
125 # No files, no patterns. Display all revs.
125 # No files, no patterns. Display all revs.
126 wanted = dict(zip(revs, revs))
126 wanted = dict(zip(revs, revs))
127 if not slowpath:
127 if not slowpath:
128 # Only files, no patterns. Check the history of each file.
128 # Only files, no patterns. Check the history of each file.
129 def filerevgen(filelog):
129 def filerevgen(filelog):
130 for i, window in increasing_windows(filelog.count()-1, -1):
130 for i, window in increasing_windows(filelog.count()-1, -1):
131 revs = []
131 revs = []
132 for j in xrange(i - window, i + 1):
132 for j in xrange(i - window, i + 1):
133 revs.append(filelog.linkrev(filelog.node(j)))
133 revs.append(filelog.linkrev(filelog.node(j)))
134 revs.reverse()
134 revs.reverse()
135 for rev in revs:
135 for rev in revs:
136 yield rev
136 yield rev
137
137
138 minrev, maxrev = min(revs), max(revs)
138 minrev, maxrev = min(revs), max(revs)
139 for file_ in files:
139 for file_ in files:
140 filelog = repo.file(file_)
140 filelog = repo.file(file_)
141 # A zero count may be a directory or deleted file, so
141 # A zero count may be a directory or deleted file, so
142 # try to find matching entries on the slow path.
142 # try to find matching entries on the slow path.
143 if filelog.count() == 0:
143 if filelog.count() == 0:
144 slowpath = True
144 slowpath = True
145 break
145 break
146 for rev in filerevgen(filelog):
146 for rev in filerevgen(filelog):
147 if rev <= maxrev:
147 if rev <= maxrev:
148 if rev < minrev:
148 if rev < minrev:
149 break
149 break
150 fncache.setdefault(rev, [])
150 fncache.setdefault(rev, [])
151 fncache[rev].append(file_)
151 fncache[rev].append(file_)
152 wanted[rev] = 1
152 wanted[rev] = 1
153 if slowpath:
153 if slowpath:
154 # The slow path checks files modified in every changeset.
154 # The slow path checks files modified in every changeset.
155 def changerevgen():
155 def changerevgen():
156 for i, window in increasing_windows(repo.changelog.count()-1, -1):
156 for i, window in increasing_windows(repo.changelog.count()-1, -1):
157 for j in xrange(i - window, i + 1):
157 for j in xrange(i - window, i + 1):
158 yield j, getchange(j)[3]
158 yield j, getchange(j)[3]
159
159
160 for rev, changefiles in changerevgen():
160 for rev, changefiles in changerevgen():
161 matches = filter(matchfn, changefiles)
161 matches = filter(matchfn, changefiles)
162 if matches:
162 if matches:
163 fncache[rev] = matches
163 fncache[rev] = matches
164 wanted[rev] = 1
164 wanted[rev] = 1
165
165
166 def iterate():
166 def iterate():
167 for i, window in increasing_windows(0, len(revs)):
167 for i, window in increasing_windows(0, len(revs)):
168 yield 'window', revs[0] < revs[-1], revs[-1]
168 yield 'window', revs[0] < revs[-1], revs[-1]
169 nrevs = [rev for rev in revs[i:i+window]
169 nrevs = [rev for rev in revs[i:i+window]
170 if rev in wanted]
170 if rev in wanted]
171 srevs = list(nrevs)
171 srevs = list(nrevs)
172 srevs.sort()
172 srevs.sort()
173 for rev in srevs:
173 for rev in srevs:
174 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
174 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
175 yield 'add', rev, fns
175 yield 'add', rev, fns
176 for rev in nrevs:
176 for rev in nrevs:
177 yield 'iter', rev, None
177 yield 'iter', rev, None
178 return iterate(), getchange, matchfn
178 return iterate(), getchange, matchfn
179
179
180 revrangesep = ':'
180 revrangesep = ':'
181
181
182 def revrange(ui, repo, revs, revlog=None):
182 def revrange(ui, repo, revs, revlog=None):
183 """Yield revision as strings from a list of revision specifications."""
183 """Yield revision as strings from a list of revision specifications."""
184 if revlog is None:
184 if revlog is None:
185 revlog = repo.changelog
185 revlog = repo.changelog
186 revcount = revlog.count()
186 revcount = revlog.count()
187 def fix(val, defval):
187 def fix(val, defval):
188 if not val:
188 if not val:
189 return defval
189 return defval
190 try:
190 try:
191 num = int(val)
191 num = int(val)
192 if str(num) != val:
192 if str(num) != val:
193 raise ValueError
193 raise ValueError
194 if num < 0:
194 if num < 0:
195 num += revcount
195 num += revcount
196 if num < 0:
196 if num < 0:
197 num = 0
197 num = 0
198 elif num >= revcount:
198 elif num >= revcount:
199 raise ValueError
199 raise ValueError
200 except ValueError:
200 except ValueError:
201 try:
201 try:
202 num = repo.changelog.rev(repo.lookup(val))
202 num = repo.changelog.rev(repo.lookup(val))
203 except KeyError:
203 except KeyError:
204 try:
204 try:
205 num = revlog.rev(revlog.lookup(val))
205 num = revlog.rev(revlog.lookup(val))
206 except KeyError:
206 except KeyError:
207 raise util.Abort(_('invalid revision identifier %s'), val)
207 raise util.Abort(_('invalid revision identifier %s'), val)
208 return num
208 return num
209 seen = {}
209 seen = {}
210 for spec in revs:
210 for spec in revs:
211 if spec.find(revrangesep) >= 0:
211 if spec.find(revrangesep) >= 0:
212 start, end = spec.split(revrangesep, 1)
212 start, end = spec.split(revrangesep, 1)
213 start = fix(start, 0)
213 start = fix(start, 0)
214 end = fix(end, revcount - 1)
214 end = fix(end, revcount - 1)
215 step = start > end and -1 or 1
215 step = start > end and -1 or 1
216 for rev in xrange(start, end+step, step):
216 for rev in xrange(start, end+step, step):
217 if rev in seen:
217 if rev in seen:
218 continue
218 continue
219 seen[rev] = 1
219 seen[rev] = 1
220 yield str(rev)
220 yield str(rev)
221 else:
221 else:
222 rev = fix(spec, None)
222 rev = fix(spec, None)
223 if rev in seen:
223 if rev in seen:
224 continue
224 continue
225 seen[rev] = 1
225 seen[rev] = 1
226 yield str(rev)
226 yield str(rev)
227
227
228 def make_filename(repo, r, pat, node=None,
228 def make_filename(repo, r, pat, node=None,
229 total=None, seqno=None, revwidth=None, pathname=None):
229 total=None, seqno=None, revwidth=None, pathname=None):
230 node_expander = {
230 node_expander = {
231 'H': lambda: hex(node),
231 'H': lambda: hex(node),
232 'R': lambda: str(r.rev(node)),
232 'R': lambda: str(r.rev(node)),
233 'h': lambda: short(node),
233 'h': lambda: short(node),
234 }
234 }
235 expander = {
235 expander = {
236 '%': lambda: '%',
236 '%': lambda: '%',
237 'b': lambda: os.path.basename(repo.root),
237 'b': lambda: os.path.basename(repo.root),
238 }
238 }
239
239
240 try:
240 try:
241 if node:
241 if node:
242 expander.update(node_expander)
242 expander.update(node_expander)
243 if node and revwidth is not None:
243 if node and revwidth is not None:
244 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
244 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
245 if total is not None:
245 if total is not None:
246 expander['N'] = lambda: str(total)
246 expander['N'] = lambda: str(total)
247 if seqno is not None:
247 if seqno is not None:
248 expander['n'] = lambda: str(seqno)
248 expander['n'] = lambda: str(seqno)
249 if total is not None and seqno is not None:
249 if total is not None and seqno is not None:
250 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
250 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
251 if pathname is not None:
251 if pathname is not None:
252 expander['s'] = lambda: os.path.basename(pathname)
252 expander['s'] = lambda: os.path.basename(pathname)
253 expander['d'] = lambda: os.path.dirname(pathname) or '.'
253 expander['d'] = lambda: os.path.dirname(pathname) or '.'
254 expander['p'] = lambda: pathname
254 expander['p'] = lambda: pathname
255
255
256 newname = []
256 newname = []
257 patlen = len(pat)
257 patlen = len(pat)
258 i = 0
258 i = 0
259 while i < patlen:
259 while i < patlen:
260 c = pat[i]
260 c = pat[i]
261 if c == '%':
261 if c == '%':
262 i += 1
262 i += 1
263 c = pat[i]
263 c = pat[i]
264 c = expander[c]()
264 c = expander[c]()
265 newname.append(c)
265 newname.append(c)
266 i += 1
266 i += 1
267 return ''.join(newname)
267 return ''.join(newname)
268 except KeyError, inst:
268 except KeyError, inst:
269 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
269 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
270 inst.args[0])
270 inst.args[0])
271
271
272 def make_file(repo, r, pat, node=None,
272 def make_file(repo, r, pat, node=None,
273 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
273 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
274 if not pat or pat == '-':
274 if not pat or pat == '-':
275 return 'w' in mode and sys.stdout or sys.stdin
275 return 'w' in mode and sys.stdout or sys.stdin
276 if hasattr(pat, 'write') and 'w' in mode:
276 if hasattr(pat, 'write') and 'w' in mode:
277 return pat
277 return pat
278 if hasattr(pat, 'read') and 'r' in mode:
278 if hasattr(pat, 'read') and 'r' in mode:
279 return pat
279 return pat
280 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
280 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
281 pathname),
281 pathname),
282 mode)
282 mode)
283
283
284 def write_bundle(cg, filename=None, compress=True):
284 def write_bundle(cg, filename=None, compress=True):
285 """Write a bundle file and return its filename.
285 """Write a bundle file and return its filename.
286
286
287 Existing files will not be overwritten.
287 Existing files will not be overwritten.
288 If no filename is specified, a temporary file is created.
288 If no filename is specified, a temporary file is created.
289 bz2 compression can be turned off.
289 bz2 compression can be turned off.
290 The bundle file will be deleted in case of errors.
290 The bundle file will be deleted in case of errors.
291 """
291 """
292 class nocompress(object):
292 class nocompress(object):
293 def compress(self, x):
293 def compress(self, x):
294 return x
294 return x
295 def flush(self):
295 def flush(self):
296 return ""
296 return ""
297
297
298 fh = None
298 fh = None
299 cleanup = None
299 cleanup = None
300 try:
300 try:
301 if filename:
301 if filename:
302 if os.path.exists(filename):
302 if os.path.exists(filename):
303 raise util.Abort(_("file '%s' already exists"), filename)
303 raise util.Abort(_("file '%s' already exists"), filename)
304 fh = open(filename, "wb")
304 fh = open(filename, "wb")
305 else:
305 else:
306 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
306 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
307 fh = os.fdopen(fd, "wb")
307 fh = os.fdopen(fd, "wb")
308 cleanup = filename
308 cleanup = filename
309
309
310 if compress:
310 if compress:
311 fh.write("HG10")
311 fh.write("HG10")
312 z = bz2.BZ2Compressor(9)
312 z = bz2.BZ2Compressor(9)
313 else:
313 else:
314 fh.write("HG10UN")
314 fh.write("HG10UN")
315 z = nocompress()
315 z = nocompress()
316 # parse the changegroup data, otherwise we will block
316 # parse the changegroup data, otherwise we will block
317 # in case of sshrepo because we don't know the end of the stream
317 # in case of sshrepo because we don't know the end of the stream
318
318
319 # an empty chunkiter is the end of the changegroup
319 # an empty chunkiter is the end of the changegroup
320 empty = False
320 empty = False
321 while not empty:
321 while not empty:
322 empty = True
322 empty = True
323 for chunk in changegroup.chunkiter(cg):
323 for chunk in changegroup.chunkiter(cg):
324 empty = False
324 empty = False
325 fh.write(z.compress(changegroup.genchunk(chunk)))
325 fh.write(z.compress(changegroup.genchunk(chunk)))
326 fh.write(z.compress(changegroup.closechunk()))
326 fh.write(z.compress(changegroup.closechunk()))
327 fh.write(z.flush())
327 fh.write(z.flush())
328 cleanup = None
328 cleanup = None
329 return filename
329 return filename
330 finally:
330 finally:
331 if fh is not None:
331 if fh is not None:
332 fh.close()
332 fh.close()
333 if cleanup is not None:
333 if cleanup is not None:
334 os.unlink(cleanup)
334 os.unlink(cleanup)
335
335
336 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
336 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
337 changes=None, text=False, opts={}):
337 changes=None, text=False, opts={}):
338 if not node1:
338 if not node1:
339 node1 = repo.dirstate.parents()[0]
339 node1 = repo.dirstate.parents()[0]
340 # reading the data for node1 early allows it to play nicely
340 # reading the data for node1 early allows it to play nicely
341 # with repo.changes and the revlog cache.
341 # with repo.changes and the revlog cache.
342 change = repo.changelog.read(node1)
342 change = repo.changelog.read(node1)
343 mmap = repo.manifest.read(change[0])
343 mmap = repo.manifest.read(change[0])
344 date1 = util.datestr(change[2])
344 date1 = util.datestr(change[2])
345
345
346 if not changes:
346 if not changes:
347 changes = repo.changes(node1, node2, files, match=match)
347 changes = repo.changes(node1, node2, files, match=match)
348 modified, added, removed, deleted, unknown = changes
348 modified, added, removed, deleted, unknown = changes
349 if files:
349 if files:
350 modified, added, removed = map(lambda x: filterfiles(files, x),
350 modified, added, removed = map(lambda x: filterfiles(files, x),
351 (modified, added, removed))
351 (modified, added, removed))
352
352
353 if not modified and not added and not removed:
353 if not modified and not added and not removed:
354 return
354 return
355
355
356 if node2:
356 if node2:
357 change = repo.changelog.read(node2)
357 change = repo.changelog.read(node2)
358 mmap2 = repo.manifest.read(change[0])
358 mmap2 = repo.manifest.read(change[0])
359 date2 = util.datestr(change[2])
359 date2 = util.datestr(change[2])
360 def read(f):
360 def read(f):
361 return repo.file(f).read(mmap2[f])
361 return repo.file(f).read(mmap2[f])
362 else:
362 else:
363 date2 = util.datestr()
363 date2 = util.datestr()
364 def read(f):
364 def read(f):
365 return repo.wread(f)
365 return repo.wread(f)
366
366
367 if ui.quiet:
367 if ui.quiet:
368 r = None
368 r = None
369 else:
369 else:
370 hexfunc = ui.verbose and hex or short
370 hexfunc = ui.verbose and hex or short
371 r = [hexfunc(node) for node in [node1, node2] if node]
371 r = [hexfunc(node) for node in [node1, node2] if node]
372
372
373 diffopts = ui.diffopts()
373 diffopts = ui.diffopts()
374 showfunc = opts.get('show_function') or diffopts['showfunc']
374 showfunc = opts.get('show_function') or diffopts['showfunc']
375 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
375 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
376 for f in modified:
376 for f in modified:
377 to = None
377 to = None
378 if f in mmap:
378 if f in mmap:
379 to = repo.file(f).read(mmap[f])
379 to = repo.file(f).read(mmap[f])
380 tn = read(f)
380 tn = read(f)
381 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,
382 showfunc=showfunc, ignorews=ignorews))
382 showfunc=showfunc, ignorews=ignorews))
383 for f in added:
383 for f in added:
384 to = None
384 to = None
385 tn = read(f)
385 tn = read(f)
386 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,
387 showfunc=showfunc, ignorews=ignorews))
387 showfunc=showfunc, ignorews=ignorews))
388 for f in removed:
388 for f in removed:
389 to = repo.file(f).read(mmap[f])
389 to = repo.file(f).read(mmap[f])
390 tn = None
390 tn = None
391 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
391 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
392 showfunc=showfunc, ignorews=ignorews))
392 showfunc=showfunc, ignorews=ignorews))
393
393
394 def trimuser(ui, name, rev, revcache):
394 def trimuser(ui, name, rev, revcache):
395 """trim the name of the user who committed a change"""
395 """trim the name of the user who committed a change"""
396 user = revcache.get(rev)
396 user = revcache.get(rev)
397 if user is None:
397 if user is None:
398 user = revcache[rev] = ui.shortuser(name)
398 user = revcache[rev] = ui.shortuser(name)
399 return user
399 return user
400
400
401 class changeset_printer(object):
401 class changeset_printer(object):
402 '''show changeset information when templating not requested.'''
402 '''show changeset information when templating not requested.'''
403
403
404 def __init__(self, ui, repo):
404 def __init__(self, ui, repo):
405 self.ui = ui
405 self.ui = ui
406 self.repo = repo
406 self.repo = repo
407
407
408 def show(self, rev=0, changenode=None, brinfo=None):
408 def show(self, rev=0, changenode=None, brinfo=None):
409 '''show a single changeset or file revision'''
409 '''show a single changeset or file revision'''
410 log = self.repo.changelog
410 log = self.repo.changelog
411 if changenode is None:
411 if changenode is None:
412 changenode = log.node(rev)
412 changenode = log.node(rev)
413 elif not rev:
413 elif not rev:
414 rev = log.rev(changenode)
414 rev = log.rev(changenode)
415
415
416 if self.ui.quiet:
416 if self.ui.quiet:
417 self.ui.write("%d:%s\n" % (rev, short(changenode)))
417 self.ui.write("%d:%s\n" % (rev, short(changenode)))
418 return
418 return
419
419
420 changes = log.read(changenode)
420 changes = log.read(changenode)
421 date = util.datestr(changes[2])
421 date = util.datestr(changes[2])
422
422
423 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
423 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
424 for p in log.parents(changenode)
424 for p in log.parents(changenode)
425 if self.ui.debugflag or p != nullid]
425 if self.ui.debugflag or p != nullid]
426 if (not self.ui.debugflag and len(parents) == 1 and
426 if (not self.ui.debugflag and len(parents) == 1 and
427 parents[0][0] == rev-1):
427 parents[0][0] == rev-1):
428 parents = []
428 parents = []
429
429
430 if self.ui.verbose:
430 if self.ui.verbose:
431 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
431 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
432 else:
432 else:
433 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
433 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
434
434
435 for tag in self.repo.nodetags(changenode):
435 for tag in self.repo.nodetags(changenode):
436 self.ui.status(_("tag: %s\n") % tag)
436 self.ui.status(_("tag: %s\n") % tag)
437 for parent in parents:
437 for parent in parents:
438 self.ui.write(_("parent: %d:%s\n") % parent)
438 self.ui.write(_("parent: %d:%s\n") % parent)
439
439
440 if brinfo and changenode in brinfo:
440 if brinfo and changenode in brinfo:
441 br = brinfo[changenode]
441 br = brinfo[changenode]
442 self.ui.write(_("branch: %s\n") % " ".join(br))
442 self.ui.write(_("branch: %s\n") % " ".join(br))
443
443
444 self.ui.debug(_("manifest: %d:%s\n") %
444 self.ui.debug(_("manifest: %d:%s\n") %
445 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
445 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
446 self.ui.status(_("user: %s\n") % changes[1])
446 self.ui.status(_("user: %s\n") % changes[1])
447 self.ui.status(_("date: %s\n") % date)
447 self.ui.status(_("date: %s\n") % date)
448
448
449 if self.ui.debugflag:
449 if self.ui.debugflag:
450 files = self.repo.changes(log.parents(changenode)[0], changenode)
450 files = self.repo.changes(log.parents(changenode)[0], changenode)
451 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
451 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
452 files):
452 files):
453 if value:
453 if value:
454 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
454 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
455 else:
455 else:
456 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
456 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
457
457
458 description = changes[4].strip()
458 description = changes[4].strip()
459 if description:
459 if description:
460 if self.ui.verbose:
460 if self.ui.verbose:
461 self.ui.status(_("description:\n"))
461 self.ui.status(_("description:\n"))
462 self.ui.status(description)
462 self.ui.status(description)
463 self.ui.status("\n\n")
463 self.ui.status("\n\n")
464 else:
464 else:
465 self.ui.status(_("summary: %s\n") %
465 self.ui.status(_("summary: %s\n") %
466 description.splitlines()[0])
466 description.splitlines()[0])
467 self.ui.status("\n")
467 self.ui.status("\n")
468
468
469 def show_changeset(ui, repo, opts):
469 def show_changeset(ui, repo, opts):
470 '''show one changeset. uses template or regular display. caller
470 '''show one changeset. uses template or regular display. caller
471 can pass in 'style' and 'template' options in opts.'''
471 can pass in 'style' and 'template' options in opts.'''
472
472
473 tmpl = opts.get('template')
473 tmpl = opts.get('template')
474 if tmpl:
474 if tmpl:
475 tmpl = templater.parsestring(tmpl, quoted=False)
475 tmpl = templater.parsestring(tmpl, quoted=False)
476 else:
476 else:
477 tmpl = ui.config('ui', 'logtemplate')
477 tmpl = ui.config('ui', 'logtemplate')
478 if tmpl: tmpl = templater.parsestring(tmpl)
478 if tmpl: tmpl = templater.parsestring(tmpl)
479 mapfile = opts.get('style') or ui.config('ui', 'style')
479 mapfile = opts.get('style') or ui.config('ui', 'style')
480 if tmpl or mapfile:
480 if tmpl or mapfile:
481 if mapfile:
481 if mapfile:
482 if not os.path.isfile(mapfile):
482 if not os.path.isfile(mapfile):
483 mapname = templater.templatepath('map-cmdline.' + mapfile)
483 mapname = templater.templatepath('map-cmdline.' + mapfile)
484 if not mapname: mapname = templater.templatepath(mapfile)
484 if not mapname: mapname = templater.templatepath(mapfile)
485 if mapname: mapfile = mapname
485 if mapname: mapfile = mapname
486 try:
486 try:
487 t = templater.changeset_templater(ui, repo, mapfile)
487 t = templater.changeset_templater(ui, repo, mapfile)
488 except SyntaxError, inst:
488 except SyntaxError, inst:
489 raise util.Abort(inst.args[0])
489 raise util.Abort(inst.args[0])
490 if tmpl: t.use_template(tmpl)
490 if tmpl: t.use_template(tmpl)
491 return t
491 return t
492 return changeset_printer(ui, repo)
492 return changeset_printer(ui, repo)
493
493
494 def show_version(ui):
494 def show_version(ui):
495 """output version and copyright information"""
495 """output version and copyright information"""
496 ui.write(_("Mercurial Distributed SCM (version %s)\n")
496 ui.write(_("Mercurial Distributed SCM (version %s)\n")
497 % version.get_version())
497 % version.get_version())
498 ui.status(_(
498 ui.status(_(
499 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
499 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
500 "This is free software; see the source for copying conditions. "
500 "This is free software; see the source for copying conditions. "
501 "There is NO\nwarranty; "
501 "There is NO\nwarranty; "
502 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
502 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
503 ))
503 ))
504
504
505 def help_(ui, cmd=None, with_version=False):
505 def help_(ui, cmd=None, with_version=False):
506 """show help for a given command or all commands"""
506 """show help for a given command or all commands"""
507 option_lists = []
507 option_lists = []
508 if cmd and cmd != 'shortlist':
508 if cmd and cmd != 'shortlist':
509 if with_version:
509 if with_version:
510 show_version(ui)
510 show_version(ui)
511 ui.write('\n')
511 ui.write('\n')
512 aliases, i = find(cmd)
512 aliases, i = find(cmd)
513 # synopsis
513 # synopsis
514 ui.write("%s\n\n" % i[2])
514 ui.write("%s\n\n" % i[2])
515
515
516 # description
516 # description
517 doc = i[0].__doc__
517 doc = i[0].__doc__
518 if not doc:
518 if not doc:
519 doc = _("(No help text available)")
519 doc = _("(No help text available)")
520 if ui.quiet:
520 if ui.quiet:
521 doc = doc.splitlines(0)[0]
521 doc = doc.splitlines(0)[0]
522 ui.write("%s\n" % doc.rstrip())
522 ui.write("%s\n" % doc.rstrip())
523
523
524 if not ui.quiet:
524 if not ui.quiet:
525 # aliases
525 # aliases
526 if len(aliases) > 1:
526 if len(aliases) > 1:
527 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
527 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
528
528
529 # options
529 # options
530 if i[1]:
530 if i[1]:
531 option_lists.append(("options", i[1]))
531 option_lists.append(("options", i[1]))
532
532
533 else:
533 else:
534 # program name
534 # program name
535 if ui.verbose or with_version:
535 if ui.verbose or with_version:
536 show_version(ui)
536 show_version(ui)
537 else:
537 else:
538 ui.status(_("Mercurial Distributed SCM\n"))
538 ui.status(_("Mercurial Distributed SCM\n"))
539 ui.status('\n')
539 ui.status('\n')
540
540
541 # list of commands
541 # list of commands
542 if cmd == "shortlist":
542 if cmd == "shortlist":
543 ui.status(_('basic commands (use "hg help" '
543 ui.status(_('basic commands (use "hg help" '
544 'for the full list or option "-v" for details):\n\n'))
544 'for the full list or option "-v" for details):\n\n'))
545 elif ui.verbose:
545 elif ui.verbose:
546 ui.status(_('list of commands:\n\n'))
546 ui.status(_('list of commands:\n\n'))
547 else:
547 else:
548 ui.status(_('list of commands (use "hg help -v" '
548 ui.status(_('list of commands (use "hg help -v" '
549 'to show aliases and global options):\n\n'))
549 'to show aliases and global options):\n\n'))
550
550
551 h = {}
551 h = {}
552 cmds = {}
552 cmds = {}
553 for c, e in table.items():
553 for c, e in table.items():
554 f = c.split("|")[0]
554 f = c.split("|")[0]
555 if cmd == "shortlist" and not f.startswith("^"):
555 if cmd == "shortlist" and not f.startswith("^"):
556 continue
556 continue
557 f = f.lstrip("^")
557 f = f.lstrip("^")
558 if not ui.debugflag and f.startswith("debug"):
558 if not ui.debugflag and f.startswith("debug"):
559 continue
559 continue
560 doc = e[0].__doc__
560 doc = e[0].__doc__
561 if not doc:
561 if not doc:
562 doc = _("(No help text available)")
562 doc = _("(No help text available)")
563 h[f] = doc.splitlines(0)[0].rstrip()
563 h[f] = doc.splitlines(0)[0].rstrip()
564 cmds[f] = c.lstrip("^")
564 cmds[f] = c.lstrip("^")
565
565
566 fns = h.keys()
566 fns = h.keys()
567 fns.sort()
567 fns.sort()
568 m = max(map(len, fns))
568 m = max(map(len, fns))
569 for f in fns:
569 for f in fns:
570 if ui.verbose:
570 if ui.verbose:
571 commands = cmds[f].replace("|",", ")
571 commands = cmds[f].replace("|",", ")
572 ui.write(" %s:\n %s\n"%(commands, h[f]))
572 ui.write(" %s:\n %s\n"%(commands, h[f]))
573 else:
573 else:
574 ui.write(' %-*s %s\n' % (m, f, h[f]))
574 ui.write(' %-*s %s\n' % (m, f, h[f]))
575
575
576 # global options
576 # global options
577 if ui.verbose:
577 if ui.verbose:
578 option_lists.append(("global options", globalopts))
578 option_lists.append(("global options", globalopts))
579
579
580 # list all option lists
580 # list all option lists
581 opt_output = []
581 opt_output = []
582 for title, options in option_lists:
582 for title, options in option_lists:
583 opt_output.append(("\n%s:\n" % title, None))
583 opt_output.append(("\n%s:\n" % title, None))
584 for shortopt, longopt, default, desc in options:
584 for shortopt, longopt, default, desc in options:
585 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
585 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
586 longopt and " --%s" % longopt),
586 longopt and " --%s" % longopt),
587 "%s%s" % (desc,
587 "%s%s" % (desc,
588 default
588 default
589 and _(" (default: %s)") % default
589 and _(" (default: %s)") % default
590 or "")))
590 or "")))
591
591
592 if opt_output:
592 if opt_output:
593 opts_len = max([len(line[0]) for line in opt_output if line[1]])
593 opts_len = max([len(line[0]) for line in opt_output if line[1]])
594 for first, second in opt_output:
594 for first, second in opt_output:
595 if second:
595 if second:
596 ui.write(" %-*s %s\n" % (opts_len, first, second))
596 ui.write(" %-*s %s\n" % (opts_len, first, second))
597 else:
597 else:
598 ui.write("%s\n" % first)
598 ui.write("%s\n" % first)
599
599
600 # Commands start here, listed alphabetically
600 # Commands start here, listed alphabetically
601
601
602 def add(ui, repo, *pats, **opts):
602 def add(ui, repo, *pats, **opts):
603 """add the specified files on the next commit
603 """add the specified files on the next commit
604
604
605 Schedule files to be version controlled and added to the repository.
605 Schedule files to be version controlled and added to the repository.
606
606
607 The files will be added to the repository at the next commit.
607 The files will be added to the repository at the next commit.
608
608
609 If no names are given, add all files in the repository.
609 If no names are given, add all files in the repository.
610 """
610 """
611
611
612 names = []
612 names = []
613 for src, abs, rel, exact in walk(repo, pats, opts):
613 for src, abs, rel, exact in walk(repo, pats, opts):
614 if exact:
614 if exact:
615 if ui.verbose:
615 if ui.verbose:
616 ui.status(_('adding %s\n') % rel)
616 ui.status(_('adding %s\n') % rel)
617 names.append(abs)
617 names.append(abs)
618 elif repo.dirstate.state(abs) == '?':
618 elif repo.dirstate.state(abs) == '?':
619 ui.status(_('adding %s\n') % rel)
619 ui.status(_('adding %s\n') % rel)
620 names.append(abs)
620 names.append(abs)
621 repo.add(names)
621 repo.add(names)
622
622
623 def addremove(ui, repo, *pats, **opts):
623 def addremove(ui, repo, *pats, **opts):
624 """add all new files, delete all missing files (DEPRECATED)
624 """add all new files, delete all missing files (DEPRECATED)
625
625
626 (DEPRECATED)
626 (DEPRECATED)
627 Add all new files and remove all missing files from the repository.
627 Add all new files and remove all missing files from the repository.
628
628
629 New files are ignored if they match any of the patterns in .hgignore. As
629 New files are ignored if they match any of the patterns in .hgignore. As
630 with add, these changes take effect at the next commit.
630 with add, these changes take effect at the next commit.
631
631
632 This command is now deprecated and will be removed in a future
632 This command is now deprecated and will be removed in a future
633 release. Please use add and remove --after instead.
633 release. Please use add and remove --after instead.
634 """
634 """
635 ui.warn(_('(the addremove command is deprecated; use add and remove '
635 ui.warn(_('(the addremove command is deprecated; use add and remove '
636 '--after instead)\n'))
636 '--after instead)\n'))
637 return addremove_lock(ui, repo, pats, opts)
637 return addremove_lock(ui, repo, pats, opts)
638
638
639 def addremove_lock(ui, repo, pats, opts, wlock=None):
639 def addremove_lock(ui, repo, pats, opts, wlock=None):
640 add, remove = [], []
640 add, remove = [], []
641 for src, abs, rel, exact in walk(repo, pats, opts):
641 for src, abs, rel, exact in walk(repo, pats, opts):
642 if src == 'f' and repo.dirstate.state(abs) == '?':
642 if src == 'f' and repo.dirstate.state(abs) == '?':
643 add.append(abs)
643 add.append(abs)
644 if ui.verbose or not exact:
644 if ui.verbose or not exact:
645 ui.status(_('adding %s\n') % ((pats and rel) or abs))
645 ui.status(_('adding %s\n') % ((pats and rel) or abs))
646 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
646 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
647 remove.append(abs)
647 remove.append(abs)
648 if ui.verbose or not exact:
648 if ui.verbose or not exact:
649 ui.status(_('removing %s\n') % ((pats and rel) or abs))
649 ui.status(_('removing %s\n') % ((pats and rel) or abs))
650 repo.add(add, wlock=wlock)
650 repo.add(add, wlock=wlock)
651 repo.remove(remove, wlock=wlock)
651 repo.remove(remove, wlock=wlock)
652
652
653 def annotate(ui, repo, *pats, **opts):
653 def annotate(ui, repo, *pats, **opts):
654 """show changeset information per file line
654 """show changeset information per file line
655
655
656 List changes in files, showing the revision id responsible for each line
656 List changes in files, showing the revision id responsible for each line
657
657
658 This command is useful to discover who did a change or when a change took
658 This command is useful to discover who did a change or when a change took
659 place.
659 place.
660
660
661 Without the -a option, annotate will avoid processing files it
661 Without the -a option, annotate will avoid processing files it
662 detects as binary. With -a, annotate will generate an annotation
662 detects as binary. With -a, annotate will generate an annotation
663 anyway, probably with undesirable results.
663 anyway, probably with undesirable results.
664 """
664 """
665 def getnode(rev):
665 def getnode(rev):
666 return short(repo.changelog.node(rev))
666 return short(repo.changelog.node(rev))
667
667
668 ucache = {}
668 ucache = {}
669 def getname(rev):
669 def getname(rev):
670 cl = repo.changelog.read(repo.changelog.node(rev))
670 cl = repo.changelog.read(repo.changelog.node(rev))
671 return trimuser(ui, cl[1], rev, ucache)
671 return trimuser(ui, cl[1], rev, ucache)
672
672
673 dcache = {}
673 dcache = {}
674 def getdate(rev):
674 def getdate(rev):
675 datestr = dcache.get(rev)
675 datestr = dcache.get(rev)
676 if datestr is None:
676 if datestr is None:
677 cl = repo.changelog.read(repo.changelog.node(rev))
677 cl = repo.changelog.read(repo.changelog.node(rev))
678 datestr = dcache[rev] = util.datestr(cl[2])
678 datestr = dcache[rev] = util.datestr(cl[2])
679 return datestr
679 return datestr
680
680
681 if not pats:
681 if not pats:
682 raise util.Abort(_('at least one file name or pattern required'))
682 raise util.Abort(_('at least one file name or pattern required'))
683
683
684 opmap = [['user', getname], ['number', str], ['changeset', getnode],
684 opmap = [['user', getname], ['number', str], ['changeset', getnode],
685 ['date', getdate]]
685 ['date', getdate]]
686 if not opts['user'] and not opts['changeset'] and not opts['date']:
686 if not opts['user'] and not opts['changeset'] and not opts['date']:
687 opts['number'] = 1
687 opts['number'] = 1
688
688
689 if opts['rev']:
689 if opts['rev']:
690 node = repo.changelog.lookup(opts['rev'])
690 node = repo.changelog.lookup(opts['rev'])
691 else:
691 else:
692 node = repo.dirstate.parents()[0]
692 node = repo.dirstate.parents()[0]
693 change = repo.changelog.read(node)
693 change = repo.changelog.read(node)
694 mmap = repo.manifest.read(change[0])
694 mmap = repo.manifest.read(change[0])
695
695
696 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
696 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
697 f = repo.file(abs)
697 f = repo.file(abs)
698 if not opts['text'] and util.binary(f.read(mmap[abs])):
698 if not opts['text'] and util.binary(f.read(mmap[abs])):
699 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
699 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
700 continue
700 continue
701
701
702 lines = f.annotate(mmap[abs])
702 lines = f.annotate(mmap[abs])
703 pieces = []
703 pieces = []
704
704
705 for o, f in opmap:
705 for o, f in opmap:
706 if opts[o]:
706 if opts[o]:
707 l = [f(n) for n, dummy in lines]
707 l = [f(n) for n, dummy in lines]
708 if l:
708 if l:
709 m = max(map(len, l))
709 m = max(map(len, l))
710 pieces.append(["%*s" % (m, x) for x in l])
710 pieces.append(["%*s" % (m, x) for x in l])
711
711
712 if pieces:
712 if pieces:
713 for p, l in zip(zip(*pieces), lines):
713 for p, l in zip(zip(*pieces), lines):
714 ui.write("%s: %s" % (" ".join(p), l[1]))
714 ui.write("%s: %s" % (" ".join(p), l[1]))
715
715
716 def archive(ui, repo, dest, **opts):
716 def archive(ui, repo, dest, **opts):
717 '''create unversioned archive of a repository revision
717 '''create unversioned archive of a repository revision
718
718
719 By default, the revision used is the parent of the working
719 By default, the revision used is the parent of the working
720 directory; use "-r" to specify a different revision.
720 directory; use "-r" to specify a different revision.
721
721
722 To specify the type of archive to create, use "-t". Valid
722 To specify the type of archive to create, use "-t". Valid
723 types are:
723 types are:
724
724
725 "files" (default): a directory full of files
725 "files" (default): a directory full of files
726 "tar": tar archive, uncompressed
726 "tar": tar archive, uncompressed
727 "tbz2": tar archive, compressed using bzip2
727 "tbz2": tar archive, compressed using bzip2
728 "tgz": tar archive, compressed using gzip
728 "tgz": tar archive, compressed using gzip
729 "uzip": zip archive, uncompressed
729 "uzip": zip archive, uncompressed
730 "zip": zip archive, compressed using deflate
730 "zip": zip archive, compressed using deflate
731
731
732 The exact name of the destination archive or directory is given
732 The exact name of the destination archive or directory is given
733 using a format string; see "hg help export" for details.
733 using a format string; see "hg help export" for details.
734
734
735 Each member added to an archive file has a directory prefix
735 Each member added to an archive file has a directory prefix
736 prepended. Use "-p" to specify a format string for the prefix.
736 prepended. Use "-p" to specify a format string for the prefix.
737 The default is the basename of the archive, with suffixes removed.
737 The default is the basename of the archive, with suffixes removed.
738 '''
738 '''
739
739
740 if opts['rev']:
740 if opts['rev']:
741 node = repo.lookup(opts['rev'])
741 node = repo.lookup(opts['rev'])
742 else:
742 else:
743 node, p2 = repo.dirstate.parents()
743 node, p2 = repo.dirstate.parents()
744 if p2 != nullid:
744 if p2 != nullid:
745 raise util.Abort(_('uncommitted merge - please provide a '
745 raise util.Abort(_('uncommitted merge - please provide a '
746 'specific revision'))
746 'specific revision'))
747
747
748 dest = make_filename(repo, repo.changelog, dest, node)
748 dest = make_filename(repo, repo.changelog, dest, node)
749 prefix = make_filename(repo, repo.changelog, opts['prefix'], node)
749 prefix = make_filename(repo, repo.changelog, opts['prefix'], node)
750 if os.path.realpath(dest) == repo.root:
750 if os.path.realpath(dest) == repo.root:
751 raise util.Abort(_('repository root cannot be destination'))
751 raise util.Abort(_('repository root cannot be destination'))
752 dummy, matchfn, dummy = matchpats(repo, [], opts)
752 dummy, matchfn, dummy = matchpats(repo, [], opts)
753 archival.archive(repo, dest, node, opts.get('type') or 'files',
753 archival.archive(repo, dest, node, opts.get('type') or 'files',
754 not opts['no_decode'], matchfn, prefix)
754 not opts['no_decode'], matchfn, prefix)
755
755
756 def backout(ui, repo, rev, **opts):
756 def backout(ui, repo, rev, **opts):
757 '''reverse effect of earlier changeset
757 '''reverse effect of earlier changeset
758
758
759 Commit the backed out changes as a new changeset. The new
759 Commit the backed out changes as a new changeset. The new
760 changeset is a child of the backed out changeset.
760 changeset is a child of the backed out changeset.
761
761
762 If you back out a changeset other than the tip, a new head is
762 If you back out a changeset other than the tip, a new head is
763 created. This head is the parent of the working directory. If
763 created. This head is the parent of the working directory. If
764 you back out an old changeset, your working directory will appear
764 you back out an old changeset, your working directory will appear
765 old after the backout. You should merge the backout changeset
765 old after the backout. You should merge the backout changeset
766 with another head.
766 with another head.
767
767
768 The --merge option remembers the parent of the working directory
768 The --merge option remembers the parent of the working directory
769 before starting the backout, then merges the new head with that
769 before starting the backout, then merges the new head with that
770 changeset afterwards. This saves you from doing the merge by
770 changeset afterwards. This saves you from doing the merge by
771 hand. The result of this merge is not committed, as for a normal
771 hand. The result of this merge is not committed, as for a normal
772 merge.'''
772 merge.'''
773
773
774 bail_if_changed(repo)
774 bail_if_changed(repo)
775 op1, op2 = repo.dirstate.parents()
775 op1, op2 = repo.dirstate.parents()
776 if op2 != nullid:
776 if op2 != nullid:
777 raise util.Abort(_('outstanding uncommitted merge'))
777 raise util.Abort(_('outstanding uncommitted merge'))
778 node = repo.lookup(rev)
778 node = repo.lookup(rev)
779 parent, p2 = repo.changelog.parents(node)
779 parent, p2 = repo.changelog.parents(node)
780 if parent == nullid:
780 if parent == nullid:
781 raise util.Abort(_('cannot back out a change with no parents'))
781 raise util.Abort(_('cannot back out a change with no parents'))
782 if p2 != nullid:
782 if p2 != nullid:
783 raise util.Abort(_('cannot back out a merge'))
783 raise util.Abort(_('cannot back out a merge'))
784 repo.update(node, force=True, show_stats=False)
784 repo.update(node, force=True, show_stats=False)
785 revert_opts = opts.copy()
785 revert_opts = opts.copy()
786 revert_opts['rev'] = hex(parent)
786 revert_opts['rev'] = hex(parent)
787 revert(ui, repo, **revert_opts)
787 revert(ui, repo, **revert_opts)
788 commit_opts = opts.copy()
788 commit_opts = opts.copy()
789 commit_opts['addremove'] = False
789 commit_opts['addremove'] = False
790 if not commit_opts['message'] and not commit_opts['logfile']:
790 if not commit_opts['message'] and not commit_opts['logfile']:
791 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
791 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
792 commit_opts['force_editor'] = True;
792 commit(ui, repo, **commit_opts)
793 commit(ui, repo, **commit_opts)
793 def nice(node):
794 def nice(node):
794 return '%d:%s' % (repo.changelog.rev(node), short(node))
795 return '%d:%s' % (repo.changelog.rev(node), short(node))
795 ui.status(_('changeset %s backs out changeset %s\n') %
796 ui.status(_('changeset %s backs out changeset %s\n') %
796 (nice(repo.changelog.tip()), nice(node)))
797 (nice(repo.changelog.tip()), nice(node)))
797 if opts['merge'] and op1 != node:
798 if opts['merge'] and op1 != node:
798 ui.status(_('merging with changeset %s\n') % nice(op1))
799 ui.status(_('merging with changeset %s\n') % nice(op1))
799 update(ui, repo, hex(op1), **opts)
800 update(ui, repo, hex(op1), **opts)
800
801
801 def bundle(ui, repo, fname, dest="default-push", **opts):
802 def bundle(ui, repo, fname, dest="default-push", **opts):
802 """create a changegroup file
803 """create a changegroup file
803
804
804 Generate a compressed changegroup file collecting all changesets
805 Generate a compressed changegroup file collecting all changesets
805 not found in the other repository.
806 not found in the other repository.
806
807
807 This file can then be transferred using conventional means and
808 This file can then be transferred using conventional means and
808 applied to another repository with the unbundle command. This is
809 applied to another repository with the unbundle command. This is
809 useful when native push and pull are not available or when
810 useful when native push and pull are not available or when
810 exporting an entire repository is undesirable. The standard file
811 exporting an entire repository is undesirable. The standard file
811 extension is ".hg".
812 extension is ".hg".
812
813
813 Unlike import/export, this exactly preserves all changeset
814 Unlike import/export, this exactly preserves all changeset
814 contents including permissions, rename data, and revision history.
815 contents including permissions, rename data, and revision history.
815 """
816 """
816 dest = ui.expandpath(dest)
817 dest = ui.expandpath(dest)
817 other = hg.repository(ui, dest)
818 other = hg.repository(ui, dest)
818 o = repo.findoutgoing(other, force=opts['force'])
819 o = repo.findoutgoing(other, force=opts['force'])
819 cg = repo.changegroup(o, 'bundle')
820 cg = repo.changegroup(o, 'bundle')
820 write_bundle(cg, fname)
821 write_bundle(cg, fname)
821
822
822 def cat(ui, repo, file1, *pats, **opts):
823 def cat(ui, repo, file1, *pats, **opts):
823 """output the latest or given revisions of files
824 """output the latest or given revisions of files
824
825
825 Print the specified files as they were at the given revision.
826 Print the specified files as they were at the given revision.
826 If no revision is given then the tip is used.
827 If no revision is given then the tip is used.
827
828
828 Output may be to a file, in which case the name of the file is
829 Output may be to a file, in which case the name of the file is
829 given using a format string. The formatting rules are the same as
830 given using a format string. The formatting rules are the same as
830 for the export command, with the following additions:
831 for the export command, with the following additions:
831
832
832 %s basename of file being printed
833 %s basename of file being printed
833 %d dirname of file being printed, or '.' if in repo root
834 %d dirname of file being printed, or '.' if in repo root
834 %p root-relative path name of file being printed
835 %p root-relative path name of file being printed
835 """
836 """
836 mf = {}
837 mf = {}
837 rev = opts['rev']
838 rev = opts['rev']
838 if rev:
839 if rev:
839 node = repo.lookup(rev)
840 node = repo.lookup(rev)
840 else:
841 else:
841 node = repo.changelog.tip()
842 node = repo.changelog.tip()
842 change = repo.changelog.read(node)
843 change = repo.changelog.read(node)
843 mf = repo.manifest.read(change[0])
844 mf = repo.manifest.read(change[0])
844 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
845 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
845 r = repo.file(abs)
846 r = repo.file(abs)
846 n = mf[abs]
847 n = mf[abs]
847 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
848 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
848 fp.write(r.read(n))
849 fp.write(r.read(n))
849
850
850 def clone(ui, source, dest=None, **opts):
851 def clone(ui, source, dest=None, **opts):
851 """make a copy of an existing repository
852 """make a copy of an existing repository
852
853
853 Create a copy of an existing repository in a new directory.
854 Create a copy of an existing repository in a new directory.
854
855
855 If no destination directory name is specified, it defaults to the
856 If no destination directory name is specified, it defaults to the
856 basename of the source.
857 basename of the source.
857
858
858 The location of the source is added to the new repository's
859 The location of the source is added to the new repository's
859 .hg/hgrc file, as the default to be used for future pulls.
860 .hg/hgrc file, as the default to be used for future pulls.
860
861
861 For efficiency, hardlinks are used for cloning whenever the source
862 For efficiency, hardlinks are used for cloning whenever the source
862 and destination are on the same filesystem. Some filesystems,
863 and destination are on the same filesystem. Some filesystems,
863 such as AFS, implement hardlinking incorrectly, but do not report
864 such as AFS, implement hardlinking incorrectly, but do not report
864 errors. In these cases, use the --pull option to avoid
865 errors. In these cases, use the --pull option to avoid
865 hardlinking.
866 hardlinking.
866
867
867 See pull for valid source format details.
868 See pull for valid source format details.
868 """
869 """
869 if dest is None:
870 if dest is None:
870 dest = os.path.basename(os.path.normpath(source))
871 dest = os.path.basename(os.path.normpath(source))
871
872
872 if os.path.exists(dest):
873 if os.path.exists(dest):
873 raise util.Abort(_("destination '%s' already exists"), dest)
874 raise util.Abort(_("destination '%s' already exists"), dest)
874
875
875 dest = os.path.realpath(dest)
876 dest = os.path.realpath(dest)
876
877
877 class Dircleanup(object):
878 class Dircleanup(object):
878 def __init__(self, dir_):
879 def __init__(self, dir_):
879 self.rmtree = shutil.rmtree
880 self.rmtree = shutil.rmtree
880 self.dir_ = dir_
881 self.dir_ = dir_
881 os.mkdir(dir_)
882 os.mkdir(dir_)
882 def close(self):
883 def close(self):
883 self.dir_ = None
884 self.dir_ = None
884 def __del__(self):
885 def __del__(self):
885 if self.dir_:
886 if self.dir_:
886 self.rmtree(self.dir_, True)
887 self.rmtree(self.dir_, True)
887
888
888 if opts['ssh']:
889 if opts['ssh']:
889 ui.setconfig("ui", "ssh", opts['ssh'])
890 ui.setconfig("ui", "ssh", opts['ssh'])
890 if opts['remotecmd']:
891 if opts['remotecmd']:
891 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
892 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
892
893
893 source = ui.expandpath(source)
894 source = ui.expandpath(source)
894
895
895 d = Dircleanup(dest)
896 d = Dircleanup(dest)
896 abspath = source
897 abspath = source
897 other = hg.repository(ui, source)
898 other = hg.repository(ui, source)
898
899
899 copy = False
900 copy = False
900 if other.dev() != -1:
901 if other.dev() != -1:
901 abspath = os.path.abspath(source)
902 abspath = os.path.abspath(source)
902 if not opts['pull'] and not opts['rev']:
903 if not opts['pull'] and not opts['rev']:
903 copy = True
904 copy = True
904
905
905 if copy:
906 if copy:
906 try:
907 try:
907 # we use a lock here because if we race with commit, we
908 # we use a lock here because if we race with commit, we
908 # can end up with extra data in the cloned revlogs that's
909 # can end up with extra data in the cloned revlogs that's
909 # not pointed to by changesets, thus causing verify to
910 # not pointed to by changesets, thus causing verify to
910 # fail
911 # fail
911 l1 = other.lock()
912 l1 = other.lock()
912 except lock.LockException:
913 except lock.LockException:
913 copy = False
914 copy = False
914
915
915 if copy:
916 if copy:
916 # we lock here to avoid premature writing to the target
917 # we lock here to avoid premature writing to the target
917 os.mkdir(os.path.join(dest, ".hg"))
918 os.mkdir(os.path.join(dest, ".hg"))
918 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
919 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
919
920
920 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
921 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
921 for f in files.split():
922 for f in files.split():
922 src = os.path.join(source, ".hg", f)
923 src = os.path.join(source, ".hg", f)
923 dst = os.path.join(dest, ".hg", f)
924 dst = os.path.join(dest, ".hg", f)
924 try:
925 try:
925 util.copyfiles(src, dst)
926 util.copyfiles(src, dst)
926 except OSError, inst:
927 except OSError, inst:
927 if inst.errno != errno.ENOENT:
928 if inst.errno != errno.ENOENT:
928 raise
929 raise
929
930
930 repo = hg.repository(ui, dest)
931 repo = hg.repository(ui, dest)
931
932
932 else:
933 else:
933 revs = None
934 revs = None
934 if opts['rev']:
935 if opts['rev']:
935 if not other.local():
936 if not other.local():
936 error = _("clone -r not supported yet for remote repositories.")
937 error = _("clone -r not supported yet for remote repositories.")
937 raise util.Abort(error)
938 raise util.Abort(error)
938 else:
939 else:
939 revs = [other.lookup(rev) for rev in opts['rev']]
940 revs = [other.lookup(rev) for rev in opts['rev']]
940 repo = hg.repository(ui, dest, create=1)
941 repo = hg.repository(ui, dest, create=1)
941 repo.pull(other, heads = revs)
942 repo.pull(other, heads = revs)
942
943
943 f = repo.opener("hgrc", "w", text=True)
944 f = repo.opener("hgrc", "w", text=True)
944 f.write("[paths]\n")
945 f.write("[paths]\n")
945 f.write("default = %s\n" % abspath)
946 f.write("default = %s\n" % abspath)
946 f.close()
947 f.close()
947
948
948 if not opts['noupdate']:
949 if not opts['noupdate']:
949 update(repo.ui, repo)
950 update(repo.ui, repo)
950
951
951 d.close()
952 d.close()
952
953
953 def commit(ui, repo, *pats, **opts):
954 def commit(ui, repo, *pats, **opts):
954 """commit the specified files or all outstanding changes
955 """commit the specified files or all outstanding changes
955
956
956 Commit changes to the given files into the repository.
957 Commit changes to the given files into the repository.
957
958
958 If a list of files is omitted, all changes reported by "hg status"
959 If a list of files is omitted, all changes reported by "hg status"
959 will be committed.
960 will be committed.
960
961
961 If no commit message is specified, the editor configured in your hgrc
962 If no commit message is specified, the editor configured in your hgrc
962 or in the EDITOR environment variable is started to enter a message.
963 or in the EDITOR environment variable is started to enter a message.
963 """
964 """
964 message = opts['message']
965 message = opts['message']
965 logfile = opts['logfile']
966 logfile = opts['logfile']
966
967
967 if message and logfile:
968 if message and logfile:
968 raise util.Abort(_('options --message and --logfile are mutually '
969 raise util.Abort(_('options --message and --logfile are mutually '
969 'exclusive'))
970 'exclusive'))
970 if not message and logfile:
971 if not message and logfile:
971 try:
972 try:
972 if logfile == '-':
973 if logfile == '-':
973 message = sys.stdin.read()
974 message = sys.stdin.read()
974 else:
975 else:
975 message = open(logfile).read()
976 message = open(logfile).read()
976 except IOError, inst:
977 except IOError, inst:
977 raise util.Abort(_("can't read commit message '%s': %s") %
978 raise util.Abort(_("can't read commit message '%s': %s") %
978 (logfile, inst.strerror))
979 (logfile, inst.strerror))
979
980
980 if opts['addremove']:
981 if opts['addremove']:
981 addremove_lock(ui, repo, pats, opts)
982 addremove_lock(ui, repo, pats, opts)
982 fns, match, anypats = matchpats(repo, pats, opts)
983 fns, match, anypats = matchpats(repo, pats, opts)
983 if pats:
984 if pats:
984 modified, added, removed, deleted, unknown = (
985 modified, added, removed, deleted, unknown = (
985 repo.changes(files=fns, match=match))
986 repo.changes(files=fns, match=match))
986 files = modified + added + removed
987 files = modified + added + removed
987 else:
988 else:
988 files = []
989 files = []
989 try:
990 try:
990 repo.commit(files, message, opts['user'], opts['date'], match)
991 try:
992 force_editor = opts['force_editor']
993 except KeyError:
994 force_editor = False
995 repo.commit(files, message, opts['user'], opts['date'], match,
996 force_editor=force_editor)
991 except ValueError, inst:
997 except ValueError, inst:
992 raise util.Abort(str(inst))
998 raise util.Abort(str(inst))
993
999
994 def docopy(ui, repo, pats, opts, wlock):
1000 def docopy(ui, repo, pats, opts, wlock):
995 # called with the repo lock held
1001 # called with the repo lock held
996 cwd = repo.getcwd()
1002 cwd = repo.getcwd()
997 errors = 0
1003 errors = 0
998 copied = []
1004 copied = []
999 targets = {}
1005 targets = {}
1000
1006
1001 def okaytocopy(abs, rel, exact):
1007 def okaytocopy(abs, rel, exact):
1002 reasons = {'?': _('is not managed'),
1008 reasons = {'?': _('is not managed'),
1003 'a': _('has been marked for add'),
1009 'a': _('has been marked for add'),
1004 'r': _('has been marked for remove')}
1010 'r': _('has been marked for remove')}
1005 state = repo.dirstate.state(abs)
1011 state = repo.dirstate.state(abs)
1006 reason = reasons.get(state)
1012 reason = reasons.get(state)
1007 if reason:
1013 if reason:
1008 if state == 'a':
1014 if state == 'a':
1009 origsrc = repo.dirstate.copied(abs)
1015 origsrc = repo.dirstate.copied(abs)
1010 if origsrc is not None:
1016 if origsrc is not None:
1011 return origsrc
1017 return origsrc
1012 if exact:
1018 if exact:
1013 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1019 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1014 else:
1020 else:
1015 return abs
1021 return abs
1016
1022
1017 def copy(origsrc, abssrc, relsrc, target, exact):
1023 def copy(origsrc, abssrc, relsrc, target, exact):
1018 abstarget = util.canonpath(repo.root, cwd, target)
1024 abstarget = util.canonpath(repo.root, cwd, target)
1019 reltarget = util.pathto(cwd, abstarget)
1025 reltarget = util.pathto(cwd, abstarget)
1020 prevsrc = targets.get(abstarget)
1026 prevsrc = targets.get(abstarget)
1021 if prevsrc is not None:
1027 if prevsrc is not None:
1022 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1028 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1023 (reltarget, abssrc, prevsrc))
1029 (reltarget, abssrc, prevsrc))
1024 return
1030 return
1025 if (not opts['after'] and os.path.exists(reltarget) or
1031 if (not opts['after'] and os.path.exists(reltarget) or
1026 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1032 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1027 if not opts['force']:
1033 if not opts['force']:
1028 ui.warn(_('%s: not overwriting - file exists\n') %
1034 ui.warn(_('%s: not overwriting - file exists\n') %
1029 reltarget)
1035 reltarget)
1030 return
1036 return
1031 if not opts['after']:
1037 if not opts['after']:
1032 os.unlink(reltarget)
1038 os.unlink(reltarget)
1033 if opts['after']:
1039 if opts['after']:
1034 if not os.path.exists(reltarget):
1040 if not os.path.exists(reltarget):
1035 return
1041 return
1036 else:
1042 else:
1037 targetdir = os.path.dirname(reltarget) or '.'
1043 targetdir = os.path.dirname(reltarget) or '.'
1038 if not os.path.isdir(targetdir):
1044 if not os.path.isdir(targetdir):
1039 os.makedirs(targetdir)
1045 os.makedirs(targetdir)
1040 try:
1046 try:
1041 restore = repo.dirstate.state(abstarget) == 'r'
1047 restore = repo.dirstate.state(abstarget) == 'r'
1042 if restore:
1048 if restore:
1043 repo.undelete([abstarget], wlock)
1049 repo.undelete([abstarget], wlock)
1044 try:
1050 try:
1045 shutil.copyfile(relsrc, reltarget)
1051 shutil.copyfile(relsrc, reltarget)
1046 shutil.copymode(relsrc, reltarget)
1052 shutil.copymode(relsrc, reltarget)
1047 restore = False
1053 restore = False
1048 finally:
1054 finally:
1049 if restore:
1055 if restore:
1050 repo.remove([abstarget], wlock)
1056 repo.remove([abstarget], wlock)
1051 except shutil.Error, inst:
1057 except shutil.Error, inst:
1052 raise util.Abort(str(inst))
1058 raise util.Abort(str(inst))
1053 except IOError, inst:
1059 except IOError, inst:
1054 if inst.errno == errno.ENOENT:
1060 if inst.errno == errno.ENOENT:
1055 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1061 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1056 else:
1062 else:
1057 ui.warn(_('%s: cannot copy - %s\n') %
1063 ui.warn(_('%s: cannot copy - %s\n') %
1058 (relsrc, inst.strerror))
1064 (relsrc, inst.strerror))
1059 errors += 1
1065 errors += 1
1060 return
1066 return
1061 if ui.verbose or not exact:
1067 if ui.verbose or not exact:
1062 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1068 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1063 targets[abstarget] = abssrc
1069 targets[abstarget] = abssrc
1064 if abstarget != origsrc:
1070 if abstarget != origsrc:
1065 repo.copy(origsrc, abstarget, wlock)
1071 repo.copy(origsrc, abstarget, wlock)
1066 copied.append((abssrc, relsrc, exact))
1072 copied.append((abssrc, relsrc, exact))
1067
1073
1068 def targetpathfn(pat, dest, srcs):
1074 def targetpathfn(pat, dest, srcs):
1069 if os.path.isdir(pat):
1075 if os.path.isdir(pat):
1070 abspfx = util.canonpath(repo.root, cwd, pat)
1076 abspfx = util.canonpath(repo.root, cwd, pat)
1071 if destdirexists:
1077 if destdirexists:
1072 striplen = len(os.path.split(abspfx)[0])
1078 striplen = len(os.path.split(abspfx)[0])
1073 else:
1079 else:
1074 striplen = len(abspfx)
1080 striplen = len(abspfx)
1075 if striplen:
1081 if striplen:
1076 striplen += len(os.sep)
1082 striplen += len(os.sep)
1077 res = lambda p: os.path.join(dest, p[striplen:])
1083 res = lambda p: os.path.join(dest, p[striplen:])
1078 elif destdirexists:
1084 elif destdirexists:
1079 res = lambda p: os.path.join(dest, os.path.basename(p))
1085 res = lambda p: os.path.join(dest, os.path.basename(p))
1080 else:
1086 else:
1081 res = lambda p: dest
1087 res = lambda p: dest
1082 return res
1088 return res
1083
1089
1084 def targetpathafterfn(pat, dest, srcs):
1090 def targetpathafterfn(pat, dest, srcs):
1085 if util.patkind(pat, None)[0]:
1091 if util.patkind(pat, None)[0]:
1086 # a mercurial pattern
1092 # a mercurial pattern
1087 res = lambda p: os.path.join(dest, os.path.basename(p))
1093 res = lambda p: os.path.join(dest, os.path.basename(p))
1088 else:
1094 else:
1089 abspfx = util.canonpath(repo.root, cwd, pat)
1095 abspfx = util.canonpath(repo.root, cwd, pat)
1090 if len(abspfx) < len(srcs[0][0]):
1096 if len(abspfx) < len(srcs[0][0]):
1091 # A directory. Either the target path contains the last
1097 # A directory. Either the target path contains the last
1092 # component of the source path or it does not.
1098 # component of the source path or it does not.
1093 def evalpath(striplen):
1099 def evalpath(striplen):
1094 score = 0
1100 score = 0
1095 for s in srcs:
1101 for s in srcs:
1096 t = os.path.join(dest, s[0][striplen:])
1102 t = os.path.join(dest, s[0][striplen:])
1097 if os.path.exists(t):
1103 if os.path.exists(t):
1098 score += 1
1104 score += 1
1099 return score
1105 return score
1100
1106
1101 striplen = len(abspfx)
1107 striplen = len(abspfx)
1102 if striplen:
1108 if striplen:
1103 striplen += len(os.sep)
1109 striplen += len(os.sep)
1104 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1110 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1105 score = evalpath(striplen)
1111 score = evalpath(striplen)
1106 striplen1 = len(os.path.split(abspfx)[0])
1112 striplen1 = len(os.path.split(abspfx)[0])
1107 if striplen1:
1113 if striplen1:
1108 striplen1 += len(os.sep)
1114 striplen1 += len(os.sep)
1109 if evalpath(striplen1) > score:
1115 if evalpath(striplen1) > score:
1110 striplen = striplen1
1116 striplen = striplen1
1111 res = lambda p: os.path.join(dest, p[striplen:])
1117 res = lambda p: os.path.join(dest, p[striplen:])
1112 else:
1118 else:
1113 # a file
1119 # a file
1114 if destdirexists:
1120 if destdirexists:
1115 res = lambda p: os.path.join(dest, os.path.basename(p))
1121 res = lambda p: os.path.join(dest, os.path.basename(p))
1116 else:
1122 else:
1117 res = lambda p: dest
1123 res = lambda p: dest
1118 return res
1124 return res
1119
1125
1120
1126
1121 pats = list(pats)
1127 pats = list(pats)
1122 if not pats:
1128 if not pats:
1123 raise util.Abort(_('no source or destination specified'))
1129 raise util.Abort(_('no source or destination specified'))
1124 if len(pats) == 1:
1130 if len(pats) == 1:
1125 raise util.Abort(_('no destination specified'))
1131 raise util.Abort(_('no destination specified'))
1126 dest = pats.pop()
1132 dest = pats.pop()
1127 destdirexists = os.path.isdir(dest)
1133 destdirexists = os.path.isdir(dest)
1128 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1134 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1129 raise util.Abort(_('with multiple sources, destination must be an '
1135 raise util.Abort(_('with multiple sources, destination must be an '
1130 'existing directory'))
1136 'existing directory'))
1131 if opts['after']:
1137 if opts['after']:
1132 tfn = targetpathafterfn
1138 tfn = targetpathafterfn
1133 else:
1139 else:
1134 tfn = targetpathfn
1140 tfn = targetpathfn
1135 copylist = []
1141 copylist = []
1136 for pat in pats:
1142 for pat in pats:
1137 srcs = []
1143 srcs = []
1138 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1144 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1139 origsrc = okaytocopy(abssrc, relsrc, exact)
1145 origsrc = okaytocopy(abssrc, relsrc, exact)
1140 if origsrc:
1146 if origsrc:
1141 srcs.append((origsrc, abssrc, relsrc, exact))
1147 srcs.append((origsrc, abssrc, relsrc, exact))
1142 if not srcs:
1148 if not srcs:
1143 continue
1149 continue
1144 copylist.append((tfn(pat, dest, srcs), srcs))
1150 copylist.append((tfn(pat, dest, srcs), srcs))
1145 if not copylist:
1151 if not copylist:
1146 raise util.Abort(_('no files to copy'))
1152 raise util.Abort(_('no files to copy'))
1147
1153
1148 for targetpath, srcs in copylist:
1154 for targetpath, srcs in copylist:
1149 for origsrc, abssrc, relsrc, exact in srcs:
1155 for origsrc, abssrc, relsrc, exact in srcs:
1150 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1156 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1151
1157
1152 if errors:
1158 if errors:
1153 ui.warn(_('(consider using --after)\n'))
1159 ui.warn(_('(consider using --after)\n'))
1154 return errors, copied
1160 return errors, copied
1155
1161
1156 def copy(ui, repo, *pats, **opts):
1162 def copy(ui, repo, *pats, **opts):
1157 """mark files as copied for the next commit
1163 """mark files as copied for the next commit
1158
1164
1159 Mark dest as having copies of source files. If dest is a
1165 Mark dest as having copies of source files. If dest is a
1160 directory, copies are put in that directory. If dest is a file,
1166 directory, copies are put in that directory. If dest is a file,
1161 there can only be one source.
1167 there can only be one source.
1162
1168
1163 By default, this command copies the contents of files as they
1169 By default, this command copies the contents of files as they
1164 stand in the working directory. If invoked with --after, the
1170 stand in the working directory. If invoked with --after, the
1165 operation is recorded, but no copying is performed.
1171 operation is recorded, but no copying is performed.
1166
1172
1167 This command takes effect in the next commit.
1173 This command takes effect in the next commit.
1168
1174
1169 NOTE: This command should be treated as experimental. While it
1175 NOTE: This command should be treated as experimental. While it
1170 should properly record copied files, this information is not yet
1176 should properly record copied files, this information is not yet
1171 fully used by merge, nor fully reported by log.
1177 fully used by merge, nor fully reported by log.
1172 """
1178 """
1173 wlock = repo.wlock(0)
1179 wlock = repo.wlock(0)
1174 errs, copied = docopy(ui, repo, pats, opts, wlock)
1180 errs, copied = docopy(ui, repo, pats, opts, wlock)
1175 return errs
1181 return errs
1176
1182
1177 def debugancestor(ui, index, rev1, rev2):
1183 def debugancestor(ui, index, rev1, rev2):
1178 """find the ancestor revision of two revisions in a given index"""
1184 """find the ancestor revision of two revisions in a given index"""
1179 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1185 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1180 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1186 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1181 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1187 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1182
1188
1183 def debugcomplete(ui, cmd='', **opts):
1189 def debugcomplete(ui, cmd='', **opts):
1184 """returns the completion list associated with the given command"""
1190 """returns the completion list associated with the given command"""
1185
1191
1186 if opts['options']:
1192 if opts['options']:
1187 options = []
1193 options = []
1188 otables = [globalopts]
1194 otables = [globalopts]
1189 if cmd:
1195 if cmd:
1190 aliases, entry = find(cmd)
1196 aliases, entry = find(cmd)
1191 otables.append(entry[1])
1197 otables.append(entry[1])
1192 for t in otables:
1198 for t in otables:
1193 for o in t:
1199 for o in t:
1194 if o[0]:
1200 if o[0]:
1195 options.append('-%s' % o[0])
1201 options.append('-%s' % o[0])
1196 options.append('--%s' % o[1])
1202 options.append('--%s' % o[1])
1197 ui.write("%s\n" % "\n".join(options))
1203 ui.write("%s\n" % "\n".join(options))
1198 return
1204 return
1199
1205
1200 clist = findpossible(cmd).keys()
1206 clist = findpossible(cmd).keys()
1201 clist.sort()
1207 clist.sort()
1202 ui.write("%s\n" % "\n".join(clist))
1208 ui.write("%s\n" % "\n".join(clist))
1203
1209
1204 def debugrebuildstate(ui, repo, rev=None):
1210 def debugrebuildstate(ui, repo, rev=None):
1205 """rebuild the dirstate as it would look like for the given revision"""
1211 """rebuild the dirstate as it would look like for the given revision"""
1206 if not rev:
1212 if not rev:
1207 rev = repo.changelog.tip()
1213 rev = repo.changelog.tip()
1208 else:
1214 else:
1209 rev = repo.lookup(rev)
1215 rev = repo.lookup(rev)
1210 change = repo.changelog.read(rev)
1216 change = repo.changelog.read(rev)
1211 n = change[0]
1217 n = change[0]
1212 files = repo.manifest.readflags(n)
1218 files = repo.manifest.readflags(n)
1213 wlock = repo.wlock()
1219 wlock = repo.wlock()
1214 repo.dirstate.rebuild(rev, files.iteritems())
1220 repo.dirstate.rebuild(rev, files.iteritems())
1215
1221
1216 def debugcheckstate(ui, repo):
1222 def debugcheckstate(ui, repo):
1217 """validate the correctness of the current dirstate"""
1223 """validate the correctness of the current dirstate"""
1218 parent1, parent2 = repo.dirstate.parents()
1224 parent1, parent2 = repo.dirstate.parents()
1219 repo.dirstate.read()
1225 repo.dirstate.read()
1220 dc = repo.dirstate.map
1226 dc = repo.dirstate.map
1221 keys = dc.keys()
1227 keys = dc.keys()
1222 keys.sort()
1228 keys.sort()
1223 m1n = repo.changelog.read(parent1)[0]
1229 m1n = repo.changelog.read(parent1)[0]
1224 m2n = repo.changelog.read(parent2)[0]
1230 m2n = repo.changelog.read(parent2)[0]
1225 m1 = repo.manifest.read(m1n)
1231 m1 = repo.manifest.read(m1n)
1226 m2 = repo.manifest.read(m2n)
1232 m2 = repo.manifest.read(m2n)
1227 errors = 0
1233 errors = 0
1228 for f in dc:
1234 for f in dc:
1229 state = repo.dirstate.state(f)
1235 state = repo.dirstate.state(f)
1230 if state in "nr" and f not in m1:
1236 if state in "nr" and f not in m1:
1231 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1237 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1232 errors += 1
1238 errors += 1
1233 if state in "a" and f in m1:
1239 if state in "a" and f in m1:
1234 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1240 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1235 errors += 1
1241 errors += 1
1236 if state in "m" and f not in m1 and f not in m2:
1242 if state in "m" and f not in m1 and f not in m2:
1237 ui.warn(_("%s in state %s, but not in either manifest\n") %
1243 ui.warn(_("%s in state %s, but not in either manifest\n") %
1238 (f, state))
1244 (f, state))
1239 errors += 1
1245 errors += 1
1240 for f in m1:
1246 for f in m1:
1241 state = repo.dirstate.state(f)
1247 state = repo.dirstate.state(f)
1242 if state not in "nrm":
1248 if state not in "nrm":
1243 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1249 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1244 errors += 1
1250 errors += 1
1245 if errors:
1251 if errors:
1246 error = _(".hg/dirstate inconsistent with current parent's manifest")
1252 error = _(".hg/dirstate inconsistent with current parent's manifest")
1247 raise util.Abort(error)
1253 raise util.Abort(error)
1248
1254
1249 def debugconfig(ui, repo):
1255 def debugconfig(ui, repo):
1250 """show combined config settings from all hgrc files"""
1256 """show combined config settings from all hgrc files"""
1251 for section, name, value in ui.walkconfig():
1257 for section, name, value in ui.walkconfig():
1252 ui.write('%s.%s=%s\n' % (section, name, value))
1258 ui.write('%s.%s=%s\n' % (section, name, value))
1253
1259
1254 def debugsetparents(ui, repo, rev1, rev2=None):
1260 def debugsetparents(ui, repo, rev1, rev2=None):
1255 """manually set the parents of the current working directory
1261 """manually set the parents of the current working directory
1256
1262
1257 This is useful for writing repository conversion tools, but should
1263 This is useful for writing repository conversion tools, but should
1258 be used with care.
1264 be used with care.
1259 """
1265 """
1260
1266
1261 if not rev2:
1267 if not rev2:
1262 rev2 = hex(nullid)
1268 rev2 = hex(nullid)
1263
1269
1264 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1270 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1265
1271
1266 def debugstate(ui, repo):
1272 def debugstate(ui, repo):
1267 """show the contents of the current dirstate"""
1273 """show the contents of the current dirstate"""
1268 repo.dirstate.read()
1274 repo.dirstate.read()
1269 dc = repo.dirstate.map
1275 dc = repo.dirstate.map
1270 keys = dc.keys()
1276 keys = dc.keys()
1271 keys.sort()
1277 keys.sort()
1272 for file_ in keys:
1278 for file_ in keys:
1273 ui.write("%c %3o %10d %s %s\n"
1279 ui.write("%c %3o %10d %s %s\n"
1274 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1280 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1275 time.strftime("%x %X",
1281 time.strftime("%x %X",
1276 time.localtime(dc[file_][3])), file_))
1282 time.localtime(dc[file_][3])), file_))
1277 for f in repo.dirstate.copies:
1283 for f in repo.dirstate.copies:
1278 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1284 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1279
1285
1280 def debugdata(ui, file_, rev):
1286 def debugdata(ui, file_, rev):
1281 """dump the contents of an data file revision"""
1287 """dump the contents of an data file revision"""
1282 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1288 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1283 file_[:-2] + ".i", file_, 0)
1289 file_[:-2] + ".i", file_, 0)
1284 try:
1290 try:
1285 ui.write(r.revision(r.lookup(rev)))
1291 ui.write(r.revision(r.lookup(rev)))
1286 except KeyError:
1292 except KeyError:
1287 raise util.Abort(_('invalid revision identifier %s'), rev)
1293 raise util.Abort(_('invalid revision identifier %s'), rev)
1288
1294
1289 def debugindex(ui, file_):
1295 def debugindex(ui, file_):
1290 """dump the contents of an index file"""
1296 """dump the contents of an index file"""
1291 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1297 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1292 ui.write(" rev offset length base linkrev" +
1298 ui.write(" rev offset length base linkrev" +
1293 " nodeid p1 p2\n")
1299 " nodeid p1 p2\n")
1294 for i in range(r.count()):
1300 for i in range(r.count()):
1295 node = r.node(i)
1301 node = r.node(i)
1296 pp = r.parents(node)
1302 pp = r.parents(node)
1297 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1303 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1298 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1304 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1299 short(node), short(pp[0]), short(pp[1])))
1305 short(node), short(pp[0]), short(pp[1])))
1300
1306
1301 def debugindexdot(ui, file_):
1307 def debugindexdot(ui, file_):
1302 """dump an index DAG as a .dot file"""
1308 """dump an index DAG as a .dot file"""
1303 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1309 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1304 ui.write("digraph G {\n")
1310 ui.write("digraph G {\n")
1305 for i in range(r.count()):
1311 for i in range(r.count()):
1306 e = r.index[i]
1312 e = r.index[i]
1307 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1313 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1308 if e[5] != nullid:
1314 if e[5] != nullid:
1309 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1315 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1310 ui.write("}\n")
1316 ui.write("}\n")
1311
1317
1312 def debugrename(ui, repo, file, rev=None):
1318 def debugrename(ui, repo, file, rev=None):
1313 """dump rename information"""
1319 """dump rename information"""
1314 r = repo.file(relpath(repo, [file])[0])
1320 r = repo.file(relpath(repo, [file])[0])
1315 if rev:
1321 if rev:
1316 try:
1322 try:
1317 # assume all revision numbers are for changesets
1323 # assume all revision numbers are for changesets
1318 n = repo.lookup(rev)
1324 n = repo.lookup(rev)
1319 change = repo.changelog.read(n)
1325 change = repo.changelog.read(n)
1320 m = repo.manifest.read(change[0])
1326 m = repo.manifest.read(change[0])
1321 n = m[relpath(repo, [file])[0]]
1327 n = m[relpath(repo, [file])[0]]
1322 except (hg.RepoError, KeyError):
1328 except (hg.RepoError, KeyError):
1323 n = r.lookup(rev)
1329 n = r.lookup(rev)
1324 else:
1330 else:
1325 n = r.tip()
1331 n = r.tip()
1326 m = r.renamed(n)
1332 m = r.renamed(n)
1327 if m:
1333 if m:
1328 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1334 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1329 else:
1335 else:
1330 ui.write(_("not renamed\n"))
1336 ui.write(_("not renamed\n"))
1331
1337
1332 def debugwalk(ui, repo, *pats, **opts):
1338 def debugwalk(ui, repo, *pats, **opts):
1333 """show how files match on given patterns"""
1339 """show how files match on given patterns"""
1334 items = list(walk(repo, pats, opts))
1340 items = list(walk(repo, pats, opts))
1335 if not items:
1341 if not items:
1336 return
1342 return
1337 fmt = '%%s %%-%ds %%-%ds %%s' % (
1343 fmt = '%%s %%-%ds %%-%ds %%s' % (
1338 max([len(abs) for (src, abs, rel, exact) in items]),
1344 max([len(abs) for (src, abs, rel, exact) in items]),
1339 max([len(rel) for (src, abs, rel, exact) in items]))
1345 max([len(rel) for (src, abs, rel, exact) in items]))
1340 for src, abs, rel, exact in items:
1346 for src, abs, rel, exact in items:
1341 line = fmt % (src, abs, rel, exact and 'exact' or '')
1347 line = fmt % (src, abs, rel, exact and 'exact' or '')
1342 ui.write("%s\n" % line.rstrip())
1348 ui.write("%s\n" % line.rstrip())
1343
1349
1344 def diff(ui, repo, *pats, **opts):
1350 def diff(ui, repo, *pats, **opts):
1345 """diff repository (or selected files)
1351 """diff repository (or selected files)
1346
1352
1347 Show differences between revisions for the specified files.
1353 Show differences between revisions for the specified files.
1348
1354
1349 Differences between files are shown using the unified diff format.
1355 Differences between files are shown using the unified diff format.
1350
1356
1351 When two revision arguments are given, then changes are shown
1357 When two revision arguments are given, then changes are shown
1352 between those revisions. If only one revision is specified then
1358 between those revisions. If only one revision is specified then
1353 that revision is compared to the working directory, and, when no
1359 that revision is compared to the working directory, and, when no
1354 revisions are specified, the working directory files are compared
1360 revisions are specified, the working directory files are compared
1355 to its parent.
1361 to its parent.
1356
1362
1357 Without the -a option, diff will avoid generating diffs of files
1363 Without the -a option, diff will avoid generating diffs of files
1358 it detects as binary. With -a, diff will generate a diff anyway,
1364 it detects as binary. With -a, diff will generate a diff anyway,
1359 probably with undesirable results.
1365 probably with undesirable results.
1360 """
1366 """
1361 node1, node2 = None, None
1367 node1, node2 = None, None
1362 revs = [repo.lookup(x) for x in opts['rev']]
1368 revs = [repo.lookup(x) for x in opts['rev']]
1363
1369
1364 if len(revs) > 0:
1370 if len(revs) > 0:
1365 node1 = revs[0]
1371 node1 = revs[0]
1366 if len(revs) > 1:
1372 if len(revs) > 1:
1367 node2 = revs[1]
1373 node2 = revs[1]
1368 if len(revs) > 2:
1374 if len(revs) > 2:
1369 raise util.Abort(_("too many revisions to diff"))
1375 raise util.Abort(_("too many revisions to diff"))
1370
1376
1371 fns, matchfn, anypats = matchpats(repo, pats, opts)
1377 fns, matchfn, anypats = matchpats(repo, pats, opts)
1372
1378
1373 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1379 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1374 text=opts['text'], opts=opts)
1380 text=opts['text'], opts=opts)
1375
1381
1376 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1382 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1377 node = repo.lookup(changeset)
1383 node = repo.lookup(changeset)
1378 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1384 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1379 if opts['switch_parent']:
1385 if opts['switch_parent']:
1380 parents.reverse()
1386 parents.reverse()
1381 prev = (parents and parents[0]) or nullid
1387 prev = (parents and parents[0]) or nullid
1382 change = repo.changelog.read(node)
1388 change = repo.changelog.read(node)
1383
1389
1384 fp = make_file(repo, repo.changelog, opts['output'],
1390 fp = make_file(repo, repo.changelog, opts['output'],
1385 node=node, total=total, seqno=seqno,
1391 node=node, total=total, seqno=seqno,
1386 revwidth=revwidth)
1392 revwidth=revwidth)
1387 if fp != sys.stdout:
1393 if fp != sys.stdout:
1388 ui.note("%s\n" % fp.name)
1394 ui.note("%s\n" % fp.name)
1389
1395
1390 fp.write("# HG changeset patch\n")
1396 fp.write("# HG changeset patch\n")
1391 fp.write("# User %s\n" % change[1])
1397 fp.write("# User %s\n" % change[1])
1392 fp.write("# Node ID %s\n" % hex(node))
1398 fp.write("# Node ID %s\n" % hex(node))
1393 fp.write("# Parent %s\n" % hex(prev))
1399 fp.write("# Parent %s\n" % hex(prev))
1394 if len(parents) > 1:
1400 if len(parents) > 1:
1395 fp.write("# Parent %s\n" % hex(parents[1]))
1401 fp.write("# Parent %s\n" % hex(parents[1]))
1396 fp.write(change[4].rstrip())
1402 fp.write(change[4].rstrip())
1397 fp.write("\n\n")
1403 fp.write("\n\n")
1398
1404
1399 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1405 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1400 if fp != sys.stdout:
1406 if fp != sys.stdout:
1401 fp.close()
1407 fp.close()
1402
1408
1403 def export(ui, repo, *changesets, **opts):
1409 def export(ui, repo, *changesets, **opts):
1404 """dump the header and diffs for one or more changesets
1410 """dump the header and diffs for one or more changesets
1405
1411
1406 Print the changeset header and diffs for one or more revisions.
1412 Print the changeset header and diffs for one or more revisions.
1407
1413
1408 The information shown in the changeset header is: author,
1414 The information shown in the changeset header is: author,
1409 changeset hash, parent and commit comment.
1415 changeset hash, parent and commit comment.
1410
1416
1411 Output may be to a file, in which case the name of the file is
1417 Output may be to a file, in which case the name of the file is
1412 given using a format string. The formatting rules are as follows:
1418 given using a format string. The formatting rules are as follows:
1413
1419
1414 %% literal "%" character
1420 %% literal "%" character
1415 %H changeset hash (40 bytes of hexadecimal)
1421 %H changeset hash (40 bytes of hexadecimal)
1416 %N number of patches being generated
1422 %N number of patches being generated
1417 %R changeset revision number
1423 %R changeset revision number
1418 %b basename of the exporting repository
1424 %b basename of the exporting repository
1419 %h short-form changeset hash (12 bytes of hexadecimal)
1425 %h short-form changeset hash (12 bytes of hexadecimal)
1420 %n zero-padded sequence number, starting at 1
1426 %n zero-padded sequence number, starting at 1
1421 %r zero-padded changeset revision number
1427 %r zero-padded changeset revision number
1422
1428
1423 Without the -a option, export will avoid generating diffs of files
1429 Without the -a option, export will avoid generating diffs of files
1424 it detects as binary. With -a, export will generate a diff anyway,
1430 it detects as binary. With -a, export will generate a diff anyway,
1425 probably with undesirable results.
1431 probably with undesirable results.
1426
1432
1427 With the --switch-parent option, the diff will be against the second
1433 With the --switch-parent option, the diff will be against the second
1428 parent. It can be useful to review a merge.
1434 parent. It can be useful to review a merge.
1429 """
1435 """
1430 if not changesets:
1436 if not changesets:
1431 raise util.Abort(_("export requires at least one changeset"))
1437 raise util.Abort(_("export requires at least one changeset"))
1432 seqno = 0
1438 seqno = 0
1433 revs = list(revrange(ui, repo, changesets))
1439 revs = list(revrange(ui, repo, changesets))
1434 total = len(revs)
1440 total = len(revs)
1435 revwidth = max(map(len, revs))
1441 revwidth = max(map(len, revs))
1436 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1442 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1437 ui.note(msg)
1443 ui.note(msg)
1438 for cset in revs:
1444 for cset in revs:
1439 seqno += 1
1445 seqno += 1
1440 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1446 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1441
1447
1442 def forget(ui, repo, *pats, **opts):
1448 def forget(ui, repo, *pats, **opts):
1443 """don't add the specified files on the next commit (DEPRECATED)
1449 """don't add the specified files on the next commit (DEPRECATED)
1444
1450
1445 (DEPRECATED)
1451 (DEPRECATED)
1446 Undo an 'hg add' scheduled for the next commit.
1452 Undo an 'hg add' scheduled for the next commit.
1447
1453
1448 This command is now deprecated and will be removed in a future
1454 This command is now deprecated and will be removed in a future
1449 release. Please use revert instead.
1455 release. Please use revert instead.
1450 """
1456 """
1451 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1457 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1452 forget = []
1458 forget = []
1453 for src, abs, rel, exact in walk(repo, pats, opts):
1459 for src, abs, rel, exact in walk(repo, pats, opts):
1454 if repo.dirstate.state(abs) == 'a':
1460 if repo.dirstate.state(abs) == 'a':
1455 forget.append(abs)
1461 forget.append(abs)
1456 if ui.verbose or not exact:
1462 if ui.verbose or not exact:
1457 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1463 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1458 repo.forget(forget)
1464 repo.forget(forget)
1459
1465
1460 def grep(ui, repo, pattern, *pats, **opts):
1466 def grep(ui, repo, pattern, *pats, **opts):
1461 """search for a pattern in specified files and revisions
1467 """search for a pattern in specified files and revisions
1462
1468
1463 Search revisions of files for a regular expression.
1469 Search revisions of files for a regular expression.
1464
1470
1465 This command behaves differently than Unix grep. It only accepts
1471 This command behaves differently than Unix grep. It only accepts
1466 Python/Perl regexps. It searches repository history, not the
1472 Python/Perl regexps. It searches repository history, not the
1467 working directory. It always prints the revision number in which
1473 working directory. It always prints the revision number in which
1468 a match appears.
1474 a match appears.
1469
1475
1470 By default, grep only prints output for the first revision of a
1476 By default, grep only prints output for the first revision of a
1471 file in which it finds a match. To get it to print every revision
1477 file in which it finds a match. To get it to print every revision
1472 that contains a change in match status ("-" for a match that
1478 that contains a change in match status ("-" for a match that
1473 becomes a non-match, or "+" for a non-match that becomes a match),
1479 becomes a non-match, or "+" for a non-match that becomes a match),
1474 use the --all flag.
1480 use the --all flag.
1475 """
1481 """
1476 reflags = 0
1482 reflags = 0
1477 if opts['ignore_case']:
1483 if opts['ignore_case']:
1478 reflags |= re.I
1484 reflags |= re.I
1479 regexp = re.compile(pattern, reflags)
1485 regexp = re.compile(pattern, reflags)
1480 sep, eol = ':', '\n'
1486 sep, eol = ':', '\n'
1481 if opts['print0']:
1487 if opts['print0']:
1482 sep = eol = '\0'
1488 sep = eol = '\0'
1483
1489
1484 fcache = {}
1490 fcache = {}
1485 def getfile(fn):
1491 def getfile(fn):
1486 if fn not in fcache:
1492 if fn not in fcache:
1487 fcache[fn] = repo.file(fn)
1493 fcache[fn] = repo.file(fn)
1488 return fcache[fn]
1494 return fcache[fn]
1489
1495
1490 def matchlines(body):
1496 def matchlines(body):
1491 begin = 0
1497 begin = 0
1492 linenum = 0
1498 linenum = 0
1493 while True:
1499 while True:
1494 match = regexp.search(body, begin)
1500 match = regexp.search(body, begin)
1495 if not match:
1501 if not match:
1496 break
1502 break
1497 mstart, mend = match.span()
1503 mstart, mend = match.span()
1498 linenum += body.count('\n', begin, mstart) + 1
1504 linenum += body.count('\n', begin, mstart) + 1
1499 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1505 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1500 lend = body.find('\n', mend)
1506 lend = body.find('\n', mend)
1501 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1507 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1502 begin = lend + 1
1508 begin = lend + 1
1503
1509
1504 class linestate(object):
1510 class linestate(object):
1505 def __init__(self, line, linenum, colstart, colend):
1511 def __init__(self, line, linenum, colstart, colend):
1506 self.line = line
1512 self.line = line
1507 self.linenum = linenum
1513 self.linenum = linenum
1508 self.colstart = colstart
1514 self.colstart = colstart
1509 self.colend = colend
1515 self.colend = colend
1510 def __eq__(self, other):
1516 def __eq__(self, other):
1511 return self.line == other.line
1517 return self.line == other.line
1512 def __hash__(self):
1518 def __hash__(self):
1513 return hash(self.line)
1519 return hash(self.line)
1514
1520
1515 matches = {}
1521 matches = {}
1516 def grepbody(fn, rev, body):
1522 def grepbody(fn, rev, body):
1517 matches[rev].setdefault(fn, {})
1523 matches[rev].setdefault(fn, {})
1518 m = matches[rev][fn]
1524 m = matches[rev][fn]
1519 for lnum, cstart, cend, line in matchlines(body):
1525 for lnum, cstart, cend, line in matchlines(body):
1520 s = linestate(line, lnum, cstart, cend)
1526 s = linestate(line, lnum, cstart, cend)
1521 m[s] = s
1527 m[s] = s
1522
1528
1523 # FIXME: prev isn't used, why ?
1529 # FIXME: prev isn't used, why ?
1524 prev = {}
1530 prev = {}
1525 ucache = {}
1531 ucache = {}
1526 def display(fn, rev, states, prevstates):
1532 def display(fn, rev, states, prevstates):
1527 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1533 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1528 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1534 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1529 counts = {'-': 0, '+': 0}
1535 counts = {'-': 0, '+': 0}
1530 filerevmatches = {}
1536 filerevmatches = {}
1531 for l in diff:
1537 for l in diff:
1532 if incrementing or not opts['all']:
1538 if incrementing or not opts['all']:
1533 change = ((l in prevstates) and '-') or '+'
1539 change = ((l in prevstates) and '-') or '+'
1534 r = rev
1540 r = rev
1535 else:
1541 else:
1536 change = ((l in states) and '-') or '+'
1542 change = ((l in states) and '-') or '+'
1537 r = prev[fn]
1543 r = prev[fn]
1538 cols = [fn, str(rev)]
1544 cols = [fn, str(rev)]
1539 if opts['line_number']:
1545 if opts['line_number']:
1540 cols.append(str(l.linenum))
1546 cols.append(str(l.linenum))
1541 if opts['all']:
1547 if opts['all']:
1542 cols.append(change)
1548 cols.append(change)
1543 if opts['user']:
1549 if opts['user']:
1544 cols.append(trimuser(ui, getchange(rev)[1], rev,
1550 cols.append(trimuser(ui, getchange(rev)[1], rev,
1545 ucache))
1551 ucache))
1546 if opts['files_with_matches']:
1552 if opts['files_with_matches']:
1547 c = (fn, rev)
1553 c = (fn, rev)
1548 if c in filerevmatches:
1554 if c in filerevmatches:
1549 continue
1555 continue
1550 filerevmatches[c] = 1
1556 filerevmatches[c] = 1
1551 else:
1557 else:
1552 cols.append(l.line)
1558 cols.append(l.line)
1553 ui.write(sep.join(cols), eol)
1559 ui.write(sep.join(cols), eol)
1554 counts[change] += 1
1560 counts[change] += 1
1555 return counts['+'], counts['-']
1561 return counts['+'], counts['-']
1556
1562
1557 fstate = {}
1563 fstate = {}
1558 skip = {}
1564 skip = {}
1559 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1565 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1560 count = 0
1566 count = 0
1561 incrementing = False
1567 incrementing = False
1562 for st, rev, fns in changeiter:
1568 for st, rev, fns in changeiter:
1563 if st == 'window':
1569 if st == 'window':
1564 incrementing = rev
1570 incrementing = rev
1565 matches.clear()
1571 matches.clear()
1566 elif st == 'add':
1572 elif st == 'add':
1567 change = repo.changelog.read(repo.lookup(str(rev)))
1573 change = repo.changelog.read(repo.lookup(str(rev)))
1568 mf = repo.manifest.read(change[0])
1574 mf = repo.manifest.read(change[0])
1569 matches[rev] = {}
1575 matches[rev] = {}
1570 for fn in fns:
1576 for fn in fns:
1571 if fn in skip:
1577 if fn in skip:
1572 continue
1578 continue
1573 fstate.setdefault(fn, {})
1579 fstate.setdefault(fn, {})
1574 try:
1580 try:
1575 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1581 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1576 except KeyError:
1582 except KeyError:
1577 pass
1583 pass
1578 elif st == 'iter':
1584 elif st == 'iter':
1579 states = matches[rev].items()
1585 states = matches[rev].items()
1580 states.sort()
1586 states.sort()
1581 for fn, m in states:
1587 for fn, m in states:
1582 if fn in skip:
1588 if fn in skip:
1583 continue
1589 continue
1584 if incrementing or not opts['all'] or fstate[fn]:
1590 if incrementing or not opts['all'] or fstate[fn]:
1585 pos, neg = display(fn, rev, m, fstate[fn])
1591 pos, neg = display(fn, rev, m, fstate[fn])
1586 count += pos + neg
1592 count += pos + neg
1587 if pos and not opts['all']:
1593 if pos and not opts['all']:
1588 skip[fn] = True
1594 skip[fn] = True
1589 fstate[fn] = m
1595 fstate[fn] = m
1590 prev[fn] = rev
1596 prev[fn] = rev
1591
1597
1592 if not incrementing:
1598 if not incrementing:
1593 fstate = fstate.items()
1599 fstate = fstate.items()
1594 fstate.sort()
1600 fstate.sort()
1595 for fn, state in fstate:
1601 for fn, state in fstate:
1596 if fn in skip:
1602 if fn in skip:
1597 continue
1603 continue
1598 display(fn, rev, {}, state)
1604 display(fn, rev, {}, state)
1599 return (count == 0 and 1) or 0
1605 return (count == 0 and 1) or 0
1600
1606
1601 def heads(ui, repo, **opts):
1607 def heads(ui, repo, **opts):
1602 """show current repository heads
1608 """show current repository heads
1603
1609
1604 Show all repository head changesets.
1610 Show all repository head changesets.
1605
1611
1606 Repository "heads" are changesets that don't have children
1612 Repository "heads" are changesets that don't have children
1607 changesets. They are where development generally takes place and
1613 changesets. They are where development generally takes place and
1608 are the usual targets for update and merge operations.
1614 are the usual targets for update and merge operations.
1609 """
1615 """
1610 if opts['rev']:
1616 if opts['rev']:
1611 heads = repo.heads(repo.lookup(opts['rev']))
1617 heads = repo.heads(repo.lookup(opts['rev']))
1612 else:
1618 else:
1613 heads = repo.heads()
1619 heads = repo.heads()
1614 br = None
1620 br = None
1615 if opts['branches']:
1621 if opts['branches']:
1616 br = repo.branchlookup(heads)
1622 br = repo.branchlookup(heads)
1617 displayer = show_changeset(ui, repo, opts)
1623 displayer = show_changeset(ui, repo, opts)
1618 for n in heads:
1624 for n in heads:
1619 displayer.show(changenode=n, brinfo=br)
1625 displayer.show(changenode=n, brinfo=br)
1620
1626
1621 def identify(ui, repo):
1627 def identify(ui, repo):
1622 """print information about the working copy
1628 """print information about the working copy
1623
1629
1624 Print a short summary of the current state of the repo.
1630 Print a short summary of the current state of the repo.
1625
1631
1626 This summary identifies the repository state using one or two parent
1632 This summary identifies the repository state using one or two parent
1627 hash identifiers, followed by a "+" if there are uncommitted changes
1633 hash identifiers, followed by a "+" if there are uncommitted changes
1628 in the working directory, followed by a list of tags for this revision.
1634 in the working directory, followed by a list of tags for this revision.
1629 """
1635 """
1630 parents = [p for p in repo.dirstate.parents() if p != nullid]
1636 parents = [p for p in repo.dirstate.parents() if p != nullid]
1631 if not parents:
1637 if not parents:
1632 ui.write(_("unknown\n"))
1638 ui.write(_("unknown\n"))
1633 return
1639 return
1634
1640
1635 hexfunc = ui.verbose and hex or short
1641 hexfunc = ui.verbose and hex or short
1636 modified, added, removed, deleted, unknown = repo.changes()
1642 modified, added, removed, deleted, unknown = repo.changes()
1637 output = ["%s%s" %
1643 output = ["%s%s" %
1638 ('+'.join([hexfunc(parent) for parent in parents]),
1644 ('+'.join([hexfunc(parent) for parent in parents]),
1639 (modified or added or removed or deleted) and "+" or "")]
1645 (modified or added or removed or deleted) and "+" or "")]
1640
1646
1641 if not ui.quiet:
1647 if not ui.quiet:
1642 # multiple tags for a single parent separated by '/'
1648 # multiple tags for a single parent separated by '/'
1643 parenttags = ['/'.join(tags)
1649 parenttags = ['/'.join(tags)
1644 for tags in map(repo.nodetags, parents) if tags]
1650 for tags in map(repo.nodetags, parents) if tags]
1645 # tags for multiple parents separated by ' + '
1651 # tags for multiple parents separated by ' + '
1646 if parenttags:
1652 if parenttags:
1647 output.append(' + '.join(parenttags))
1653 output.append(' + '.join(parenttags))
1648
1654
1649 ui.write("%s\n" % ' '.join(output))
1655 ui.write("%s\n" % ' '.join(output))
1650
1656
1651 def import_(ui, repo, patch1, *patches, **opts):
1657 def import_(ui, repo, patch1, *patches, **opts):
1652 """import an ordered set of patches
1658 """import an ordered set of patches
1653
1659
1654 Import a list of patches and commit them individually.
1660 Import a list of patches and commit them individually.
1655
1661
1656 If there are outstanding changes in the working directory, import
1662 If there are outstanding changes in the working directory, import
1657 will abort unless given the -f flag.
1663 will abort unless given the -f flag.
1658
1664
1659 If a patch looks like a mail message (its first line starts with
1665 If a patch looks like a mail message (its first line starts with
1660 "From " or looks like an RFC822 header), it will not be applied
1666 "From " or looks like an RFC822 header), it will not be applied
1661 unless the -f option is used. The importer neither parses nor
1667 unless the -f option is used. The importer neither parses nor
1662 discards mail headers, so use -f only to override the "mailness"
1668 discards mail headers, so use -f only to override the "mailness"
1663 safety check, not to import a real mail message.
1669 safety check, not to import a real mail message.
1664 """
1670 """
1665 patches = (patch1,) + patches
1671 patches = (patch1,) + patches
1666
1672
1667 if not opts['force']:
1673 if not opts['force']:
1668 bail_if_changed(repo)
1674 bail_if_changed(repo)
1669
1675
1670 d = opts["base"]
1676 d = opts["base"]
1671 strip = opts["strip"]
1677 strip = opts["strip"]
1672
1678
1673 mailre = re.compile(r'(?:From |[\w-]+:)')
1679 mailre = re.compile(r'(?:From |[\w-]+:)')
1674
1680
1675 # attempt to detect the start of a patch
1681 # attempt to detect the start of a patch
1676 # (this heuristic is borrowed from quilt)
1682 # (this heuristic is borrowed from quilt)
1677 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1683 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1678 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1684 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1679 '(---|\*\*\*)[ \t])')
1685 '(---|\*\*\*)[ \t])')
1680
1686
1681 for patch in patches:
1687 for patch in patches:
1682 ui.status(_("applying %s\n") % patch)
1688 ui.status(_("applying %s\n") % patch)
1683 pf = os.path.join(d, patch)
1689 pf = os.path.join(d, patch)
1684
1690
1685 message = []
1691 message = []
1686 user = None
1692 user = None
1687 hgpatch = False
1693 hgpatch = False
1688 for line in file(pf):
1694 for line in file(pf):
1689 line = line.rstrip()
1695 line = line.rstrip()
1690 if (not message and not hgpatch and
1696 if (not message and not hgpatch and
1691 mailre.match(line) and not opts['force']):
1697 mailre.match(line) and not opts['force']):
1692 if len(line) > 35:
1698 if len(line) > 35:
1693 line = line[:32] + '...'
1699 line = line[:32] + '...'
1694 raise util.Abort(_('first line looks like a '
1700 raise util.Abort(_('first line looks like a '
1695 'mail header: ') + line)
1701 'mail header: ') + line)
1696 if diffre.match(line):
1702 if diffre.match(line):
1697 break
1703 break
1698 elif hgpatch:
1704 elif hgpatch:
1699 # parse values when importing the result of an hg export
1705 # parse values when importing the result of an hg export
1700 if line.startswith("# User "):
1706 if line.startswith("# User "):
1701 user = line[7:]
1707 user = line[7:]
1702 ui.debug(_('User: %s\n') % user)
1708 ui.debug(_('User: %s\n') % user)
1703 elif not line.startswith("# ") and line:
1709 elif not line.startswith("# ") and line:
1704 message.append(line)
1710 message.append(line)
1705 hgpatch = False
1711 hgpatch = False
1706 elif line == '# HG changeset patch':
1712 elif line == '# HG changeset patch':
1707 hgpatch = True
1713 hgpatch = True
1708 message = [] # We may have collected garbage
1714 message = [] # We may have collected garbage
1709 else:
1715 else:
1710 message.append(line)
1716 message.append(line)
1711
1717
1712 # make sure message isn't empty
1718 # make sure message isn't empty
1713 if not message:
1719 if not message:
1714 message = _("imported patch %s\n") % patch
1720 message = _("imported patch %s\n") % patch
1715 else:
1721 else:
1716 message = "%s\n" % '\n'.join(message)
1722 message = "%s\n" % '\n'.join(message)
1717 ui.debug(_('message:\n%s\n') % message)
1723 ui.debug(_('message:\n%s\n') % message)
1718
1724
1719 files = util.patch(strip, pf, ui)
1725 files = util.patch(strip, pf, ui)
1720
1726
1721 if len(files) > 0:
1727 if len(files) > 0:
1722 addremove_lock(ui, repo, files, {})
1728 addremove_lock(ui, repo, files, {})
1723 repo.commit(files, message, user)
1729 repo.commit(files, message, user)
1724
1730
1725 def incoming(ui, repo, source="default", **opts):
1731 def incoming(ui, repo, source="default", **opts):
1726 """show new changesets found in source
1732 """show new changesets found in source
1727
1733
1728 Show new changesets found in the specified path/URL or the default
1734 Show new changesets found in the specified path/URL or the default
1729 pull location. These are the changesets that would be pulled if a pull
1735 pull location. These are the changesets that would be pulled if a pull
1730 was requested.
1736 was requested.
1731
1737
1732 For remote repository, using --bundle avoids downloading the changesets
1738 For remote repository, using --bundle avoids downloading the changesets
1733 twice if the incoming is followed by a pull.
1739 twice if the incoming is followed by a pull.
1734
1740
1735 See pull for valid source format details.
1741 See pull for valid source format details.
1736 """
1742 """
1737 source = ui.expandpath(source)
1743 source = ui.expandpath(source)
1738 if opts['ssh']:
1744 if opts['ssh']:
1739 ui.setconfig("ui", "ssh", opts['ssh'])
1745 ui.setconfig("ui", "ssh", opts['ssh'])
1740 if opts['remotecmd']:
1746 if opts['remotecmd']:
1741 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1747 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1742
1748
1743 other = hg.repository(ui, source)
1749 other = hg.repository(ui, source)
1744 incoming = repo.findincoming(other, force=opts["force"])
1750 incoming = repo.findincoming(other, force=opts["force"])
1745 if not incoming:
1751 if not incoming:
1746 ui.status(_("no changes found\n"))
1752 ui.status(_("no changes found\n"))
1747 return
1753 return
1748
1754
1749 cleanup = None
1755 cleanup = None
1750 try:
1756 try:
1751 fname = opts["bundle"]
1757 fname = opts["bundle"]
1752 if fname or not other.local():
1758 if fname or not other.local():
1753 # create a bundle (uncompressed if other repo is not local)
1759 # create a bundle (uncompressed if other repo is not local)
1754 cg = other.changegroup(incoming, "incoming")
1760 cg = other.changegroup(incoming, "incoming")
1755 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1761 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1756 # keep written bundle?
1762 # keep written bundle?
1757 if opts["bundle"]:
1763 if opts["bundle"]:
1758 cleanup = None
1764 cleanup = None
1759 if not other.local():
1765 if not other.local():
1760 # use the created uncompressed bundlerepo
1766 # use the created uncompressed bundlerepo
1761 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1767 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1762
1768
1763 o = other.changelog.nodesbetween(incoming)[0]
1769 o = other.changelog.nodesbetween(incoming)[0]
1764 if opts['newest_first']:
1770 if opts['newest_first']:
1765 o.reverse()
1771 o.reverse()
1766 displayer = show_changeset(ui, other, opts)
1772 displayer = show_changeset(ui, other, opts)
1767 for n in o:
1773 for n in o:
1768 parents = [p for p in other.changelog.parents(n) if p != nullid]
1774 parents = [p for p in other.changelog.parents(n) if p != nullid]
1769 if opts['no_merges'] and len(parents) == 2:
1775 if opts['no_merges'] and len(parents) == 2:
1770 continue
1776 continue
1771 displayer.show(changenode=n)
1777 displayer.show(changenode=n)
1772 if opts['patch']:
1778 if opts['patch']:
1773 prev = (parents and parents[0]) or nullid
1779 prev = (parents and parents[0]) or nullid
1774 dodiff(ui, ui, other, prev, n)
1780 dodiff(ui, ui, other, prev, n)
1775 ui.write("\n")
1781 ui.write("\n")
1776 finally:
1782 finally:
1777 if hasattr(other, 'close'):
1783 if hasattr(other, 'close'):
1778 other.close()
1784 other.close()
1779 if cleanup:
1785 if cleanup:
1780 os.unlink(cleanup)
1786 os.unlink(cleanup)
1781
1787
1782 def init(ui, dest="."):
1788 def init(ui, dest="."):
1783 """create a new repository in the given directory
1789 """create a new repository in the given directory
1784
1790
1785 Initialize a new repository in the given directory. If the given
1791 Initialize a new repository in the given directory. If the given
1786 directory does not exist, it is created.
1792 directory does not exist, it is created.
1787
1793
1788 If no directory is given, the current directory is used.
1794 If no directory is given, the current directory is used.
1789 """
1795 """
1790 if not os.path.exists(dest):
1796 if not os.path.exists(dest):
1791 os.mkdir(dest)
1797 os.mkdir(dest)
1792 hg.repository(ui, dest, create=1)
1798 hg.repository(ui, dest, create=1)
1793
1799
1794 def locate(ui, repo, *pats, **opts):
1800 def locate(ui, repo, *pats, **opts):
1795 """locate files matching specific patterns
1801 """locate files matching specific patterns
1796
1802
1797 Print all files under Mercurial control whose names match the
1803 Print all files under Mercurial control whose names match the
1798 given patterns.
1804 given patterns.
1799
1805
1800 This command searches the current directory and its
1806 This command searches the current directory and its
1801 subdirectories. To search an entire repository, move to the root
1807 subdirectories. To search an entire repository, move to the root
1802 of the repository.
1808 of the repository.
1803
1809
1804 If no patterns are given to match, this command prints all file
1810 If no patterns are given to match, this command prints all file
1805 names.
1811 names.
1806
1812
1807 If you want to feed the output of this command into the "xargs"
1813 If you want to feed the output of this command into the "xargs"
1808 command, use the "-0" option to both this command and "xargs".
1814 command, use the "-0" option to both this command and "xargs".
1809 This will avoid the problem of "xargs" treating single filenames
1815 This will avoid the problem of "xargs" treating single filenames
1810 that contain white space as multiple filenames.
1816 that contain white space as multiple filenames.
1811 """
1817 """
1812 end = opts['print0'] and '\0' or '\n'
1818 end = opts['print0'] and '\0' or '\n'
1813 rev = opts['rev']
1819 rev = opts['rev']
1814 if rev:
1820 if rev:
1815 node = repo.lookup(rev)
1821 node = repo.lookup(rev)
1816 else:
1822 else:
1817 node = None
1823 node = None
1818
1824
1819 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1825 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1820 head='(?:.*/|)'):
1826 head='(?:.*/|)'):
1821 if not node and repo.dirstate.state(abs) == '?':
1827 if not node and repo.dirstate.state(abs) == '?':
1822 continue
1828 continue
1823 if opts['fullpath']:
1829 if opts['fullpath']:
1824 ui.write(os.path.join(repo.root, abs), end)
1830 ui.write(os.path.join(repo.root, abs), end)
1825 else:
1831 else:
1826 ui.write(((pats and rel) or abs), end)
1832 ui.write(((pats and rel) or abs), end)
1827
1833
1828 def log(ui, repo, *pats, **opts):
1834 def log(ui, repo, *pats, **opts):
1829 """show revision history of entire repository or files
1835 """show revision history of entire repository or files
1830
1836
1831 Print the revision history of the specified files or the entire project.
1837 Print the revision history of the specified files or the entire project.
1832
1838
1833 By default this command outputs: changeset id and hash, tags,
1839 By default this command outputs: changeset id and hash, tags,
1834 non-trivial parents, user, date and time, and a summary for each
1840 non-trivial parents, user, date and time, and a summary for each
1835 commit. When the -v/--verbose switch is used, the list of changed
1841 commit. When the -v/--verbose switch is used, the list of changed
1836 files and full commit message is shown.
1842 files and full commit message is shown.
1837 """
1843 """
1838 class dui(object):
1844 class dui(object):
1839 # Implement and delegate some ui protocol. Save hunks of
1845 # Implement and delegate some ui protocol. Save hunks of
1840 # output for later display in the desired order.
1846 # output for later display in the desired order.
1841 def __init__(self, ui):
1847 def __init__(self, ui):
1842 self.ui = ui
1848 self.ui = ui
1843 self.hunk = {}
1849 self.hunk = {}
1844 self.header = {}
1850 self.header = {}
1845 def bump(self, rev):
1851 def bump(self, rev):
1846 self.rev = rev
1852 self.rev = rev
1847 self.hunk[rev] = []
1853 self.hunk[rev] = []
1848 self.header[rev] = []
1854 self.header[rev] = []
1849 def note(self, *args):
1855 def note(self, *args):
1850 if self.verbose:
1856 if self.verbose:
1851 self.write(*args)
1857 self.write(*args)
1852 def status(self, *args):
1858 def status(self, *args):
1853 if not self.quiet:
1859 if not self.quiet:
1854 self.write(*args)
1860 self.write(*args)
1855 def write(self, *args):
1861 def write(self, *args):
1856 self.hunk[self.rev].append(args)
1862 self.hunk[self.rev].append(args)
1857 def write_header(self, *args):
1863 def write_header(self, *args):
1858 self.header[self.rev].append(args)
1864 self.header[self.rev].append(args)
1859 def debug(self, *args):
1865 def debug(self, *args):
1860 if self.debugflag:
1866 if self.debugflag:
1861 self.write(*args)
1867 self.write(*args)
1862 def __getattr__(self, key):
1868 def __getattr__(self, key):
1863 return getattr(self.ui, key)
1869 return getattr(self.ui, key)
1864
1870
1865 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1871 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1866
1872
1867 if opts['limit']:
1873 if opts['limit']:
1868 try:
1874 try:
1869 limit = int(opts['limit'])
1875 limit = int(opts['limit'])
1870 except ValueError:
1876 except ValueError:
1871 raise util.Abort(_('limit must be a positive integer'))
1877 raise util.Abort(_('limit must be a positive integer'))
1872 if limit <= 0: raise util.Abort(_('limit must be positive'))
1878 if limit <= 0: raise util.Abort(_('limit must be positive'))
1873 else:
1879 else:
1874 limit = sys.maxint
1880 limit = sys.maxint
1875 count = 0
1881 count = 0
1876
1882
1877 displayer = show_changeset(ui, repo, opts)
1883 displayer = show_changeset(ui, repo, opts)
1878 for st, rev, fns in changeiter:
1884 for st, rev, fns in changeiter:
1879 if st == 'window':
1885 if st == 'window':
1880 du = dui(ui)
1886 du = dui(ui)
1881 displayer.ui = du
1887 displayer.ui = du
1882 elif st == 'add':
1888 elif st == 'add':
1883 du.bump(rev)
1889 du.bump(rev)
1884 changenode = repo.changelog.node(rev)
1890 changenode = repo.changelog.node(rev)
1885 parents = [p for p in repo.changelog.parents(changenode)
1891 parents = [p for p in repo.changelog.parents(changenode)
1886 if p != nullid]
1892 if p != nullid]
1887 if opts['no_merges'] and len(parents) == 2:
1893 if opts['no_merges'] and len(parents) == 2:
1888 continue
1894 continue
1889 if opts['only_merges'] and len(parents) != 2:
1895 if opts['only_merges'] and len(parents) != 2:
1890 continue
1896 continue
1891
1897
1892 if opts['keyword']:
1898 if opts['keyword']:
1893 changes = getchange(rev)
1899 changes = getchange(rev)
1894 miss = 0
1900 miss = 0
1895 for k in [kw.lower() for kw in opts['keyword']]:
1901 for k in [kw.lower() for kw in opts['keyword']]:
1896 if not (k in changes[1].lower() or
1902 if not (k in changes[1].lower() or
1897 k in changes[4].lower() or
1903 k in changes[4].lower() or
1898 k in " ".join(changes[3][:20]).lower()):
1904 k in " ".join(changes[3][:20]).lower()):
1899 miss = 1
1905 miss = 1
1900 break
1906 break
1901 if miss:
1907 if miss:
1902 continue
1908 continue
1903
1909
1904 br = None
1910 br = None
1905 if opts['branches']:
1911 if opts['branches']:
1906 br = repo.branchlookup([repo.changelog.node(rev)])
1912 br = repo.branchlookup([repo.changelog.node(rev)])
1907
1913
1908 displayer.show(rev, brinfo=br)
1914 displayer.show(rev, brinfo=br)
1909 if opts['patch']:
1915 if opts['patch']:
1910 prev = (parents and parents[0]) or nullid
1916 prev = (parents and parents[0]) or nullid
1911 dodiff(du, du, repo, prev, changenode, match=matchfn)
1917 dodiff(du, du, repo, prev, changenode, match=matchfn)
1912 du.write("\n\n")
1918 du.write("\n\n")
1913 elif st == 'iter':
1919 elif st == 'iter':
1914 if count == limit: break
1920 if count == limit: break
1915 if du.header[rev]:
1921 if du.header[rev]:
1916 for args in du.header[rev]:
1922 for args in du.header[rev]:
1917 ui.write_header(*args)
1923 ui.write_header(*args)
1918 if du.hunk[rev]:
1924 if du.hunk[rev]:
1919 count += 1
1925 count += 1
1920 for args in du.hunk[rev]:
1926 for args in du.hunk[rev]:
1921 ui.write(*args)
1927 ui.write(*args)
1922
1928
1923 def manifest(ui, repo, rev=None):
1929 def manifest(ui, repo, rev=None):
1924 """output the latest or given revision of the project manifest
1930 """output the latest or given revision of the project manifest
1925
1931
1926 Print a list of version controlled files for the given revision.
1932 Print a list of version controlled files for the given revision.
1927
1933
1928 The manifest is the list of files being version controlled. If no revision
1934 The manifest is the list of files being version controlled. If no revision
1929 is given then the tip is used.
1935 is given then the tip is used.
1930 """
1936 """
1931 if rev:
1937 if rev:
1932 try:
1938 try:
1933 # assume all revision numbers are for changesets
1939 # assume all revision numbers are for changesets
1934 n = repo.lookup(rev)
1940 n = repo.lookup(rev)
1935 change = repo.changelog.read(n)
1941 change = repo.changelog.read(n)
1936 n = change[0]
1942 n = change[0]
1937 except hg.RepoError:
1943 except hg.RepoError:
1938 n = repo.manifest.lookup(rev)
1944 n = repo.manifest.lookup(rev)
1939 else:
1945 else:
1940 n = repo.manifest.tip()
1946 n = repo.manifest.tip()
1941 m = repo.manifest.read(n)
1947 m = repo.manifest.read(n)
1942 mf = repo.manifest.readflags(n)
1948 mf = repo.manifest.readflags(n)
1943 files = m.keys()
1949 files = m.keys()
1944 files.sort()
1950 files.sort()
1945
1951
1946 for f in files:
1952 for f in files:
1947 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1953 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1948
1954
1949 def merge(ui, repo, node=None, **opts):
1955 def merge(ui, repo, node=None, **opts):
1950 """Merge working directory with another revision
1956 """Merge working directory with another revision
1951
1957
1952 Merge the contents of the current working directory and the
1958 Merge the contents of the current working directory and the
1953 requested revision. Files that changed between either parent are
1959 requested revision. Files that changed between either parent are
1954 marked as changed for the next commit and a commit must be
1960 marked as changed for the next commit and a commit must be
1955 performed before any further updates are allowed.
1961 performed before any further updates are allowed.
1956 """
1962 """
1957 return update(ui, repo, node=node, merge=True, **opts)
1963 return update(ui, repo, node=node, merge=True, **opts)
1958
1964
1959 def outgoing(ui, repo, dest="default-push", **opts):
1965 def outgoing(ui, repo, dest="default-push", **opts):
1960 """show changesets not found in destination
1966 """show changesets not found in destination
1961
1967
1962 Show changesets not found in the specified destination repository or
1968 Show changesets not found in the specified destination repository or
1963 the default push location. These are the changesets that would be pushed
1969 the default push location. These are the changesets that would be pushed
1964 if a push was requested.
1970 if a push was requested.
1965
1971
1966 See pull for valid destination format details.
1972 See pull for valid destination format details.
1967 """
1973 """
1968 dest = ui.expandpath(dest)
1974 dest = ui.expandpath(dest)
1969 if opts['ssh']:
1975 if opts['ssh']:
1970 ui.setconfig("ui", "ssh", opts['ssh'])
1976 ui.setconfig("ui", "ssh", opts['ssh'])
1971 if opts['remotecmd']:
1977 if opts['remotecmd']:
1972 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1978 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1973
1979
1974 other = hg.repository(ui, dest)
1980 other = hg.repository(ui, dest)
1975 o = repo.findoutgoing(other, force=opts['force'])
1981 o = repo.findoutgoing(other, force=opts['force'])
1976 if not o:
1982 if not o:
1977 ui.status(_("no changes found\n"))
1983 ui.status(_("no changes found\n"))
1978 return
1984 return
1979 o = repo.changelog.nodesbetween(o)[0]
1985 o = repo.changelog.nodesbetween(o)[0]
1980 if opts['newest_first']:
1986 if opts['newest_first']:
1981 o.reverse()
1987 o.reverse()
1982 displayer = show_changeset(ui, repo, opts)
1988 displayer = show_changeset(ui, repo, opts)
1983 for n in o:
1989 for n in o:
1984 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1990 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1985 if opts['no_merges'] and len(parents) == 2:
1991 if opts['no_merges'] and len(parents) == 2:
1986 continue
1992 continue
1987 displayer.show(changenode=n)
1993 displayer.show(changenode=n)
1988 if opts['patch']:
1994 if opts['patch']:
1989 prev = (parents and parents[0]) or nullid
1995 prev = (parents and parents[0]) or nullid
1990 dodiff(ui, ui, repo, prev, n)
1996 dodiff(ui, ui, repo, prev, n)
1991 ui.write("\n")
1997 ui.write("\n")
1992
1998
1993 def parents(ui, repo, rev=None, branches=None, **opts):
1999 def parents(ui, repo, rev=None, branches=None, **opts):
1994 """show the parents of the working dir or revision
2000 """show the parents of the working dir or revision
1995
2001
1996 Print the working directory's parent revisions.
2002 Print the working directory's parent revisions.
1997 """
2003 """
1998 if rev:
2004 if rev:
1999 p = repo.changelog.parents(repo.lookup(rev))
2005 p = repo.changelog.parents(repo.lookup(rev))
2000 else:
2006 else:
2001 p = repo.dirstate.parents()
2007 p = repo.dirstate.parents()
2002
2008
2003 br = None
2009 br = None
2004 if branches is not None:
2010 if branches is not None:
2005 br = repo.branchlookup(p)
2011 br = repo.branchlookup(p)
2006 displayer = show_changeset(ui, repo, opts)
2012 displayer = show_changeset(ui, repo, opts)
2007 for n in p:
2013 for n in p:
2008 if n != nullid:
2014 if n != nullid:
2009 displayer.show(changenode=n, brinfo=br)
2015 displayer.show(changenode=n, brinfo=br)
2010
2016
2011 def paths(ui, repo, search=None):
2017 def paths(ui, repo, search=None):
2012 """show definition of symbolic path names
2018 """show definition of symbolic path names
2013
2019
2014 Show definition of symbolic path name NAME. If no name is given, show
2020 Show definition of symbolic path name NAME. If no name is given, show
2015 definition of available names.
2021 definition of available names.
2016
2022
2017 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2023 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2018 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2024 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2019 """
2025 """
2020 if search:
2026 if search:
2021 for name, path in ui.configitems("paths"):
2027 for name, path in ui.configitems("paths"):
2022 if name == search:
2028 if name == search:
2023 ui.write("%s\n" % path)
2029 ui.write("%s\n" % path)
2024 return
2030 return
2025 ui.warn(_("not found!\n"))
2031 ui.warn(_("not found!\n"))
2026 return 1
2032 return 1
2027 else:
2033 else:
2028 for name, path in ui.configitems("paths"):
2034 for name, path in ui.configitems("paths"):
2029 ui.write("%s = %s\n" % (name, path))
2035 ui.write("%s = %s\n" % (name, path))
2030
2036
2031 def postincoming(ui, repo, modheads, optupdate):
2037 def postincoming(ui, repo, modheads, optupdate):
2032 if modheads == 0:
2038 if modheads == 0:
2033 return
2039 return
2034 if optupdate:
2040 if optupdate:
2035 if modheads == 1:
2041 if modheads == 1:
2036 return update(ui, repo)
2042 return update(ui, repo)
2037 else:
2043 else:
2038 ui.status(_("not updating, since new heads added\n"))
2044 ui.status(_("not updating, since new heads added\n"))
2039 if modheads > 1:
2045 if modheads > 1:
2040 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2046 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2041 else:
2047 else:
2042 ui.status(_("(run 'hg update' to get a working copy)\n"))
2048 ui.status(_("(run 'hg update' to get a working copy)\n"))
2043
2049
2044 def pull(ui, repo, source="default", **opts):
2050 def pull(ui, repo, source="default", **opts):
2045 """pull changes from the specified source
2051 """pull changes from the specified source
2046
2052
2047 Pull changes from a remote repository to a local one.
2053 Pull changes from a remote repository to a local one.
2048
2054
2049 This finds all changes from the repository at the specified path
2055 This finds all changes from the repository at the specified path
2050 or URL and adds them to the local repository. By default, this
2056 or URL and adds them to the local repository. By default, this
2051 does not update the copy of the project in the working directory.
2057 does not update the copy of the project in the working directory.
2052
2058
2053 Valid URLs are of the form:
2059 Valid URLs are of the form:
2054
2060
2055 local/filesystem/path
2061 local/filesystem/path
2056 http://[user@]host[:port][/path]
2062 http://[user@]host[:port][/path]
2057 https://[user@]host[:port][/path]
2063 https://[user@]host[:port][/path]
2058 ssh://[user@]host[:port][/path]
2064 ssh://[user@]host[:port][/path]
2059
2065
2060 Some notes about using SSH with Mercurial:
2066 Some notes about using SSH with Mercurial:
2061 - SSH requires an accessible shell account on the destination machine
2067 - SSH requires an accessible shell account on the destination machine
2062 and a copy of hg in the remote path or specified with as remotecmd.
2068 and a copy of hg in the remote path or specified with as remotecmd.
2063 - /path is relative to the remote user's home directory by default.
2069 - /path is relative to the remote user's home directory by default.
2064 Use two slashes at the start of a path to specify an absolute path.
2070 Use two slashes at the start of a path to specify an absolute path.
2065 - Mercurial doesn't use its own compression via SSH; the right thing
2071 - Mercurial doesn't use its own compression via SSH; the right thing
2066 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2072 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2067 Host *.mylocalnetwork.example.com
2073 Host *.mylocalnetwork.example.com
2068 Compression off
2074 Compression off
2069 Host *
2075 Host *
2070 Compression on
2076 Compression on
2071 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2077 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2072 with the --ssh command line option.
2078 with the --ssh command line option.
2073 """
2079 """
2074 source = ui.expandpath(source)
2080 source = ui.expandpath(source)
2075 ui.status(_('pulling from %s\n') % (source))
2081 ui.status(_('pulling from %s\n') % (source))
2076
2082
2077 if opts['ssh']:
2083 if opts['ssh']:
2078 ui.setconfig("ui", "ssh", opts['ssh'])
2084 ui.setconfig("ui", "ssh", opts['ssh'])
2079 if opts['remotecmd']:
2085 if opts['remotecmd']:
2080 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2086 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2081
2087
2082 other = hg.repository(ui, source)
2088 other = hg.repository(ui, source)
2083 revs = None
2089 revs = None
2084 if opts['rev'] and not other.local():
2090 if opts['rev'] and not other.local():
2085 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2091 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2086 elif opts['rev']:
2092 elif opts['rev']:
2087 revs = [other.lookup(rev) for rev in opts['rev']]
2093 revs = [other.lookup(rev) for rev in opts['rev']]
2088 modheads = repo.pull(other, heads=revs, force=opts['force'])
2094 modheads = repo.pull(other, heads=revs, force=opts['force'])
2089 return postincoming(ui, repo, modheads, opts['update'])
2095 return postincoming(ui, repo, modheads, opts['update'])
2090
2096
2091 def push(ui, repo, dest="default-push", **opts):
2097 def push(ui, repo, dest="default-push", **opts):
2092 """push changes to the specified destination
2098 """push changes to the specified destination
2093
2099
2094 Push changes from the local repository to the given destination.
2100 Push changes from the local repository to the given destination.
2095
2101
2096 This is the symmetrical operation for pull. It helps to move
2102 This is the symmetrical operation for pull. It helps to move
2097 changes from the current repository to a different one. If the
2103 changes from the current repository to a different one. If the
2098 destination is local this is identical to a pull in that directory
2104 destination is local this is identical to a pull in that directory
2099 from the current one.
2105 from the current one.
2100
2106
2101 By default, push will refuse to run if it detects the result would
2107 By default, push will refuse to run if it detects the result would
2102 increase the number of remote heads. This generally indicates the
2108 increase the number of remote heads. This generally indicates the
2103 the client has forgotten to sync and merge before pushing.
2109 the client has forgotten to sync and merge before pushing.
2104
2110
2105 Valid URLs are of the form:
2111 Valid URLs are of the form:
2106
2112
2107 local/filesystem/path
2113 local/filesystem/path
2108 ssh://[user@]host[:port][/path]
2114 ssh://[user@]host[:port][/path]
2109
2115
2110 Look at the help text for the pull command for important details
2116 Look at the help text for the pull command for important details
2111 about ssh:// URLs.
2117 about ssh:// URLs.
2112 """
2118 """
2113 dest = ui.expandpath(dest)
2119 dest = ui.expandpath(dest)
2114 ui.status('pushing to %s\n' % (dest))
2120 ui.status('pushing to %s\n' % (dest))
2115
2121
2116 if opts['ssh']:
2122 if opts['ssh']:
2117 ui.setconfig("ui", "ssh", opts['ssh'])
2123 ui.setconfig("ui", "ssh", opts['ssh'])
2118 if opts['remotecmd']:
2124 if opts['remotecmd']:
2119 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2125 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2120
2126
2121 other = hg.repository(ui, dest)
2127 other = hg.repository(ui, dest)
2122 revs = None
2128 revs = None
2123 if opts['rev']:
2129 if opts['rev']:
2124 revs = [repo.lookup(rev) for rev in opts['rev']]
2130 revs = [repo.lookup(rev) for rev in opts['rev']]
2125 r = repo.push(other, opts['force'], revs=revs)
2131 r = repo.push(other, opts['force'], revs=revs)
2126 return r == 0
2132 return r == 0
2127
2133
2128 def rawcommit(ui, repo, *flist, **rc):
2134 def rawcommit(ui, repo, *flist, **rc):
2129 """raw commit interface (DEPRECATED)
2135 """raw commit interface (DEPRECATED)
2130
2136
2131 (DEPRECATED)
2137 (DEPRECATED)
2132 Lowlevel commit, for use in helper scripts.
2138 Lowlevel commit, for use in helper scripts.
2133
2139
2134 This command is not intended to be used by normal users, as it is
2140 This command is not intended to be used by normal users, as it is
2135 primarily useful for importing from other SCMs.
2141 primarily useful for importing from other SCMs.
2136
2142
2137 This command is now deprecated and will be removed in a future
2143 This command is now deprecated and will be removed in a future
2138 release, please use debugsetparents and commit instead.
2144 release, please use debugsetparents and commit instead.
2139 """
2145 """
2140
2146
2141 ui.warn(_("(the rawcommit command is deprecated)\n"))
2147 ui.warn(_("(the rawcommit command is deprecated)\n"))
2142
2148
2143 message = rc['message']
2149 message = rc['message']
2144 if not message and rc['logfile']:
2150 if not message and rc['logfile']:
2145 try:
2151 try:
2146 message = open(rc['logfile']).read()
2152 message = open(rc['logfile']).read()
2147 except IOError:
2153 except IOError:
2148 pass
2154 pass
2149 if not message and not rc['logfile']:
2155 if not message and not rc['logfile']:
2150 raise util.Abort(_("missing commit message"))
2156 raise util.Abort(_("missing commit message"))
2151
2157
2152 files = relpath(repo, list(flist))
2158 files = relpath(repo, list(flist))
2153 if rc['files']:
2159 if rc['files']:
2154 files += open(rc['files']).read().splitlines()
2160 files += open(rc['files']).read().splitlines()
2155
2161
2156 rc['parent'] = map(repo.lookup, rc['parent'])
2162 rc['parent'] = map(repo.lookup, rc['parent'])
2157
2163
2158 try:
2164 try:
2159 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2165 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2160 except ValueError, inst:
2166 except ValueError, inst:
2161 raise util.Abort(str(inst))
2167 raise util.Abort(str(inst))
2162
2168
2163 def recover(ui, repo):
2169 def recover(ui, repo):
2164 """roll back an interrupted transaction
2170 """roll back an interrupted transaction
2165
2171
2166 Recover from an interrupted commit or pull.
2172 Recover from an interrupted commit or pull.
2167
2173
2168 This command tries to fix the repository status after an interrupted
2174 This command tries to fix the repository status after an interrupted
2169 operation. It should only be necessary when Mercurial suggests it.
2175 operation. It should only be necessary when Mercurial suggests it.
2170 """
2176 """
2171 if repo.recover():
2177 if repo.recover():
2172 return repo.verify()
2178 return repo.verify()
2173 return 1
2179 return 1
2174
2180
2175 def remove(ui, repo, *pats, **opts):
2181 def remove(ui, repo, *pats, **opts):
2176 """remove the specified files on the next commit
2182 """remove the specified files on the next commit
2177
2183
2178 Schedule the indicated files for removal from the repository.
2184 Schedule the indicated files for removal from the repository.
2179
2185
2180 This command schedules the files to be removed at the next commit.
2186 This command schedules the files to be removed at the next commit.
2181 This only removes files from the current branch, not from the
2187 This only removes files from the current branch, not from the
2182 entire project history. If the files still exist in the working
2188 entire project history. If the files still exist in the working
2183 directory, they will be deleted from it. If invoked with --after,
2189 directory, they will be deleted from it. If invoked with --after,
2184 files that have been manually deleted are marked as removed.
2190 files that have been manually deleted are marked as removed.
2185 """
2191 """
2186 names = []
2192 names = []
2187 if not opts['after'] and not pats:
2193 if not opts['after'] and not pats:
2188 raise util.Abort(_('no files specified'))
2194 raise util.Abort(_('no files specified'))
2189 def okaytoremove(abs, rel, exact):
2195 def okaytoremove(abs, rel, exact):
2190 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2196 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2191 reason = None
2197 reason = None
2192 if not deleted and opts['after']:
2198 if not deleted and opts['after']:
2193 reason = _('is still present')
2199 reason = _('is still present')
2194 elif modified and not opts['force']:
2200 elif modified and not opts['force']:
2195 reason = _('is modified')
2201 reason = _('is modified')
2196 elif added:
2202 elif added:
2197 reason = _('has been marked for add')
2203 reason = _('has been marked for add')
2198 elif unknown:
2204 elif unknown:
2199 reason = _('is not managed')
2205 reason = _('is not managed')
2200 elif removed:
2206 elif removed:
2201 return False
2207 return False
2202 if reason:
2208 if reason:
2203 if exact:
2209 if exact:
2204 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2210 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2205 else:
2211 else:
2206 return True
2212 return True
2207 for src, abs, rel, exact in walk(repo, pats, opts):
2213 for src, abs, rel, exact in walk(repo, pats, opts):
2208 if okaytoremove(abs, rel, exact):
2214 if okaytoremove(abs, rel, exact):
2209 if ui.verbose or not exact:
2215 if ui.verbose or not exact:
2210 ui.status(_('removing %s\n') % rel)
2216 ui.status(_('removing %s\n') % rel)
2211 names.append(abs)
2217 names.append(abs)
2212 repo.remove(names, unlink=not opts['after'])
2218 repo.remove(names, unlink=not opts['after'])
2213
2219
2214 def rename(ui, repo, *pats, **opts):
2220 def rename(ui, repo, *pats, **opts):
2215 """rename files; equivalent of copy + remove
2221 """rename files; equivalent of copy + remove
2216
2222
2217 Mark dest as copies of sources; mark sources for deletion. If
2223 Mark dest as copies of sources; mark sources for deletion. If
2218 dest is a directory, copies are put in that directory. If dest is
2224 dest is a directory, copies are put in that directory. If dest is
2219 a file, there can only be one source.
2225 a file, there can only be one source.
2220
2226
2221 By default, this command copies the contents of files as they
2227 By default, this command copies the contents of files as they
2222 stand in the working directory. If invoked with --after, the
2228 stand in the working directory. If invoked with --after, the
2223 operation is recorded, but no copying is performed.
2229 operation is recorded, but no copying is performed.
2224
2230
2225 This command takes effect in the next commit.
2231 This command takes effect in the next commit.
2226
2232
2227 NOTE: This command should be treated as experimental. While it
2233 NOTE: This command should be treated as experimental. While it
2228 should properly record rename files, this information is not yet
2234 should properly record rename files, this information is not yet
2229 fully used by merge, nor fully reported by log.
2235 fully used by merge, nor fully reported by log.
2230 """
2236 """
2231 wlock = repo.wlock(0)
2237 wlock = repo.wlock(0)
2232 errs, copied = docopy(ui, repo, pats, opts, wlock)
2238 errs, copied = docopy(ui, repo, pats, opts, wlock)
2233 names = []
2239 names = []
2234 for abs, rel, exact in copied:
2240 for abs, rel, exact in copied:
2235 if ui.verbose or not exact:
2241 if ui.verbose or not exact:
2236 ui.status(_('removing %s\n') % rel)
2242 ui.status(_('removing %s\n') % rel)
2237 names.append(abs)
2243 names.append(abs)
2238 repo.remove(names, True, wlock)
2244 repo.remove(names, True, wlock)
2239 return errs
2245 return errs
2240
2246
2241 def revert(ui, repo, *pats, **opts):
2247 def revert(ui, repo, *pats, **opts):
2242 """revert files or dirs to their states as of some revision
2248 """revert files or dirs to their states as of some revision
2243
2249
2244 With no revision specified, revert the named files or directories
2250 With no revision specified, revert the named files or directories
2245 to the contents they had in the parent of the working directory.
2251 to the contents they had in the parent of the working directory.
2246 This restores the contents of the affected files to an unmodified
2252 This restores the contents of the affected files to an unmodified
2247 state. If the working directory has two parents, you must
2253 state. If the working directory has two parents, you must
2248 explicitly specify the revision to revert to.
2254 explicitly specify the revision to revert to.
2249
2255
2250 Modified files are saved with a .orig suffix before reverting.
2256 Modified files are saved with a .orig suffix before reverting.
2251 To disable these backups, use --no-backup.
2257 To disable these backups, use --no-backup.
2252
2258
2253 Using the -r option, revert the given files or directories to
2259 Using the -r option, revert the given files or directories to
2254 their contents as of a specific revision. This can be helpful to"roll
2260 their contents as of a specific revision. This can be helpful to"roll
2255 back" some or all of a change that should not have been committed.
2261 back" some or all of a change that should not have been committed.
2256
2262
2257 Revert modifies the working directory. It does not commit any
2263 Revert modifies the working directory. It does not commit any
2258 changes, or change the parent of the working directory. If you
2264 changes, or change the parent of the working directory. If you
2259 revert to a revision other than the parent of the working
2265 revert to a revision other than the parent of the working
2260 directory, the reverted files will thus appear modified
2266 directory, the reverted files will thus appear modified
2261 afterwards.
2267 afterwards.
2262
2268
2263 If a file has been deleted, it is recreated. If the executable
2269 If a file has been deleted, it is recreated. If the executable
2264 mode of a file was changed, it is reset.
2270 mode of a file was changed, it is reset.
2265
2271
2266 If names are given, all files matching the names are reverted.
2272 If names are given, all files matching the names are reverted.
2267
2273
2268 If no arguments are given, all files in the repository are reverted.
2274 If no arguments are given, all files in the repository are reverted.
2269 """
2275 """
2270 parent, p2 = repo.dirstate.parents()
2276 parent, p2 = repo.dirstate.parents()
2271 if opts['rev']:
2277 if opts['rev']:
2272 node = repo.lookup(opts['rev'])
2278 node = repo.lookup(opts['rev'])
2273 elif p2 != nullid:
2279 elif p2 != nullid:
2274 raise util.Abort(_('working dir has two parents; '
2280 raise util.Abort(_('working dir has two parents; '
2275 'you must specify the revision to revert to'))
2281 'you must specify the revision to revert to'))
2276 else:
2282 else:
2277 node = parent
2283 node = parent
2278 mf = repo.manifest.read(repo.changelog.read(node)[0])
2284 mf = repo.manifest.read(repo.changelog.read(node)[0])
2279
2285
2280 wlock = repo.wlock()
2286 wlock = repo.wlock()
2281
2287
2282 # need all matching names in dirstate and manifest of target rev,
2288 # need all matching names in dirstate and manifest of target rev,
2283 # so have to walk both. do not print errors if files exist in one
2289 # so have to walk both. do not print errors if files exist in one
2284 # but not other.
2290 # but not other.
2285
2291
2286 names = {}
2292 names = {}
2287 target_only = {}
2293 target_only = {}
2288
2294
2289 # walk dirstate.
2295 # walk dirstate.
2290
2296
2291 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2297 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2292 names[abs] = (rel, exact)
2298 names[abs] = (rel, exact)
2293 if src == 'b':
2299 if src == 'b':
2294 target_only[abs] = True
2300 target_only[abs] = True
2295
2301
2296 # walk target manifest.
2302 # walk target manifest.
2297
2303
2298 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2304 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2299 badmatch=names.has_key):
2305 badmatch=names.has_key):
2300 if abs in names: continue
2306 if abs in names: continue
2301 names[abs] = (rel, exact)
2307 names[abs] = (rel, exact)
2302 target_only[abs] = True
2308 target_only[abs] = True
2303
2309
2304 changes = repo.changes(match=names.has_key, wlock=wlock)
2310 changes = repo.changes(match=names.has_key, wlock=wlock)
2305 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2311 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2306
2312
2307 revert = ([], _('reverting %s\n'))
2313 revert = ([], _('reverting %s\n'))
2308 add = ([], _('adding %s\n'))
2314 add = ([], _('adding %s\n'))
2309 remove = ([], _('removing %s\n'))
2315 remove = ([], _('removing %s\n'))
2310 forget = ([], _('forgetting %s\n'))
2316 forget = ([], _('forgetting %s\n'))
2311 undelete = ([], _('undeleting %s\n'))
2317 undelete = ([], _('undeleting %s\n'))
2312 update = {}
2318 update = {}
2313
2319
2314 disptable = (
2320 disptable = (
2315 # dispatch table:
2321 # dispatch table:
2316 # file state
2322 # file state
2317 # action if in target manifest
2323 # action if in target manifest
2318 # action if not in target manifest
2324 # action if not in target manifest
2319 # make backup if in target manifest
2325 # make backup if in target manifest
2320 # make backup if not in target manifest
2326 # make backup if not in target manifest
2321 (modified, revert, remove, True, True),
2327 (modified, revert, remove, True, True),
2322 (added, revert, forget, True, False),
2328 (added, revert, forget, True, False),
2323 (removed, undelete, None, False, False),
2329 (removed, undelete, None, False, False),
2324 (deleted, revert, remove, False, False),
2330 (deleted, revert, remove, False, False),
2325 (unknown, add, None, True, False),
2331 (unknown, add, None, True, False),
2326 (target_only, add, None, False, False),
2332 (target_only, add, None, False, False),
2327 )
2333 )
2328
2334
2329 entries = names.items()
2335 entries = names.items()
2330 entries.sort()
2336 entries.sort()
2331
2337
2332 for abs, (rel, exact) in entries:
2338 for abs, (rel, exact) in entries:
2333 in_mf = abs in mf
2339 in_mf = abs in mf
2334 def handle(xlist, dobackup):
2340 def handle(xlist, dobackup):
2335 xlist[0].append(abs)
2341 xlist[0].append(abs)
2336 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2342 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2337 bakname = "%s.orig" % rel
2343 bakname = "%s.orig" % rel
2338 ui.note(_('saving current version of %s as %s\n') %
2344 ui.note(_('saving current version of %s as %s\n') %
2339 (rel, bakname))
2345 (rel, bakname))
2340 shutil.copyfile(rel, bakname)
2346 shutil.copyfile(rel, bakname)
2341 shutil.copymode(rel, bakname)
2347 shutil.copymode(rel, bakname)
2342 if ui.verbose or not exact:
2348 if ui.verbose or not exact:
2343 ui.status(xlist[1] % rel)
2349 ui.status(xlist[1] % rel)
2344 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2350 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2345 if abs not in table: continue
2351 if abs not in table: continue
2346 # file has changed in dirstate
2352 # file has changed in dirstate
2347 if in_mf:
2353 if in_mf:
2348 handle(hitlist, backuphit)
2354 handle(hitlist, backuphit)
2349 elif misslist is not None:
2355 elif misslist is not None:
2350 handle(misslist, backupmiss)
2356 handle(misslist, backupmiss)
2351 else:
2357 else:
2352 if exact: ui.warn(_('file not managed: %s\n' % rel))
2358 if exact: ui.warn(_('file not managed: %s\n' % rel))
2353 break
2359 break
2354 else:
2360 else:
2355 # file has not changed in dirstate
2361 # file has not changed in dirstate
2356 if node == parent:
2362 if node == parent:
2357 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2363 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2358 continue
2364 continue
2359 if not in_mf:
2365 if not in_mf:
2360 handle(remove, False)
2366 handle(remove, False)
2361 update[abs] = True
2367 update[abs] = True
2362
2368
2363 repo.dirstate.forget(forget[0])
2369 repo.dirstate.forget(forget[0])
2364 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2370 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2365 show_stats=False)
2371 show_stats=False)
2366 repo.dirstate.update(add[0], 'a')
2372 repo.dirstate.update(add[0], 'a')
2367 repo.dirstate.update(undelete[0], 'n')
2373 repo.dirstate.update(undelete[0], 'n')
2368 repo.dirstate.update(remove[0], 'r')
2374 repo.dirstate.update(remove[0], 'r')
2369 return r
2375 return r
2370
2376
2371 def rollback(ui, repo):
2377 def rollback(ui, repo):
2372 """roll back the last transaction in this repository
2378 """roll back the last transaction in this repository
2373
2379
2374 Roll back the last transaction in this repository, restoring the
2380 Roll back the last transaction in this repository, restoring the
2375 project to its state prior to the transaction.
2381 project to its state prior to the transaction.
2376
2382
2377 Transactions are used to encapsulate the effects of all commands
2383 Transactions are used to encapsulate the effects of all commands
2378 that create new changesets or propagate existing changesets into a
2384 that create new changesets or propagate existing changesets into a
2379 repository. For example, the following commands are transactional,
2385 repository. For example, the following commands are transactional,
2380 and their effects can be rolled back:
2386 and their effects can be rolled back:
2381
2387
2382 commit
2388 commit
2383 import
2389 import
2384 pull
2390 pull
2385 push (with this repository as destination)
2391 push (with this repository as destination)
2386 unbundle
2392 unbundle
2387
2393
2388 This command should be used with care. There is only one level of
2394 This command should be used with care. There is only one level of
2389 rollback, and there is no way to undo a rollback.
2395 rollback, and there is no way to undo a rollback.
2390
2396
2391 This command is not intended for use on public repositories. Once
2397 This command is not intended for use on public repositories. Once
2392 changes are visible for pull by other users, rolling a transaction
2398 changes are visible for pull by other users, rolling a transaction
2393 back locally is ineffective (someone else may already have pulled
2399 back locally is ineffective (someone else may already have pulled
2394 the changes). Furthermore, a race is possible with readers of the
2400 the changes). Furthermore, a race is possible with readers of the
2395 repository; for example an in-progress pull from the repository
2401 repository; for example an in-progress pull from the repository
2396 may fail if a rollback is performed.
2402 may fail if a rollback is performed.
2397 """
2403 """
2398 repo.undo()
2404 repo.undo()
2399
2405
2400 def root(ui, repo):
2406 def root(ui, repo):
2401 """print the root (top) of the current working dir
2407 """print the root (top) of the current working dir
2402
2408
2403 Print the root directory of the current repository.
2409 Print the root directory of the current repository.
2404 """
2410 """
2405 ui.write(repo.root + "\n")
2411 ui.write(repo.root + "\n")
2406
2412
2407 def serve(ui, repo, **opts):
2413 def serve(ui, repo, **opts):
2408 """export the repository via HTTP
2414 """export the repository via HTTP
2409
2415
2410 Start a local HTTP repository browser and pull server.
2416 Start a local HTTP repository browser and pull server.
2411
2417
2412 By default, the server logs accesses to stdout and errors to
2418 By default, the server logs accesses to stdout and errors to
2413 stderr. Use the "-A" and "-E" options to log to files.
2419 stderr. Use the "-A" and "-E" options to log to files.
2414 """
2420 """
2415
2421
2416 if opts["stdio"]:
2422 if opts["stdio"]:
2417 if repo is None:
2423 if repo is None:
2418 raise hg.RepoError(_('no repo found'))
2424 raise hg.RepoError(_('no repo found'))
2419 fin, fout = sys.stdin, sys.stdout
2425 fin, fout = sys.stdin, sys.stdout
2420 sys.stdout = sys.stderr
2426 sys.stdout = sys.stderr
2421
2427
2422 # Prevent insertion/deletion of CRs
2428 # Prevent insertion/deletion of CRs
2423 util.set_binary(fin)
2429 util.set_binary(fin)
2424 util.set_binary(fout)
2430 util.set_binary(fout)
2425
2431
2426 def getarg():
2432 def getarg():
2427 argline = fin.readline()[:-1]
2433 argline = fin.readline()[:-1]
2428 arg, l = argline.split()
2434 arg, l = argline.split()
2429 val = fin.read(int(l))
2435 val = fin.read(int(l))
2430 return arg, val
2436 return arg, val
2431 def respond(v):
2437 def respond(v):
2432 fout.write("%d\n" % len(v))
2438 fout.write("%d\n" % len(v))
2433 fout.write(v)
2439 fout.write(v)
2434 fout.flush()
2440 fout.flush()
2435
2441
2436 lock = None
2442 lock = None
2437
2443
2438 while 1:
2444 while 1:
2439 cmd = fin.readline()[:-1]
2445 cmd = fin.readline()[:-1]
2440 if cmd == '':
2446 if cmd == '':
2441 return
2447 return
2442 if cmd == "heads":
2448 if cmd == "heads":
2443 h = repo.heads()
2449 h = repo.heads()
2444 respond(" ".join(map(hex, h)) + "\n")
2450 respond(" ".join(map(hex, h)) + "\n")
2445 if cmd == "lock":
2451 if cmd == "lock":
2446 lock = repo.lock()
2452 lock = repo.lock()
2447 respond("")
2453 respond("")
2448 if cmd == "unlock":
2454 if cmd == "unlock":
2449 if lock:
2455 if lock:
2450 lock.release()
2456 lock.release()
2451 lock = None
2457 lock = None
2452 respond("")
2458 respond("")
2453 elif cmd == "branches":
2459 elif cmd == "branches":
2454 arg, nodes = getarg()
2460 arg, nodes = getarg()
2455 nodes = map(bin, nodes.split(" "))
2461 nodes = map(bin, nodes.split(" "))
2456 r = []
2462 r = []
2457 for b in repo.branches(nodes):
2463 for b in repo.branches(nodes):
2458 r.append(" ".join(map(hex, b)) + "\n")
2464 r.append(" ".join(map(hex, b)) + "\n")
2459 respond("".join(r))
2465 respond("".join(r))
2460 elif cmd == "between":
2466 elif cmd == "between":
2461 arg, pairs = getarg()
2467 arg, pairs = getarg()
2462 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2468 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2463 r = []
2469 r = []
2464 for b in repo.between(pairs):
2470 for b in repo.between(pairs):
2465 r.append(" ".join(map(hex, b)) + "\n")
2471 r.append(" ".join(map(hex, b)) + "\n")
2466 respond("".join(r))
2472 respond("".join(r))
2467 elif cmd == "changegroup":
2473 elif cmd == "changegroup":
2468 nodes = []
2474 nodes = []
2469 arg, roots = getarg()
2475 arg, roots = getarg()
2470 nodes = map(bin, roots.split(" "))
2476 nodes = map(bin, roots.split(" "))
2471
2477
2472 cg = repo.changegroup(nodes, 'serve')
2478 cg = repo.changegroup(nodes, 'serve')
2473 while 1:
2479 while 1:
2474 d = cg.read(4096)
2480 d = cg.read(4096)
2475 if not d:
2481 if not d:
2476 break
2482 break
2477 fout.write(d)
2483 fout.write(d)
2478
2484
2479 fout.flush()
2485 fout.flush()
2480
2486
2481 elif cmd == "addchangegroup":
2487 elif cmd == "addchangegroup":
2482 if not lock:
2488 if not lock:
2483 respond("not locked")
2489 respond("not locked")
2484 continue
2490 continue
2485 respond("")
2491 respond("")
2486
2492
2487 r = repo.addchangegroup(fin, 'serve')
2493 r = repo.addchangegroup(fin, 'serve')
2488 respond(str(r))
2494 respond(str(r))
2489
2495
2490 optlist = ("name templates style address port ipv6"
2496 optlist = ("name templates style address port ipv6"
2491 " accesslog errorlog webdir_conf")
2497 " accesslog errorlog webdir_conf")
2492 for o in optlist.split():
2498 for o in optlist.split():
2493 if opts[o]:
2499 if opts[o]:
2494 ui.setconfig("web", o, opts[o])
2500 ui.setconfig("web", o, opts[o])
2495
2501
2496 if repo is None and not ui.config("web", "webdir_conf"):
2502 if repo is None and not ui.config("web", "webdir_conf"):
2497 raise hg.RepoError(_('no repo found'))
2503 raise hg.RepoError(_('no repo found'))
2498
2504
2499 if opts['daemon'] and not opts['daemon_pipefds']:
2505 if opts['daemon'] and not opts['daemon_pipefds']:
2500 rfd, wfd = os.pipe()
2506 rfd, wfd = os.pipe()
2501 args = sys.argv[:]
2507 args = sys.argv[:]
2502 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2508 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2503 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2509 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2504 args[0], args)
2510 args[0], args)
2505 os.close(wfd)
2511 os.close(wfd)
2506 os.read(rfd, 1)
2512 os.read(rfd, 1)
2507 os._exit(0)
2513 os._exit(0)
2508
2514
2509 try:
2515 try:
2510 httpd = hgweb.create_server(ui, repo)
2516 httpd = hgweb.create_server(ui, repo)
2511 except socket.error, inst:
2517 except socket.error, inst:
2512 raise util.Abort(_('cannot start server: ') + inst.args[1])
2518 raise util.Abort(_('cannot start server: ') + inst.args[1])
2513
2519
2514 if ui.verbose:
2520 if ui.verbose:
2515 addr, port = httpd.socket.getsockname()
2521 addr, port = httpd.socket.getsockname()
2516 if addr == '0.0.0.0':
2522 if addr == '0.0.0.0':
2517 addr = socket.gethostname()
2523 addr = socket.gethostname()
2518 else:
2524 else:
2519 try:
2525 try:
2520 addr = socket.gethostbyaddr(addr)[0]
2526 addr = socket.gethostbyaddr(addr)[0]
2521 except socket.error:
2527 except socket.error:
2522 pass
2528 pass
2523 if port != 80:
2529 if port != 80:
2524 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2530 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2525 else:
2531 else:
2526 ui.status(_('listening at http://%s/\n') % addr)
2532 ui.status(_('listening at http://%s/\n') % addr)
2527
2533
2528 if opts['pid_file']:
2534 if opts['pid_file']:
2529 fp = open(opts['pid_file'], 'w')
2535 fp = open(opts['pid_file'], 'w')
2530 fp.write(str(os.getpid()))
2536 fp.write(str(os.getpid()))
2531 fp.close()
2537 fp.close()
2532
2538
2533 if opts['daemon_pipefds']:
2539 if opts['daemon_pipefds']:
2534 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2540 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2535 os.close(rfd)
2541 os.close(rfd)
2536 os.write(wfd, 'y')
2542 os.write(wfd, 'y')
2537 os.close(wfd)
2543 os.close(wfd)
2538 sys.stdout.flush()
2544 sys.stdout.flush()
2539 sys.stderr.flush()
2545 sys.stderr.flush()
2540 fd = os.open(util.nulldev, os.O_RDWR)
2546 fd = os.open(util.nulldev, os.O_RDWR)
2541 if fd != 0: os.dup2(fd, 0)
2547 if fd != 0: os.dup2(fd, 0)
2542 if fd != 1: os.dup2(fd, 1)
2548 if fd != 1: os.dup2(fd, 1)
2543 if fd != 2: os.dup2(fd, 2)
2549 if fd != 2: os.dup2(fd, 2)
2544 if fd not in (0, 1, 2): os.close(fd)
2550 if fd not in (0, 1, 2): os.close(fd)
2545
2551
2546 httpd.serve_forever()
2552 httpd.serve_forever()
2547
2553
2548 def status(ui, repo, *pats, **opts):
2554 def status(ui, repo, *pats, **opts):
2549 """show changed files in the working directory
2555 """show changed files in the working directory
2550
2556
2551 Show changed files in the repository. If names are
2557 Show changed files in the repository. If names are
2552 given, only files that match are shown.
2558 given, only files that match are shown.
2553
2559
2554 The codes used to show the status of files are:
2560 The codes used to show the status of files are:
2555 M = modified
2561 M = modified
2556 A = added
2562 A = added
2557 R = removed
2563 R = removed
2558 ! = deleted, but still tracked
2564 ! = deleted, but still tracked
2559 ? = not tracked
2565 ? = not tracked
2560 I = ignored (not shown by default)
2566 I = ignored (not shown by default)
2561 """
2567 """
2562
2568
2563 show_ignored = opts['ignored'] and True or False
2569 show_ignored = opts['ignored'] and True or False
2564 files, matchfn, anypats = matchpats(repo, pats, opts)
2570 files, matchfn, anypats = matchpats(repo, pats, opts)
2565 cwd = (pats and repo.getcwd()) or ''
2571 cwd = (pats and repo.getcwd()) or ''
2566 modified, added, removed, deleted, unknown, ignored = [
2572 modified, added, removed, deleted, unknown, ignored = [
2567 [util.pathto(cwd, x) for x in n]
2573 [util.pathto(cwd, x) for x in n]
2568 for n in repo.changes(files=files, match=matchfn,
2574 for n in repo.changes(files=files, match=matchfn,
2569 show_ignored=show_ignored)]
2575 show_ignored=show_ignored)]
2570
2576
2571 changetypes = [('modified', 'M', modified),
2577 changetypes = [('modified', 'M', modified),
2572 ('added', 'A', added),
2578 ('added', 'A', added),
2573 ('removed', 'R', removed),
2579 ('removed', 'R', removed),
2574 ('deleted', '!', deleted),
2580 ('deleted', '!', deleted),
2575 ('unknown', '?', unknown),
2581 ('unknown', '?', unknown),
2576 ('ignored', 'I', ignored)]
2582 ('ignored', 'I', ignored)]
2577
2583
2578 end = opts['print0'] and '\0' or '\n'
2584 end = opts['print0'] and '\0' or '\n'
2579
2585
2580 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2586 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2581 or changetypes):
2587 or changetypes):
2582 if opts['no_status']:
2588 if opts['no_status']:
2583 format = "%%s%s" % end
2589 format = "%%s%s" % end
2584 else:
2590 else:
2585 format = "%s %%s%s" % (char, end)
2591 format = "%s %%s%s" % (char, end)
2586
2592
2587 for f in changes:
2593 for f in changes:
2588 ui.write(format % f)
2594 ui.write(format % f)
2589
2595
2590 def tag(ui, repo, name, rev_=None, **opts):
2596 def tag(ui, repo, name, rev_=None, **opts):
2591 """add a tag for the current tip or a given revision
2597 """add a tag for the current tip or a given revision
2592
2598
2593 Name a particular revision using <name>.
2599 Name a particular revision using <name>.
2594
2600
2595 Tags are used to name particular revisions of the repository and are
2601 Tags are used to name particular revisions of the repository and are
2596 very useful to compare different revision, to go back to significant
2602 very useful to compare different revision, to go back to significant
2597 earlier versions or to mark branch points as releases, etc.
2603 earlier versions or to mark branch points as releases, etc.
2598
2604
2599 If no revision is given, the tip is used.
2605 If no revision is given, the tip is used.
2600
2606
2601 To facilitate version control, distribution, and merging of tags,
2607 To facilitate version control, distribution, and merging of tags,
2602 they are stored as a file named ".hgtags" which is managed
2608 they are stored as a file named ".hgtags" which is managed
2603 similarly to other project files and can be hand-edited if
2609 similarly to other project files and can be hand-edited if
2604 necessary. The file '.hg/localtags' is used for local tags (not
2610 necessary. The file '.hg/localtags' is used for local tags (not
2605 shared among repositories).
2611 shared among repositories).
2606 """
2612 """
2607 if name == "tip":
2613 if name == "tip":
2608 raise util.Abort(_("the name 'tip' is reserved"))
2614 raise util.Abort(_("the name 'tip' is reserved"))
2609 if rev_ is not None:
2615 if rev_ is not None:
2610 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2616 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2611 "please use 'hg tag [-r REV] NAME' instead\n"))
2617 "please use 'hg tag [-r REV] NAME' instead\n"))
2612 if opts['rev']:
2618 if opts['rev']:
2613 raise util.Abort(_("use only one form to specify the revision"))
2619 raise util.Abort(_("use only one form to specify the revision"))
2614 if opts['rev']:
2620 if opts['rev']:
2615 rev_ = opts['rev']
2621 rev_ = opts['rev']
2616 if rev_:
2622 if rev_:
2617 r = hex(repo.lookup(rev_))
2623 r = hex(repo.lookup(rev_))
2618 else:
2624 else:
2619 r = hex(repo.changelog.tip())
2625 r = hex(repo.changelog.tip())
2620
2626
2621 disallowed = (revrangesep, '\r', '\n')
2627 disallowed = (revrangesep, '\r', '\n')
2622 for c in disallowed:
2628 for c in disallowed:
2623 if name.find(c) >= 0:
2629 if name.find(c) >= 0:
2624 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2630 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2625
2631
2626 repo.hook('pretag', throw=True, node=r, tag=name,
2632 repo.hook('pretag', throw=True, node=r, tag=name,
2627 local=int(not not opts['local']))
2633 local=int(not not opts['local']))
2628
2634
2629 if opts['local']:
2635 if opts['local']:
2630 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2636 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2631 repo.hook('tag', node=r, tag=name, local=1)
2637 repo.hook('tag', node=r, tag=name, local=1)
2632 return
2638 return
2633
2639
2634 for x in repo.changes():
2640 for x in repo.changes():
2635 if ".hgtags" in x:
2641 if ".hgtags" in x:
2636 raise util.Abort(_("working copy of .hgtags is changed "
2642 raise util.Abort(_("working copy of .hgtags is changed "
2637 "(please commit .hgtags manually)"))
2643 "(please commit .hgtags manually)"))
2638
2644
2639 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2645 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2640 if repo.dirstate.state(".hgtags") == '?':
2646 if repo.dirstate.state(".hgtags") == '?':
2641 repo.add([".hgtags"])
2647 repo.add([".hgtags"])
2642
2648
2643 message = (opts['message'] or
2649 message = (opts['message'] or
2644 _("Added tag %s for changeset %s") % (name, r))
2650 _("Added tag %s for changeset %s") % (name, r))
2645 try:
2651 try:
2646 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2652 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2647 repo.hook('tag', node=r, tag=name, local=0)
2653 repo.hook('tag', node=r, tag=name, local=0)
2648 except ValueError, inst:
2654 except ValueError, inst:
2649 raise util.Abort(str(inst))
2655 raise util.Abort(str(inst))
2650
2656
2651 def tags(ui, repo):
2657 def tags(ui, repo):
2652 """list repository tags
2658 """list repository tags
2653
2659
2654 List the repository tags.
2660 List the repository tags.
2655
2661
2656 This lists both regular and local tags.
2662 This lists both regular and local tags.
2657 """
2663 """
2658
2664
2659 l = repo.tagslist()
2665 l = repo.tagslist()
2660 l.reverse()
2666 l.reverse()
2661 for t, n in l:
2667 for t, n in l:
2662 try:
2668 try:
2663 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2669 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2664 except KeyError:
2670 except KeyError:
2665 r = " ?:?"
2671 r = " ?:?"
2666 if ui.quiet:
2672 if ui.quiet:
2667 ui.write("%s\n" % t)
2673 ui.write("%s\n" % t)
2668 else:
2674 else:
2669 ui.write("%-30s %s\n" % (t, r))
2675 ui.write("%-30s %s\n" % (t, r))
2670
2676
2671 def tip(ui, repo, **opts):
2677 def tip(ui, repo, **opts):
2672 """show the tip revision
2678 """show the tip revision
2673
2679
2674 Show the tip revision.
2680 Show the tip revision.
2675 """
2681 """
2676 n = repo.changelog.tip()
2682 n = repo.changelog.tip()
2677 br = None
2683 br = None
2678 if opts['branches']:
2684 if opts['branches']:
2679 br = repo.branchlookup([n])
2685 br = repo.branchlookup([n])
2680 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2686 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2681 if opts['patch']:
2687 if opts['patch']:
2682 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2688 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2683
2689
2684 def unbundle(ui, repo, fname, **opts):
2690 def unbundle(ui, repo, fname, **opts):
2685 """apply a changegroup file
2691 """apply a changegroup file
2686
2692
2687 Apply a compressed changegroup file generated by the bundle
2693 Apply a compressed changegroup file generated by the bundle
2688 command.
2694 command.
2689 """
2695 """
2690 f = urllib.urlopen(fname)
2696 f = urllib.urlopen(fname)
2691
2697
2692 header = f.read(6)
2698 header = f.read(6)
2693 if not header.startswith("HG"):
2699 if not header.startswith("HG"):
2694 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2700 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2695 elif not header.startswith("HG10"):
2701 elif not header.startswith("HG10"):
2696 raise util.Abort(_("%s: unknown bundle version") % fname)
2702 raise util.Abort(_("%s: unknown bundle version") % fname)
2697 elif header == "HG10BZ":
2703 elif header == "HG10BZ":
2698 def generator(f):
2704 def generator(f):
2699 zd = bz2.BZ2Decompressor()
2705 zd = bz2.BZ2Decompressor()
2700 zd.decompress("BZ")
2706 zd.decompress("BZ")
2701 for chunk in f:
2707 for chunk in f:
2702 yield zd.decompress(chunk)
2708 yield zd.decompress(chunk)
2703 elif header == "HG10UN":
2709 elif header == "HG10UN":
2704 def generator(f):
2710 def generator(f):
2705 for chunk in f:
2711 for chunk in f:
2706 yield chunk
2712 yield chunk
2707 else:
2713 else:
2708 raise util.Abort(_("%s: unknown bundle compression type")
2714 raise util.Abort(_("%s: unknown bundle compression type")
2709 % fname)
2715 % fname)
2710 gen = generator(util.filechunkiter(f, 4096))
2716 gen = generator(util.filechunkiter(f, 4096))
2711 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle')
2717 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle')
2712 return postincoming(ui, repo, modheads, opts['update'])
2718 return postincoming(ui, repo, modheads, opts['update'])
2713
2719
2714 def undo(ui, repo):
2720 def undo(ui, repo):
2715 """undo the last commit or pull (DEPRECATED)
2721 """undo the last commit or pull (DEPRECATED)
2716
2722
2717 (DEPRECATED)
2723 (DEPRECATED)
2718 This command is now deprecated and will be removed in a future
2724 This command is now deprecated and will be removed in a future
2719 release. Please use the rollback command instead. For usage
2725 release. Please use the rollback command instead. For usage
2720 instructions, see the rollback command.
2726 instructions, see the rollback command.
2721 """
2727 """
2722 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2728 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2723 repo.undo()
2729 repo.undo()
2724
2730
2725 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2731 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2726 branch=None, **opts):
2732 branch=None, **opts):
2727 """update or merge working directory
2733 """update or merge working directory
2728
2734
2729 Update the working directory to the specified revision.
2735 Update the working directory to the specified revision.
2730
2736
2731 If there are no outstanding changes in the working directory and
2737 If there are no outstanding changes in the working directory and
2732 there is a linear relationship between the current version and the
2738 there is a linear relationship between the current version and the
2733 requested version, the result is the requested version.
2739 requested version, the result is the requested version.
2734
2740
2735 Otherwise the result is a merge between the contents of the
2741 Otherwise the result is a merge between the contents of the
2736 current working directory and the requested version. Files that
2742 current working directory and the requested version. Files that
2737 changed between either parent are marked as changed for the next
2743 changed between either parent are marked as changed for the next
2738 commit and a commit must be performed before any further updates
2744 commit and a commit must be performed before any further updates
2739 are allowed.
2745 are allowed.
2740
2746
2741 By default, update will refuse to run if doing so would require
2747 By default, update will refuse to run if doing so would require
2742 merging or discarding local changes.
2748 merging or discarding local changes.
2743 """
2749 """
2744 if branch:
2750 if branch:
2745 br = repo.branchlookup(branch=branch)
2751 br = repo.branchlookup(branch=branch)
2746 found = []
2752 found = []
2747 for x in br:
2753 for x in br:
2748 if branch in br[x]:
2754 if branch in br[x]:
2749 found.append(x)
2755 found.append(x)
2750 if len(found) > 1:
2756 if len(found) > 1:
2751 ui.warn(_("Found multiple heads for %s\n") % branch)
2757 ui.warn(_("Found multiple heads for %s\n") % branch)
2752 for x in found:
2758 for x in found:
2753 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2759 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2754 return 1
2760 return 1
2755 if len(found) == 1:
2761 if len(found) == 1:
2756 node = found[0]
2762 node = found[0]
2757 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2763 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2758 else:
2764 else:
2759 ui.warn(_("branch %s not found\n") % (branch))
2765 ui.warn(_("branch %s not found\n") % (branch))
2760 return 1
2766 return 1
2761 else:
2767 else:
2762 node = node and repo.lookup(node) or repo.changelog.tip()
2768 node = node and repo.lookup(node) or repo.changelog.tip()
2763 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2769 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2764
2770
2765 def verify(ui, repo):
2771 def verify(ui, repo):
2766 """verify the integrity of the repository
2772 """verify the integrity of the repository
2767
2773
2768 Verify the integrity of the current repository.
2774 Verify the integrity of the current repository.
2769
2775
2770 This will perform an extensive check of the repository's
2776 This will perform an extensive check of the repository's
2771 integrity, validating the hashes and checksums of each entry in
2777 integrity, validating the hashes and checksums of each entry in
2772 the changelog, manifest, and tracked files, as well as the
2778 the changelog, manifest, and tracked files, as well as the
2773 integrity of their crosslinks and indices.
2779 integrity of their crosslinks and indices.
2774 """
2780 """
2775 return repo.verify()
2781 return repo.verify()
2776
2782
2777 # Command options and aliases are listed here, alphabetically
2783 # Command options and aliases are listed here, alphabetically
2778
2784
2779 table = {
2785 table = {
2780 "^add":
2786 "^add":
2781 (add,
2787 (add,
2782 [('I', 'include', [], _('include names matching the given patterns')),
2788 [('I', 'include', [], _('include names matching the given patterns')),
2783 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2789 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2784 _('hg add [OPTION]... [FILE]...')),
2790 _('hg add [OPTION]... [FILE]...')),
2785 "debugaddremove|addremove":
2791 "debugaddremove|addremove":
2786 (addremove,
2792 (addremove,
2787 [('I', 'include', [], _('include names matching the given patterns')),
2793 [('I', 'include', [], _('include names matching the given patterns')),
2788 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2794 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2789 _('hg addremove [OPTION]... [FILE]...')),
2795 _('hg addremove [OPTION]... [FILE]...')),
2790 "^annotate":
2796 "^annotate":
2791 (annotate,
2797 (annotate,
2792 [('r', 'rev', '', _('annotate the specified revision')),
2798 [('r', 'rev', '', _('annotate the specified revision')),
2793 ('a', 'text', None, _('treat all files as text')),
2799 ('a', 'text', None, _('treat all files as text')),
2794 ('u', 'user', None, _('list the author')),
2800 ('u', 'user', None, _('list the author')),
2795 ('d', 'date', None, _('list the date')),
2801 ('d', 'date', None, _('list the date')),
2796 ('n', 'number', None, _('list the revision number (default)')),
2802 ('n', 'number', None, _('list the revision number (default)')),
2797 ('c', 'changeset', None, _('list the changeset')),
2803 ('c', 'changeset', None, _('list the changeset')),
2798 ('I', 'include', [], _('include names matching the given patterns')),
2804 ('I', 'include', [], _('include names matching the given patterns')),
2799 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2805 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2800 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2806 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2801 "archive":
2807 "archive":
2802 (archive,
2808 (archive,
2803 [('', 'no-decode', None, _('do not pass files through decoders')),
2809 [('', 'no-decode', None, _('do not pass files through decoders')),
2804 ('p', 'prefix', '', _('directory prefix for files in archive')),
2810 ('p', 'prefix', '', _('directory prefix for files in archive')),
2805 ('r', 'rev', '', _('revision to distribute')),
2811 ('r', 'rev', '', _('revision to distribute')),
2806 ('t', 'type', '', _('type of distribution to create')),
2812 ('t', 'type', '', _('type of distribution to create')),
2807 ('I', 'include', [], _('include names matching the given patterns')),
2813 ('I', 'include', [], _('include names matching the given patterns')),
2808 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2814 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2809 _('hg archive [OPTION]... DEST')),
2815 _('hg archive [OPTION]... DEST')),
2810 "backout":
2816 "backout":
2811 (backout,
2817 (backout,
2812 [('', 'merge', None,
2818 [('', 'merge', None,
2813 _('merge with old dirstate parent after backout')),
2819 _('merge with old dirstate parent after backout')),
2814 ('m', 'message', '', _('use <text> as commit message')),
2820 ('m', 'message', '', _('use <text> as commit message')),
2815 ('l', 'logfile', '', _('read commit message from <file>')),
2821 ('l', 'logfile', '', _('read commit message from <file>')),
2816 ('d', 'date', '', _('record datecode as commit date')),
2822 ('d', 'date', '', _('record datecode as commit date')),
2817 ('u', 'user', '', _('record user as committer')),
2823 ('u', 'user', '', _('record user as committer')),
2818 ('I', 'include', [], _('include names matching the given patterns')),
2824 ('I', 'include', [], _('include names matching the given patterns')),
2819 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2825 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2820 _('hg backout [OPTION]... REV')),
2826 _('hg backout [OPTION]... REV')),
2821 "bundle":
2827 "bundle":
2822 (bundle,
2828 (bundle,
2823 [('f', 'force', None,
2829 [('f', 'force', None,
2824 _('run even when remote repository is unrelated'))],
2830 _('run even when remote repository is unrelated'))],
2825 _('hg bundle FILE DEST')),
2831 _('hg bundle FILE DEST')),
2826 "cat":
2832 "cat":
2827 (cat,
2833 (cat,
2828 [('o', 'output', '', _('print output to file with formatted name')),
2834 [('o', 'output', '', _('print output to file with formatted name')),
2829 ('r', 'rev', '', _('print the given revision')),
2835 ('r', 'rev', '', _('print the given revision')),
2830 ('I', 'include', [], _('include names matching the given patterns')),
2836 ('I', 'include', [], _('include names matching the given patterns')),
2831 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2837 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2832 _('hg cat [OPTION]... FILE...')),
2838 _('hg cat [OPTION]... FILE...')),
2833 "^clone":
2839 "^clone":
2834 (clone,
2840 (clone,
2835 [('U', 'noupdate', None, _('do not update the new working directory')),
2841 [('U', 'noupdate', None, _('do not update the new working directory')),
2836 ('r', 'rev', [],
2842 ('r', 'rev', [],
2837 _('a changeset you would like to have after cloning')),
2843 _('a changeset you would like to have after cloning')),
2838 ('', 'pull', None, _('use pull protocol to copy metadata')),
2844 ('', 'pull', None, _('use pull protocol to copy metadata')),
2839 ('e', 'ssh', '', _('specify ssh command to use')),
2845 ('e', 'ssh', '', _('specify ssh command to use')),
2840 ('', 'remotecmd', '',
2846 ('', 'remotecmd', '',
2841 _('specify hg command to run on the remote side'))],
2847 _('specify hg command to run on the remote side'))],
2842 _('hg clone [OPTION]... SOURCE [DEST]')),
2848 _('hg clone [OPTION]... SOURCE [DEST]')),
2843 "^commit|ci":
2849 "^commit|ci":
2844 (commit,
2850 (commit,
2845 [('A', 'addremove', None,
2851 [('A', 'addremove', None,
2846 _('mark new/missing files as added/removed before committing')),
2852 _('mark new/missing files as added/removed before committing')),
2847 ('m', 'message', '', _('use <text> as commit message')),
2853 ('m', 'message', '', _('use <text> as commit message')),
2848 ('l', 'logfile', '', _('read the commit message from <file>')),
2854 ('l', 'logfile', '', _('read the commit message from <file>')),
2849 ('d', 'date', '', _('record datecode as commit date')),
2855 ('d', 'date', '', _('record datecode as commit date')),
2850 ('u', 'user', '', _('record user as commiter')),
2856 ('u', 'user', '', _('record user as commiter')),
2851 ('I', 'include', [], _('include names matching the given patterns')),
2857 ('I', 'include', [], _('include names matching the given patterns')),
2852 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2858 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2853 _('hg commit [OPTION]... [FILE]...')),
2859 _('hg commit [OPTION]... [FILE]...')),
2854 "copy|cp":
2860 "copy|cp":
2855 (copy,
2861 (copy,
2856 [('A', 'after', None, _('record a copy that has already occurred')),
2862 [('A', 'after', None, _('record a copy that has already occurred')),
2857 ('f', 'force', None,
2863 ('f', 'force', None,
2858 _('forcibly copy over an existing managed file')),
2864 _('forcibly copy over an existing managed file')),
2859 ('I', 'include', [], _('include names matching the given patterns')),
2865 ('I', 'include', [], _('include names matching the given patterns')),
2860 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2866 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2861 _('hg copy [OPTION]... [SOURCE]... DEST')),
2867 _('hg copy [OPTION]... [SOURCE]... DEST')),
2862 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2868 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2863 "debugcomplete":
2869 "debugcomplete":
2864 (debugcomplete,
2870 (debugcomplete,
2865 [('o', 'options', None, _('show the command options'))],
2871 [('o', 'options', None, _('show the command options'))],
2866 _('debugcomplete [-o] CMD')),
2872 _('debugcomplete [-o] CMD')),
2867 "debugrebuildstate":
2873 "debugrebuildstate":
2868 (debugrebuildstate,
2874 (debugrebuildstate,
2869 [('r', 'rev', '', _('revision to rebuild to'))],
2875 [('r', 'rev', '', _('revision to rebuild to'))],
2870 _('debugrebuildstate [-r REV] [REV]')),
2876 _('debugrebuildstate [-r REV] [REV]')),
2871 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2877 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2872 "debugconfig": (debugconfig, [], _('debugconfig')),
2878 "debugconfig": (debugconfig, [], _('debugconfig')),
2873 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2879 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2874 "debugstate": (debugstate, [], _('debugstate')),
2880 "debugstate": (debugstate, [], _('debugstate')),
2875 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2881 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2876 "debugindex": (debugindex, [], _('debugindex FILE')),
2882 "debugindex": (debugindex, [], _('debugindex FILE')),
2877 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2883 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2878 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2884 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2879 "debugwalk":
2885 "debugwalk":
2880 (debugwalk,
2886 (debugwalk,
2881 [('I', 'include', [], _('include names matching the given patterns')),
2887 [('I', 'include', [], _('include names matching the given patterns')),
2882 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2888 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2883 _('debugwalk [OPTION]... [FILE]...')),
2889 _('debugwalk [OPTION]... [FILE]...')),
2884 "^diff":
2890 "^diff":
2885 (diff,
2891 (diff,
2886 [('r', 'rev', [], _('revision')),
2892 [('r', 'rev', [], _('revision')),
2887 ('a', 'text', None, _('treat all files as text')),
2893 ('a', 'text', None, _('treat all files as text')),
2888 ('p', 'show-function', None,
2894 ('p', 'show-function', None,
2889 _('show which function each change is in')),
2895 _('show which function each change is in')),
2890 ('w', 'ignore-all-space', None,
2896 ('w', 'ignore-all-space', None,
2891 _('ignore white space when comparing lines')),
2897 _('ignore white space when comparing lines')),
2892 ('I', 'include', [], _('include names matching the given patterns')),
2898 ('I', 'include', [], _('include names matching the given patterns')),
2893 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2899 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2894 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2900 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2895 "^export":
2901 "^export":
2896 (export,
2902 (export,
2897 [('o', 'output', '', _('print output to file with formatted name')),
2903 [('o', 'output', '', _('print output to file with formatted name')),
2898 ('a', 'text', None, _('treat all files as text')),
2904 ('a', 'text', None, _('treat all files as text')),
2899 ('', 'switch-parent', None, _('diff against the second parent'))],
2905 ('', 'switch-parent', None, _('diff against the second parent'))],
2900 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2906 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2901 "debugforget|forget":
2907 "debugforget|forget":
2902 (forget,
2908 (forget,
2903 [('I', 'include', [], _('include names matching the given patterns')),
2909 [('I', 'include', [], _('include names matching the given patterns')),
2904 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2910 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2905 _('hg forget [OPTION]... FILE...')),
2911 _('hg forget [OPTION]... FILE...')),
2906 "grep":
2912 "grep":
2907 (grep,
2913 (grep,
2908 [('0', 'print0', None, _('end fields with NUL')),
2914 [('0', 'print0', None, _('end fields with NUL')),
2909 ('', 'all', None, _('print all revisions that match')),
2915 ('', 'all', None, _('print all revisions that match')),
2910 ('i', 'ignore-case', None, _('ignore case when matching')),
2916 ('i', 'ignore-case', None, _('ignore case when matching')),
2911 ('l', 'files-with-matches', None,
2917 ('l', 'files-with-matches', None,
2912 _('print only filenames and revs that match')),
2918 _('print only filenames and revs that match')),
2913 ('n', 'line-number', None, _('print matching line numbers')),
2919 ('n', 'line-number', None, _('print matching line numbers')),
2914 ('r', 'rev', [], _('search in given revision range')),
2920 ('r', 'rev', [], _('search in given revision range')),
2915 ('u', 'user', None, _('print user who committed change')),
2921 ('u', 'user', None, _('print user who committed change')),
2916 ('I', 'include', [], _('include names matching the given patterns')),
2922 ('I', 'include', [], _('include names matching the given patterns')),
2917 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2923 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2918 _('hg grep [OPTION]... PATTERN [FILE]...')),
2924 _('hg grep [OPTION]... PATTERN [FILE]...')),
2919 "heads":
2925 "heads":
2920 (heads,
2926 (heads,
2921 [('b', 'branches', None, _('show branches')),
2927 [('b', 'branches', None, _('show branches')),
2922 ('', 'style', '', _('display using template map file')),
2928 ('', 'style', '', _('display using template map file')),
2923 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2929 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2924 ('', 'template', '', _('display with template'))],
2930 ('', 'template', '', _('display with template'))],
2925 _('hg heads [-b] [-r <rev>]')),
2931 _('hg heads [-b] [-r <rev>]')),
2926 "help": (help_, [], _('hg help [COMMAND]')),
2932 "help": (help_, [], _('hg help [COMMAND]')),
2927 "identify|id": (identify, [], _('hg identify')),
2933 "identify|id": (identify, [], _('hg identify')),
2928 "import|patch":
2934 "import|patch":
2929 (import_,
2935 (import_,
2930 [('p', 'strip', 1,
2936 [('p', 'strip', 1,
2931 _('directory strip option for patch. This has the same\n'
2937 _('directory strip option for patch. This has the same\n'
2932 'meaning as the corresponding patch option')),
2938 'meaning as the corresponding patch option')),
2933 ('b', 'base', '', _('base path')),
2939 ('b', 'base', '', _('base path')),
2934 ('f', 'force', None,
2940 ('f', 'force', None,
2935 _('skip check for outstanding uncommitted changes'))],
2941 _('skip check for outstanding uncommitted changes'))],
2936 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2942 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2937 "incoming|in": (incoming,
2943 "incoming|in": (incoming,
2938 [('M', 'no-merges', None, _('do not show merges')),
2944 [('M', 'no-merges', None, _('do not show merges')),
2939 ('f', 'force', None,
2945 ('f', 'force', None,
2940 _('run even when remote repository is unrelated')),
2946 _('run even when remote repository is unrelated')),
2941 ('', 'style', '', _('display using template map file')),
2947 ('', 'style', '', _('display using template map file')),
2942 ('n', 'newest-first', None, _('show newest record first')),
2948 ('n', 'newest-first', None, _('show newest record first')),
2943 ('', 'bundle', '', _('file to store the bundles into')),
2949 ('', 'bundle', '', _('file to store the bundles into')),
2944 ('p', 'patch', None, _('show patch')),
2950 ('p', 'patch', None, _('show patch')),
2945 ('', 'template', '', _('display with template')),
2951 ('', 'template', '', _('display with template')),
2946 ('e', 'ssh', '', _('specify ssh command to use')),
2952 ('e', 'ssh', '', _('specify ssh command to use')),
2947 ('', 'remotecmd', '',
2953 ('', 'remotecmd', '',
2948 _('specify hg command to run on the remote side'))],
2954 _('specify hg command to run on the remote side'))],
2949 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
2955 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
2950 "^init": (init, [], _('hg init [DEST]')),
2956 "^init": (init, [], _('hg init [DEST]')),
2951 "locate":
2957 "locate":
2952 (locate,
2958 (locate,
2953 [('r', 'rev', '', _('search the repository as it stood at rev')),
2959 [('r', 'rev', '', _('search the repository as it stood at rev')),
2954 ('0', 'print0', None,
2960 ('0', 'print0', None,
2955 _('end filenames with NUL, for use with xargs')),
2961 _('end filenames with NUL, for use with xargs')),
2956 ('f', 'fullpath', None,
2962 ('f', 'fullpath', None,
2957 _('print complete paths from the filesystem root')),
2963 _('print complete paths from the filesystem root')),
2958 ('I', 'include', [], _('include names matching the given patterns')),
2964 ('I', 'include', [], _('include names matching the given patterns')),
2959 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2965 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2960 _('hg locate [OPTION]... [PATTERN]...')),
2966 _('hg locate [OPTION]... [PATTERN]...')),
2961 "^log|history":
2967 "^log|history":
2962 (log,
2968 (log,
2963 [('b', 'branches', None, _('show branches')),
2969 [('b', 'branches', None, _('show branches')),
2964 ('k', 'keyword', [], _('search for a keyword')),
2970 ('k', 'keyword', [], _('search for a keyword')),
2965 ('l', 'limit', '', _('limit number of changes displayed')),
2971 ('l', 'limit', '', _('limit number of changes displayed')),
2966 ('r', 'rev', [], _('show the specified revision or range')),
2972 ('r', 'rev', [], _('show the specified revision or range')),
2967 ('M', 'no-merges', None, _('do not show merges')),
2973 ('M', 'no-merges', None, _('do not show merges')),
2968 ('', 'style', '', _('display using template map file')),
2974 ('', 'style', '', _('display using template map file')),
2969 ('m', 'only-merges', None, _('show only merges')),
2975 ('m', 'only-merges', None, _('show only merges')),
2970 ('p', 'patch', None, _('show patch')),
2976 ('p', 'patch', None, _('show patch')),
2971 ('', 'template', '', _('display with template')),
2977 ('', 'template', '', _('display with template')),
2972 ('I', 'include', [], _('include names matching the given patterns')),
2978 ('I', 'include', [], _('include names matching the given patterns')),
2973 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2979 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2974 _('hg log [OPTION]... [FILE]')),
2980 _('hg log [OPTION]... [FILE]')),
2975 "manifest": (manifest, [], _('hg manifest [REV]')),
2981 "manifest": (manifest, [], _('hg manifest [REV]')),
2976 "merge":
2982 "merge":
2977 (merge,
2983 (merge,
2978 [('b', 'branch', '', _('merge with head of a specific branch')),
2984 [('b', 'branch', '', _('merge with head of a specific branch')),
2979 ('f', 'force', None, _('force a merge with outstanding changes'))],
2985 ('f', 'force', None, _('force a merge with outstanding changes'))],
2980 _('hg merge [-b TAG] [-f] [REV]')),
2986 _('hg merge [-b TAG] [-f] [REV]')),
2981 "outgoing|out": (outgoing,
2987 "outgoing|out": (outgoing,
2982 [('M', 'no-merges', None, _('do not show merges')),
2988 [('M', 'no-merges', None, _('do not show merges')),
2983 ('f', 'force', None,
2989 ('f', 'force', None,
2984 _('run even when remote repository is unrelated')),
2990 _('run even when remote repository is unrelated')),
2985 ('p', 'patch', None, _('show patch')),
2991 ('p', 'patch', None, _('show patch')),
2986 ('', 'style', '', _('display using template map file')),
2992 ('', 'style', '', _('display using template map file')),
2987 ('n', 'newest-first', None, _('show newest record first')),
2993 ('n', 'newest-first', None, _('show newest record first')),
2988 ('', 'template', '', _('display with template')),
2994 ('', 'template', '', _('display with template')),
2989 ('e', 'ssh', '', _('specify ssh command to use')),
2995 ('e', 'ssh', '', _('specify ssh command to use')),
2990 ('', 'remotecmd', '',
2996 ('', 'remotecmd', '',
2991 _('specify hg command to run on the remote side'))],
2997 _('specify hg command to run on the remote side'))],
2992 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2998 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2993 "^parents":
2999 "^parents":
2994 (parents,
3000 (parents,
2995 [('b', 'branches', None, _('show branches')),
3001 [('b', 'branches', None, _('show branches')),
2996 ('', 'style', '', _('display using template map file')),
3002 ('', 'style', '', _('display using template map file')),
2997 ('', 'template', '', _('display with template'))],
3003 ('', 'template', '', _('display with template'))],
2998 _('hg parents [-b] [REV]')),
3004 _('hg parents [-b] [REV]')),
2999 "paths": (paths, [], _('hg paths [NAME]')),
3005 "paths": (paths, [], _('hg paths [NAME]')),
3000 "^pull":
3006 "^pull":
3001 (pull,
3007 (pull,
3002 [('u', 'update', None,
3008 [('u', 'update', None,
3003 _('update the working directory to tip after pull')),
3009 _('update the working directory to tip after pull')),
3004 ('e', 'ssh', '', _('specify ssh command to use')),
3010 ('e', 'ssh', '', _('specify ssh command to use')),
3005 ('f', 'force', None,
3011 ('f', 'force', None,
3006 _('run even when remote repository is unrelated')),
3012 _('run even when remote repository is unrelated')),
3007 ('r', 'rev', [], _('a specific revision you would like to pull')),
3013 ('r', 'rev', [], _('a specific revision you would like to pull')),
3008 ('', 'remotecmd', '',
3014 ('', 'remotecmd', '',
3009 _('specify hg command to run on the remote side'))],
3015 _('specify hg command to run on the remote side'))],
3010 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
3016 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
3011 "^push":
3017 "^push":
3012 (push,
3018 (push,
3013 [('f', 'force', None, _('force push')),
3019 [('f', 'force', None, _('force push')),
3014 ('e', 'ssh', '', _('specify ssh command to use')),
3020 ('e', 'ssh', '', _('specify ssh command to use')),
3015 ('r', 'rev', [], _('a specific revision you would like to push')),
3021 ('r', 'rev', [], _('a specific revision you would like to push')),
3016 ('', 'remotecmd', '',
3022 ('', 'remotecmd', '',
3017 _('specify hg command to run on the remote side'))],
3023 _('specify hg command to run on the remote side'))],
3018 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
3024 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
3019 "debugrawcommit|rawcommit":
3025 "debugrawcommit|rawcommit":
3020 (rawcommit,
3026 (rawcommit,
3021 [('p', 'parent', [], _('parent')),
3027 [('p', 'parent', [], _('parent')),
3022 ('d', 'date', '', _('date code')),
3028 ('d', 'date', '', _('date code')),
3023 ('u', 'user', '', _('user')),
3029 ('u', 'user', '', _('user')),
3024 ('F', 'files', '', _('file list')),
3030 ('F', 'files', '', _('file list')),
3025 ('m', 'message', '', _('commit message')),
3031 ('m', 'message', '', _('commit message')),
3026 ('l', 'logfile', '', _('commit message file'))],
3032 ('l', 'logfile', '', _('commit message file'))],
3027 _('hg debugrawcommit [OPTION]... [FILE]...')),
3033 _('hg debugrawcommit [OPTION]... [FILE]...')),
3028 "recover": (recover, [], _('hg recover')),
3034 "recover": (recover, [], _('hg recover')),
3029 "^remove|rm":
3035 "^remove|rm":
3030 (remove,
3036 (remove,
3031 [('A', 'after', None, _('record remove that has already occurred')),
3037 [('A', 'after', None, _('record remove that has already occurred')),
3032 ('f', 'force', None, _('remove file even if modified')),
3038 ('f', 'force', None, _('remove file even if modified')),
3033 ('I', 'include', [], _('include names matching the given patterns')),
3039 ('I', 'include', [], _('include names matching the given patterns')),
3034 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3040 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3035 _('hg remove [OPTION]... FILE...')),
3041 _('hg remove [OPTION]... FILE...')),
3036 "rename|mv":
3042 "rename|mv":
3037 (rename,
3043 (rename,
3038 [('A', 'after', None, _('record a rename that has already occurred')),
3044 [('A', 'after', None, _('record a rename that has already occurred')),
3039 ('f', 'force', None,
3045 ('f', 'force', None,
3040 _('forcibly copy over an existing managed file')),
3046 _('forcibly copy over an existing managed file')),
3041 ('I', 'include', [], _('include names matching the given patterns')),
3047 ('I', 'include', [], _('include names matching the given patterns')),
3042 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3048 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3043 _('hg rename [OPTION]... SOURCE... DEST')),
3049 _('hg rename [OPTION]... SOURCE... DEST')),
3044 "^revert":
3050 "^revert":
3045 (revert,
3051 (revert,
3046 [('r', 'rev', '', _('revision to revert to')),
3052 [('r', 'rev', '', _('revision to revert to')),
3047 ('', 'no-backup', None, _('do not save backup copies of files')),
3053 ('', 'no-backup', None, _('do not save backup copies of files')),
3048 ('I', 'include', [], _('include names matching given patterns')),
3054 ('I', 'include', [], _('include names matching given patterns')),
3049 ('X', 'exclude', [], _('exclude names matching given patterns'))],
3055 ('X', 'exclude', [], _('exclude names matching given patterns'))],
3050 _('hg revert [-r REV] [NAME]...')),
3056 _('hg revert [-r REV] [NAME]...')),
3051 "rollback": (rollback, [], _('hg rollback')),
3057 "rollback": (rollback, [], _('hg rollback')),
3052 "root": (root, [], _('hg root')),
3058 "root": (root, [], _('hg root')),
3053 "^serve":
3059 "^serve":
3054 (serve,
3060 (serve,
3055 [('A', 'accesslog', '', _('name of access log file to write to')),
3061 [('A', 'accesslog', '', _('name of access log file to write to')),
3056 ('d', 'daemon', None, _('run server in background')),
3062 ('d', 'daemon', None, _('run server in background')),
3057 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3063 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3058 ('E', 'errorlog', '', _('name of error log file to write to')),
3064 ('E', 'errorlog', '', _('name of error log file to write to')),
3059 ('p', 'port', 0, _('port to use (default: 8000)')),
3065 ('p', 'port', 0, _('port to use (default: 8000)')),
3060 ('a', 'address', '', _('address to use')),
3066 ('a', 'address', '', _('address to use')),
3061 ('n', 'name', '',
3067 ('n', 'name', '',
3062 _('name to show in web pages (default: working dir)')),
3068 _('name to show in web pages (default: working dir)')),
3063 ('', 'webdir-conf', '', _('name of the webdir config file'
3069 ('', 'webdir-conf', '', _('name of the webdir config file'
3064 ' (serve more than one repo)')),
3070 ' (serve more than one repo)')),
3065 ('', 'pid-file', '', _('name of file to write process ID to')),
3071 ('', 'pid-file', '', _('name of file to write process ID to')),
3066 ('', 'stdio', None, _('for remote clients')),
3072 ('', 'stdio', None, _('for remote clients')),
3067 ('t', 'templates', '', _('web templates to use')),
3073 ('t', 'templates', '', _('web templates to use')),
3068 ('', 'style', '', _('template style to use')),
3074 ('', 'style', '', _('template style to use')),
3069 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3075 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3070 _('hg serve [OPTION]...')),
3076 _('hg serve [OPTION]...')),
3071 "^status|st":
3077 "^status|st":
3072 (status,
3078 (status,
3073 [('m', 'modified', None, _('show only modified files')),
3079 [('m', 'modified', None, _('show only modified files')),
3074 ('a', 'added', None, _('show only added files')),
3080 ('a', 'added', None, _('show only added files')),
3075 ('r', 'removed', None, _('show only removed files')),
3081 ('r', 'removed', None, _('show only removed files')),
3076 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3082 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3077 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3083 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3078 ('i', 'ignored', None, _('show ignored files')),
3084 ('i', 'ignored', None, _('show ignored files')),
3079 ('n', 'no-status', None, _('hide status prefix')),
3085 ('n', 'no-status', None, _('hide status prefix')),
3080 ('0', 'print0', None,
3086 ('0', 'print0', None,
3081 _('end filenames with NUL, for use with xargs')),
3087 _('end filenames with NUL, for use with xargs')),
3082 ('I', 'include', [], _('include names matching the given patterns')),
3088 ('I', 'include', [], _('include names matching the given patterns')),
3083 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3089 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3084 _('hg status [OPTION]... [FILE]...')),
3090 _('hg status [OPTION]... [FILE]...')),
3085 "tag":
3091 "tag":
3086 (tag,
3092 (tag,
3087 [('l', 'local', None, _('make the tag local')),
3093 [('l', 'local', None, _('make the tag local')),
3088 ('m', 'message', '', _('message for tag commit log entry')),
3094 ('m', 'message', '', _('message for tag commit log entry')),
3089 ('d', 'date', '', _('record datecode as commit date')),
3095 ('d', 'date', '', _('record datecode as commit date')),
3090 ('u', 'user', '', _('record user as commiter')),
3096 ('u', 'user', '', _('record user as commiter')),
3091 ('r', 'rev', '', _('revision to tag'))],
3097 ('r', 'rev', '', _('revision to tag'))],
3092 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3098 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3093 "tags": (tags, [], _('hg tags')),
3099 "tags": (tags, [], _('hg tags')),
3094 "tip":
3100 "tip":
3095 (tip,
3101 (tip,
3096 [('b', 'branches', None, _('show branches')),
3102 [('b', 'branches', None, _('show branches')),
3097 ('', 'style', '', _('display using template map file')),
3103 ('', 'style', '', _('display using template map file')),
3098 ('p', 'patch', None, _('show patch')),
3104 ('p', 'patch', None, _('show patch')),
3099 ('', 'template', '', _('display with template'))],
3105 ('', 'template', '', _('display with template'))],
3100 _('hg tip [-b] [-p]')),
3106 _('hg tip [-b] [-p]')),
3101 "unbundle":
3107 "unbundle":
3102 (unbundle,
3108 (unbundle,
3103 [('u', 'update', None,
3109 [('u', 'update', None,
3104 _('update the working directory to tip after unbundle'))],
3110 _('update the working directory to tip after unbundle'))],
3105 _('hg unbundle [-u] FILE')),
3111 _('hg unbundle [-u] FILE')),
3106 "debugundo|undo": (undo, [], _('hg undo')),
3112 "debugundo|undo": (undo, [], _('hg undo')),
3107 "^update|up|checkout|co":
3113 "^update|up|checkout|co":
3108 (update,
3114 (update,
3109 [('b', 'branch', '', _('checkout the head of a specific branch')),
3115 [('b', 'branch', '', _('checkout the head of a specific branch')),
3110 ('m', 'merge', None, _('allow merging of branches')),
3116 ('m', 'merge', None, _('allow merging of branches')),
3111 ('C', 'clean', None, _('overwrite locally modified files')),
3117 ('C', 'clean', None, _('overwrite locally modified files')),
3112 ('f', 'force', None, _('force a merge with outstanding changes'))],
3118 ('f', 'force', None, _('force a merge with outstanding changes'))],
3113 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3119 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3114 "verify": (verify, [], _('hg verify')),
3120 "verify": (verify, [], _('hg verify')),
3115 "version": (show_version, [], _('hg version')),
3121 "version": (show_version, [], _('hg version')),
3116 }
3122 }
3117
3123
3118 globalopts = [
3124 globalopts = [
3119 ('R', 'repository', '',
3125 ('R', 'repository', '',
3120 _('repository root directory or symbolic path name')),
3126 _('repository root directory or symbolic path name')),
3121 ('', 'cwd', '', _('change working directory')),
3127 ('', 'cwd', '', _('change working directory')),
3122 ('y', 'noninteractive', None,
3128 ('y', 'noninteractive', None,
3123 _('do not prompt, assume \'yes\' for any required answers')),
3129 _('do not prompt, assume \'yes\' for any required answers')),
3124 ('q', 'quiet', None, _('suppress output')),
3130 ('q', 'quiet', None, _('suppress output')),
3125 ('v', 'verbose', None, _('enable additional output')),
3131 ('v', 'verbose', None, _('enable additional output')),
3126 ('', 'debug', None, _('enable debugging output')),
3132 ('', 'debug', None, _('enable debugging output')),
3127 ('', 'debugger', None, _('start debugger')),
3133 ('', 'debugger', None, _('start debugger')),
3128 ('', 'traceback', None, _('print traceback on exception')),
3134 ('', 'traceback', None, _('print traceback on exception')),
3129 ('', 'time', None, _('time how long the command takes')),
3135 ('', 'time', None, _('time how long the command takes')),
3130 ('', 'profile', None, _('print command execution profile')),
3136 ('', 'profile', None, _('print command execution profile')),
3131 ('', 'version', None, _('output version information and exit')),
3137 ('', 'version', None, _('output version information and exit')),
3132 ('h', 'help', None, _('display help and exit')),
3138 ('h', 'help', None, _('display help and exit')),
3133 ]
3139 ]
3134
3140
3135 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3141 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3136 " debugindex debugindexdot")
3142 " debugindex debugindexdot")
3137 optionalrepo = ("paths serve debugconfig")
3143 optionalrepo = ("paths serve debugconfig")
3138
3144
3139 def findpossible(cmd):
3145 def findpossible(cmd):
3140 """
3146 """
3141 Return cmd -> (aliases, command table entry)
3147 Return cmd -> (aliases, command table entry)
3142 for each matching command.
3148 for each matching command.
3143 Return debug commands (or their aliases) only if no normal command matches.
3149 Return debug commands (or their aliases) only if no normal command matches.
3144 """
3150 """
3145 choice = {}
3151 choice = {}
3146 debugchoice = {}
3152 debugchoice = {}
3147 for e in table.keys():
3153 for e in table.keys():
3148 aliases = e.lstrip("^").split("|")
3154 aliases = e.lstrip("^").split("|")
3149 found = None
3155 found = None
3150 if cmd in aliases:
3156 if cmd in aliases:
3151 found = cmd
3157 found = cmd
3152 else:
3158 else:
3153 for a in aliases:
3159 for a in aliases:
3154 if a.startswith(cmd):
3160 if a.startswith(cmd):
3155 found = a
3161 found = a
3156 break
3162 break
3157 if found is not None:
3163 if found is not None:
3158 if aliases[0].startswith("debug"):
3164 if aliases[0].startswith("debug"):
3159 debugchoice[found] = (aliases, table[e])
3165 debugchoice[found] = (aliases, table[e])
3160 else:
3166 else:
3161 choice[found] = (aliases, table[e])
3167 choice[found] = (aliases, table[e])
3162
3168
3163 if not choice and debugchoice:
3169 if not choice and debugchoice:
3164 choice = debugchoice
3170 choice = debugchoice
3165
3171
3166 return choice
3172 return choice
3167
3173
3168 def find(cmd):
3174 def find(cmd):
3169 """Return (aliases, command table entry) for command string."""
3175 """Return (aliases, command table entry) for command string."""
3170 choice = findpossible(cmd)
3176 choice = findpossible(cmd)
3171
3177
3172 if choice.has_key(cmd):
3178 if choice.has_key(cmd):
3173 return choice[cmd]
3179 return choice[cmd]
3174
3180
3175 if len(choice) > 1:
3181 if len(choice) > 1:
3176 clist = choice.keys()
3182 clist = choice.keys()
3177 clist.sort()
3183 clist.sort()
3178 raise AmbiguousCommand(cmd, clist)
3184 raise AmbiguousCommand(cmd, clist)
3179
3185
3180 if choice:
3186 if choice:
3181 return choice.values()[0]
3187 return choice.values()[0]
3182
3188
3183 raise UnknownCommand(cmd)
3189 raise UnknownCommand(cmd)
3184
3190
3185 def catchterm(*args):
3191 def catchterm(*args):
3186 raise util.SignalInterrupt
3192 raise util.SignalInterrupt
3187
3193
3188 def run():
3194 def run():
3189 sys.exit(dispatch(sys.argv[1:]))
3195 sys.exit(dispatch(sys.argv[1:]))
3190
3196
3191 class ParseError(Exception):
3197 class ParseError(Exception):
3192 """Exception raised on errors in parsing the command line."""
3198 """Exception raised on errors in parsing the command line."""
3193
3199
3194 def parse(ui, args):
3200 def parse(ui, args):
3195 options = {}
3201 options = {}
3196 cmdoptions = {}
3202 cmdoptions = {}
3197
3203
3198 try:
3204 try:
3199 args = fancyopts.fancyopts(args, globalopts, options)
3205 args = fancyopts.fancyopts(args, globalopts, options)
3200 except fancyopts.getopt.GetoptError, inst:
3206 except fancyopts.getopt.GetoptError, inst:
3201 raise ParseError(None, inst)
3207 raise ParseError(None, inst)
3202
3208
3203 if args:
3209 if args:
3204 cmd, args = args[0], args[1:]
3210 cmd, args = args[0], args[1:]
3205 aliases, i = find(cmd)
3211 aliases, i = find(cmd)
3206 cmd = aliases[0]
3212 cmd = aliases[0]
3207 defaults = ui.config("defaults", cmd)
3213 defaults = ui.config("defaults", cmd)
3208 if defaults:
3214 if defaults:
3209 args = defaults.split() + args
3215 args = defaults.split() + args
3210 c = list(i[1])
3216 c = list(i[1])
3211 else:
3217 else:
3212 cmd = None
3218 cmd = None
3213 c = []
3219 c = []
3214
3220
3215 # combine global options into local
3221 # combine global options into local
3216 for o in globalopts:
3222 for o in globalopts:
3217 c.append((o[0], o[1], options[o[1]], o[3]))
3223 c.append((o[0], o[1], options[o[1]], o[3]))
3218
3224
3219 try:
3225 try:
3220 args = fancyopts.fancyopts(args, c, cmdoptions)
3226 args = fancyopts.fancyopts(args, c, cmdoptions)
3221 except fancyopts.getopt.GetoptError, inst:
3227 except fancyopts.getopt.GetoptError, inst:
3222 raise ParseError(cmd, inst)
3228 raise ParseError(cmd, inst)
3223
3229
3224 # separate global options back out
3230 # separate global options back out
3225 for o in globalopts:
3231 for o in globalopts:
3226 n = o[1]
3232 n = o[1]
3227 options[n] = cmdoptions[n]
3233 options[n] = cmdoptions[n]
3228 del cmdoptions[n]
3234 del cmdoptions[n]
3229
3235
3230 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3236 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3231
3237
3232 def dispatch(args):
3238 def dispatch(args):
3233 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3239 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3234 num = getattr(signal, name, None)
3240 num = getattr(signal, name, None)
3235 if num: signal.signal(num, catchterm)
3241 if num: signal.signal(num, catchterm)
3236
3242
3237 try:
3243 try:
3238 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3244 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3239 except util.Abort, inst:
3245 except util.Abort, inst:
3240 sys.stderr.write(_("abort: %s\n") % inst)
3246 sys.stderr.write(_("abort: %s\n") % inst)
3241 return -1
3247 return -1
3242
3248
3243 external = []
3249 external = []
3244 for x in u.extensions():
3250 for x in u.extensions():
3245 try:
3251 try:
3246 if x[1]:
3252 if x[1]:
3247 mod = imp.load_source(x[0], x[1])
3253 mod = imp.load_source(x[0], x[1])
3248 else:
3254 else:
3249 def importh(name):
3255 def importh(name):
3250 mod = __import__(name)
3256 mod = __import__(name)
3251 components = name.split('.')
3257 components = name.split('.')
3252 for comp in components[1:]:
3258 for comp in components[1:]:
3253 mod = getattr(mod, comp)
3259 mod = getattr(mod, comp)
3254 return mod
3260 return mod
3255 try:
3261 try:
3256 mod = importh("hgext." + x[0])
3262 mod = importh("hgext." + x[0])
3257 except ImportError:
3263 except ImportError:
3258 mod = importh(x[0])
3264 mod = importh(x[0])
3259 external.append(mod)
3265 external.append(mod)
3260 except Exception, inst:
3266 except Exception, inst:
3261 u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst))
3267 u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst))
3262 if u.traceback:
3268 if u.traceback:
3263 traceback.print_exc()
3269 traceback.print_exc()
3264 return 1
3270 return 1
3265 continue
3271 continue
3266
3272
3267 for x in external:
3273 for x in external:
3268 cmdtable = getattr(x, 'cmdtable', {})
3274 cmdtable = getattr(x, 'cmdtable', {})
3269 for t in cmdtable:
3275 for t in cmdtable:
3270 if t in table:
3276 if t in table:
3271 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3277 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3272 table.update(cmdtable)
3278 table.update(cmdtable)
3273
3279
3274 try:
3280 try:
3275 cmd, func, args, options, cmdoptions = parse(u, args)
3281 cmd, func, args, options, cmdoptions = parse(u, args)
3276 if options["time"]:
3282 if options["time"]:
3277 def get_times():
3283 def get_times():
3278 t = os.times()
3284 t = os.times()
3279 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3285 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3280 t = (t[0], t[1], t[2], t[3], time.clock())
3286 t = (t[0], t[1], t[2], t[3], time.clock())
3281 return t
3287 return t
3282 s = get_times()
3288 s = get_times()
3283 def print_time():
3289 def print_time():
3284 t = get_times()
3290 t = get_times()
3285 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3291 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3286 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3292 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3287 atexit.register(print_time)
3293 atexit.register(print_time)
3288
3294
3289 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3295 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3290 not options["noninteractive"], options["traceback"])
3296 not options["noninteractive"], options["traceback"])
3291
3297
3292 # enter the debugger before command execution
3298 # enter the debugger before command execution
3293 if options['debugger']:
3299 if options['debugger']:
3294 pdb.set_trace()
3300 pdb.set_trace()
3295
3301
3296 try:
3302 try:
3297 if options['cwd']:
3303 if options['cwd']:
3298 try:
3304 try:
3299 os.chdir(options['cwd'])
3305 os.chdir(options['cwd'])
3300 except OSError, inst:
3306 except OSError, inst:
3301 raise util.Abort('%s: %s' %
3307 raise util.Abort('%s: %s' %
3302 (options['cwd'], inst.strerror))
3308 (options['cwd'], inst.strerror))
3303
3309
3304 path = u.expandpath(options["repository"]) or ""
3310 path = u.expandpath(options["repository"]) or ""
3305 repo = path and hg.repository(u, path=path) or None
3311 repo = path and hg.repository(u, path=path) or None
3306
3312
3307 if options['help']:
3313 if options['help']:
3308 return help_(u, cmd, options['version'])
3314 return help_(u, cmd, options['version'])
3309 elif options['version']:
3315 elif options['version']:
3310 return show_version(u)
3316 return show_version(u)
3311 elif not cmd:
3317 elif not cmd:
3312 return help_(u, 'shortlist')
3318 return help_(u, 'shortlist')
3313
3319
3314 if cmd not in norepo.split():
3320 if cmd not in norepo.split():
3315 try:
3321 try:
3316 if not repo:
3322 if not repo:
3317 repo = hg.repository(u, path=path)
3323 repo = hg.repository(u, path=path)
3318 u = repo.ui
3324 u = repo.ui
3319 for x in external:
3325 for x in external:
3320 if hasattr(x, 'reposetup'):
3326 if hasattr(x, 'reposetup'):
3321 x.reposetup(u, repo)
3327 x.reposetup(u, repo)
3322 except hg.RepoError:
3328 except hg.RepoError:
3323 if cmd not in optionalrepo.split():
3329 if cmd not in optionalrepo.split():
3324 raise
3330 raise
3325 d = lambda: func(u, repo, *args, **cmdoptions)
3331 d = lambda: func(u, repo, *args, **cmdoptions)
3326 else:
3332 else:
3327 d = lambda: func(u, *args, **cmdoptions)
3333 d = lambda: func(u, *args, **cmdoptions)
3328
3334
3329 try:
3335 try:
3330 if options['profile']:
3336 if options['profile']:
3331 import hotshot, hotshot.stats
3337 import hotshot, hotshot.stats
3332 prof = hotshot.Profile("hg.prof")
3338 prof = hotshot.Profile("hg.prof")
3333 try:
3339 try:
3334 try:
3340 try:
3335 return prof.runcall(d)
3341 return prof.runcall(d)
3336 except:
3342 except:
3337 try:
3343 try:
3338 u.warn(_('exception raised - generating '
3344 u.warn(_('exception raised - generating '
3339 'profile anyway\n'))
3345 'profile anyway\n'))
3340 except:
3346 except:
3341 pass
3347 pass
3342 raise
3348 raise
3343 finally:
3349 finally:
3344 prof.close()
3350 prof.close()
3345 stats = hotshot.stats.load("hg.prof")
3351 stats = hotshot.stats.load("hg.prof")
3346 stats.strip_dirs()
3352 stats.strip_dirs()
3347 stats.sort_stats('time', 'calls')
3353 stats.sort_stats('time', 'calls')
3348 stats.print_stats(40)
3354 stats.print_stats(40)
3349 else:
3355 else:
3350 return d()
3356 return d()
3351 finally:
3357 finally:
3352 u.flush()
3358 u.flush()
3353 except:
3359 except:
3354 # enter the debugger when we hit an exception
3360 # enter the debugger when we hit an exception
3355 if options['debugger']:
3361 if options['debugger']:
3356 pdb.post_mortem(sys.exc_info()[2])
3362 pdb.post_mortem(sys.exc_info()[2])
3357 if u.traceback:
3363 if u.traceback:
3358 traceback.print_exc()
3364 traceback.print_exc()
3359 raise
3365 raise
3360 except ParseError, inst:
3366 except ParseError, inst:
3361 if inst.args[0]:
3367 if inst.args[0]:
3362 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3368 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3363 help_(u, inst.args[0])
3369 help_(u, inst.args[0])
3364 else:
3370 else:
3365 u.warn(_("hg: %s\n") % inst.args[1])
3371 u.warn(_("hg: %s\n") % inst.args[1])
3366 help_(u, 'shortlist')
3372 help_(u, 'shortlist')
3367 except AmbiguousCommand, inst:
3373 except AmbiguousCommand, inst:
3368 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3374 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3369 (inst.args[0], " ".join(inst.args[1])))
3375 (inst.args[0], " ".join(inst.args[1])))
3370 except UnknownCommand, inst:
3376 except UnknownCommand, inst:
3371 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3377 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3372 help_(u, 'shortlist')
3378 help_(u, 'shortlist')
3373 except hg.RepoError, inst:
3379 except hg.RepoError, inst:
3374 u.warn(_("abort: %s!\n") % inst)
3380 u.warn(_("abort: %s!\n") % inst)
3375 except lock.LockHeld, inst:
3381 except lock.LockHeld, inst:
3376 if inst.errno == errno.ETIMEDOUT:
3382 if inst.errno == errno.ETIMEDOUT:
3377 reason = _('timed out waiting for lock held by %s') % inst.locker
3383 reason = _('timed out waiting for lock held by %s') % inst.locker
3378 else:
3384 else:
3379 reason = _('lock held by %s') % inst.locker
3385 reason = _('lock held by %s') % inst.locker
3380 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3386 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3381 except lock.LockUnavailable, inst:
3387 except lock.LockUnavailable, inst:
3382 u.warn(_("abort: could not lock %s: %s\n") %
3388 u.warn(_("abort: could not lock %s: %s\n") %
3383 (inst.desc or inst.filename, inst.strerror))
3389 (inst.desc or inst.filename, inst.strerror))
3384 except revlog.RevlogError, inst:
3390 except revlog.RevlogError, inst:
3385 u.warn(_("abort: "), inst, "!\n")
3391 u.warn(_("abort: "), inst, "!\n")
3386 except util.SignalInterrupt:
3392 except util.SignalInterrupt:
3387 u.warn(_("killed!\n"))
3393 u.warn(_("killed!\n"))
3388 except KeyboardInterrupt:
3394 except KeyboardInterrupt:
3389 try:
3395 try:
3390 u.warn(_("interrupted!\n"))
3396 u.warn(_("interrupted!\n"))
3391 except IOError, inst:
3397 except IOError, inst:
3392 if inst.errno == errno.EPIPE:
3398 if inst.errno == errno.EPIPE:
3393 if u.debugflag:
3399 if u.debugflag:
3394 u.warn(_("\nbroken pipe\n"))
3400 u.warn(_("\nbroken pipe\n"))
3395 else:
3401 else:
3396 raise
3402 raise
3397 except IOError, inst:
3403 except IOError, inst:
3398 if hasattr(inst, "code"):
3404 if hasattr(inst, "code"):
3399 u.warn(_("abort: %s\n") % inst)
3405 u.warn(_("abort: %s\n") % inst)
3400 elif hasattr(inst, "reason"):
3406 elif hasattr(inst, "reason"):
3401 u.warn(_("abort: error: %s\n") % inst.reason[1])
3407 u.warn(_("abort: error: %s\n") % inst.reason[1])
3402 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3408 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3403 if u.debugflag:
3409 if u.debugflag:
3404 u.warn(_("broken pipe\n"))
3410 u.warn(_("broken pipe\n"))
3405 elif getattr(inst, "strerror", None):
3411 elif getattr(inst, "strerror", None):
3406 if getattr(inst, "filename", None):
3412 if getattr(inst, "filename", None):
3407 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3413 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3408 else:
3414 else:
3409 u.warn(_("abort: %s\n") % inst.strerror)
3415 u.warn(_("abort: %s\n") % inst.strerror)
3410 else:
3416 else:
3411 raise
3417 raise
3412 except OSError, inst:
3418 except OSError, inst:
3413 if hasattr(inst, "filename"):
3419 if hasattr(inst, "filename"):
3414 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3420 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3415 else:
3421 else:
3416 u.warn(_("abort: %s\n") % inst.strerror)
3422 u.warn(_("abort: %s\n") % inst.strerror)
3417 except util.Abort, inst:
3423 except util.Abort, inst:
3418 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3424 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3419 except TypeError, inst:
3425 except TypeError, inst:
3420 # was this an argument error?
3426 # was this an argument error?
3421 tb = traceback.extract_tb(sys.exc_info()[2])
3427 tb = traceback.extract_tb(sys.exc_info()[2])
3422 if len(tb) > 2: # no
3428 if len(tb) > 2: # no
3423 raise
3429 raise
3424 u.debug(inst, "\n")
3430 u.debug(inst, "\n")
3425 u.warn(_("%s: invalid arguments\n") % cmd)
3431 u.warn(_("%s: invalid arguments\n") % cmd)
3426 help_(u, cmd)
3432 help_(u, cmd)
3427 except SystemExit, inst:
3433 except SystemExit, inst:
3428 # Commands shouldn't sys.exit directly, but give a return code.
3434 # Commands shouldn't sys.exit directly, but give a return code.
3429 # Just in case catch this and and pass exit code to caller.
3435 # Just in case catch this and and pass exit code to caller.
3430 return inst.code
3436 return inst.code
3431 except:
3437 except:
3432 u.warn(_("** unknown exception encountered, details follow\n"))
3438 u.warn(_("** unknown exception encountered, details follow\n"))
3433 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3439 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3434 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3440 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3435 % version.get_version())
3441 % version.get_version())
3436 raise
3442 raise
3437
3443
3438 return -1
3444 return -1
@@ -1,2090 +1,2094 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import 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(), "appendfile changegroup")
13 demandload(globals(), "appendfile changegroup")
14 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
14 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
15 demandload(globals(), "revlog traceback")
15 demandload(globals(), "revlog traceback")
16
16
17 class localrepository(object):
17 class localrepository(object):
18 def __del__(self):
18 def __del__(self):
19 self.transhandle = None
19 self.transhandle = None
20 def __init__(self, parentui, path=None, create=0):
20 def __init__(self, parentui, path=None, create=0):
21 if not path:
21 if not path:
22 p = os.getcwd()
22 p = os.getcwd()
23 while not os.path.isdir(os.path.join(p, ".hg")):
23 while not os.path.isdir(os.path.join(p, ".hg")):
24 oldp = p
24 oldp = p
25 p = os.path.dirname(p)
25 p = os.path.dirname(p)
26 if p == oldp:
26 if p == oldp:
27 raise repo.RepoError(_("no repo found"))
27 raise repo.RepoError(_("no repo found"))
28 path = p
28 path = p
29 self.path = os.path.join(path, ".hg")
29 self.path = os.path.join(path, ".hg")
30
30
31 if not create and not os.path.isdir(self.path):
31 if not create and not os.path.isdir(self.path):
32 raise repo.RepoError(_("repository %s not found") % path)
32 raise repo.RepoError(_("repository %s not found") % path)
33
33
34 self.root = os.path.abspath(path)
34 self.root = os.path.abspath(path)
35 self.origroot = path
35 self.origroot = path
36 self.ui = ui.ui(parentui=parentui)
36 self.ui = ui.ui(parentui=parentui)
37 self.opener = util.opener(self.path)
37 self.opener = util.opener(self.path)
38 self.wopener = util.opener(self.root)
38 self.wopener = util.opener(self.root)
39
39
40 try:
40 try:
41 self.ui.readconfig(self.join("hgrc"), self.root)
41 self.ui.readconfig(self.join("hgrc"), self.root)
42 except IOError:
42 except IOError:
43 pass
43 pass
44
44
45 v = self.ui.revlogopts
45 v = self.ui.revlogopts
46 self.revlogversion = int(v.get('format', revlog.REVLOG_DEFAULT_FORMAT))
46 self.revlogversion = int(v.get('format', revlog.REVLOG_DEFAULT_FORMAT))
47 self.revlogv1 = self.revlogversion != revlog.REVLOGV0
47 self.revlogv1 = self.revlogversion != revlog.REVLOGV0
48 fl = v.get('flags', None)
48 fl = v.get('flags', None)
49 flags = 0
49 flags = 0
50 if fl != None:
50 if fl != None:
51 for x in fl.split():
51 for x in fl.split():
52 flags |= revlog.flagstr(x)
52 flags |= revlog.flagstr(x)
53 elif self.revlogv1:
53 elif self.revlogv1:
54 flags = revlog.REVLOG_DEFAULT_FLAGS
54 flags = revlog.REVLOG_DEFAULT_FLAGS
55
55
56 v = self.revlogversion | flags
56 v = self.revlogversion | flags
57 self.manifest = manifest.manifest(self.opener, v)
57 self.manifest = manifest.manifest(self.opener, v)
58 self.changelog = changelog.changelog(self.opener, v)
58 self.changelog = changelog.changelog(self.opener, v)
59
59
60 # the changelog might not have the inline index flag
60 # the changelog might not have the inline index flag
61 # on. If the format of the changelog is the same as found in
61 # on. If the format of the changelog is the same as found in
62 # .hgrc, apply any flags found in the .hgrc as well.
62 # .hgrc, apply any flags found in the .hgrc as well.
63 # Otherwise, just version from the changelog
63 # Otherwise, just version from the changelog
64 v = self.changelog.version
64 v = self.changelog.version
65 if v == self.revlogversion:
65 if v == self.revlogversion:
66 v |= flags
66 v |= flags
67 self.revlogversion = v
67 self.revlogversion = v
68
68
69 self.tagscache = None
69 self.tagscache = None
70 self.nodetagscache = None
70 self.nodetagscache = None
71 self.encodepats = None
71 self.encodepats = None
72 self.decodepats = None
72 self.decodepats = None
73 self.transhandle = None
73 self.transhandle = None
74
74
75 if create:
75 if create:
76 os.mkdir(self.path)
76 os.mkdir(self.path)
77 os.mkdir(self.join("data"))
77 os.mkdir(self.join("data"))
78
78
79 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
79 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
80
80
81 def hook(self, name, throw=False, **args):
81 def hook(self, name, throw=False, **args):
82 def callhook(hname, funcname):
82 def callhook(hname, funcname):
83 '''call python hook. hook is callable object, looked up as
83 '''call python hook. hook is callable object, looked up as
84 name in python module. if callable returns "true", hook
84 name in python module. if callable returns "true", hook
85 fails, else passes. if hook raises exception, treated as
85 fails, else passes. if hook raises exception, treated as
86 hook failure. exception propagates if throw is "true".
86 hook failure. exception propagates if throw is "true".
87
87
88 reason for "true" meaning "hook failed" is so that
88 reason for "true" meaning "hook failed" is so that
89 unmodified commands (e.g. mercurial.commands.update) can
89 unmodified commands (e.g. mercurial.commands.update) can
90 be run as hooks without wrappers to convert return values.'''
90 be run as hooks without wrappers to convert return values.'''
91
91
92 self.ui.note(_("calling hook %s: %s\n") % (hname, funcname))
92 self.ui.note(_("calling hook %s: %s\n") % (hname, funcname))
93 d = funcname.rfind('.')
93 d = funcname.rfind('.')
94 if d == -1:
94 if d == -1:
95 raise util.Abort(_('%s hook is invalid ("%s" not in a module)')
95 raise util.Abort(_('%s hook is invalid ("%s" not in a module)')
96 % (hname, funcname))
96 % (hname, funcname))
97 modname = funcname[:d]
97 modname = funcname[:d]
98 try:
98 try:
99 obj = __import__(modname)
99 obj = __import__(modname)
100 except ImportError:
100 except ImportError:
101 raise util.Abort(_('%s hook is invalid '
101 raise util.Abort(_('%s hook is invalid '
102 '(import of "%s" failed)') %
102 '(import of "%s" failed)') %
103 (hname, modname))
103 (hname, modname))
104 try:
104 try:
105 for p in funcname.split('.')[1:]:
105 for p in funcname.split('.')[1:]:
106 obj = getattr(obj, p)
106 obj = getattr(obj, p)
107 except AttributeError, err:
107 except AttributeError, err:
108 raise util.Abort(_('%s hook is invalid '
108 raise util.Abort(_('%s hook is invalid '
109 '("%s" is not defined)') %
109 '("%s" is not defined)') %
110 (hname, funcname))
110 (hname, funcname))
111 if not callable(obj):
111 if not callable(obj):
112 raise util.Abort(_('%s hook is invalid '
112 raise util.Abort(_('%s hook is invalid '
113 '("%s" is not callable)') %
113 '("%s" is not callable)') %
114 (hname, funcname))
114 (hname, funcname))
115 try:
115 try:
116 r = obj(ui=self.ui, repo=self, hooktype=name, **args)
116 r = obj(ui=self.ui, repo=self, hooktype=name, **args)
117 except (KeyboardInterrupt, util.SignalInterrupt):
117 except (KeyboardInterrupt, util.SignalInterrupt):
118 raise
118 raise
119 except Exception, exc:
119 except Exception, exc:
120 if isinstance(exc, util.Abort):
120 if isinstance(exc, util.Abort):
121 self.ui.warn(_('error: %s hook failed: %s\n') %
121 self.ui.warn(_('error: %s hook failed: %s\n') %
122 (hname, exc.args[0] % exc.args[1:]))
122 (hname, exc.args[0] % exc.args[1:]))
123 else:
123 else:
124 self.ui.warn(_('error: %s hook raised an exception: '
124 self.ui.warn(_('error: %s hook raised an exception: '
125 '%s\n') % (hname, exc))
125 '%s\n') % (hname, exc))
126 if throw:
126 if throw:
127 raise
127 raise
128 if self.ui.traceback:
128 if self.ui.traceback:
129 traceback.print_exc()
129 traceback.print_exc()
130 return True
130 return True
131 if r:
131 if r:
132 if throw:
132 if throw:
133 raise util.Abort(_('%s hook failed') % hname)
133 raise util.Abort(_('%s hook failed') % hname)
134 self.ui.warn(_('warning: %s hook failed\n') % hname)
134 self.ui.warn(_('warning: %s hook failed\n') % hname)
135 return r
135 return r
136
136
137 def runhook(name, cmd):
137 def runhook(name, cmd):
138 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
138 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
139 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()] +
139 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()] +
140 [(k.upper(), v) for k, v in args.iteritems()])
140 [(k.upper(), v) for k, v in args.iteritems()])
141 r = util.system(cmd, environ=env, cwd=self.root)
141 r = util.system(cmd, environ=env, cwd=self.root)
142 if r:
142 if r:
143 desc, r = util.explain_exit(r)
143 desc, r = util.explain_exit(r)
144 if throw:
144 if throw:
145 raise util.Abort(_('%s hook %s') % (name, desc))
145 raise util.Abort(_('%s hook %s') % (name, desc))
146 self.ui.warn(_('warning: %s hook %s\n') % (name, desc))
146 self.ui.warn(_('warning: %s hook %s\n') % (name, desc))
147 return r
147 return r
148
148
149 r = False
149 r = False
150 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
150 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
151 if hname.split(".", 1)[0] == name and cmd]
151 if hname.split(".", 1)[0] == name and cmd]
152 hooks.sort()
152 hooks.sort()
153 for hname, cmd in hooks:
153 for hname, cmd in hooks:
154 if cmd.startswith('python:'):
154 if cmd.startswith('python:'):
155 r = callhook(hname, cmd[7:].strip()) or r
155 r = callhook(hname, cmd[7:].strip()) or r
156 else:
156 else:
157 r = runhook(hname, cmd) or r
157 r = runhook(hname, cmd) or r
158 return r
158 return r
159
159
160 def tags(self):
160 def tags(self):
161 '''return a mapping of tag to node'''
161 '''return a mapping of tag to node'''
162 if not self.tagscache:
162 if not self.tagscache:
163 self.tagscache = {}
163 self.tagscache = {}
164
164
165 def parsetag(line, context):
165 def parsetag(line, context):
166 if not line:
166 if not line:
167 return
167 return
168 s = l.split(" ", 1)
168 s = l.split(" ", 1)
169 if len(s) != 2:
169 if len(s) != 2:
170 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
170 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
171 return
171 return
172 node, key = s
172 node, key = s
173 try:
173 try:
174 bin_n = bin(node)
174 bin_n = bin(node)
175 except TypeError:
175 except TypeError:
176 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
176 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
177 return
177 return
178 if bin_n not in self.changelog.nodemap:
178 if bin_n not in self.changelog.nodemap:
179 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
179 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
180 return
180 return
181 self.tagscache[key.strip()] = bin_n
181 self.tagscache[key.strip()] = bin_n
182
182
183 # read each head of the tags file, ending with the tip
183 # read each head of the tags file, ending with the tip
184 # and add each tag found to the map, with "newer" ones
184 # and add each tag found to the map, with "newer" ones
185 # taking precedence
185 # taking precedence
186 fl = self.file(".hgtags")
186 fl = self.file(".hgtags")
187 h = fl.heads()
187 h = fl.heads()
188 h.reverse()
188 h.reverse()
189 for r in h:
189 for r in h:
190 count = 0
190 count = 0
191 for l in fl.read(r).splitlines():
191 for l in fl.read(r).splitlines():
192 count += 1
192 count += 1
193 parsetag(l, ".hgtags:%d" % count)
193 parsetag(l, ".hgtags:%d" % count)
194
194
195 try:
195 try:
196 f = self.opener("localtags")
196 f = self.opener("localtags")
197 count = 0
197 count = 0
198 for l in f:
198 for l in f:
199 count += 1
199 count += 1
200 parsetag(l, "localtags:%d" % count)
200 parsetag(l, "localtags:%d" % count)
201 except IOError:
201 except IOError:
202 pass
202 pass
203
203
204 self.tagscache['tip'] = self.changelog.tip()
204 self.tagscache['tip'] = self.changelog.tip()
205
205
206 return self.tagscache
206 return self.tagscache
207
207
208 def tagslist(self):
208 def tagslist(self):
209 '''return a list of tags ordered by revision'''
209 '''return a list of tags ordered by revision'''
210 l = []
210 l = []
211 for t, n in self.tags().items():
211 for t, n in self.tags().items():
212 try:
212 try:
213 r = self.changelog.rev(n)
213 r = self.changelog.rev(n)
214 except:
214 except:
215 r = -2 # sort to the beginning of the list if unknown
215 r = -2 # sort to the beginning of the list if unknown
216 l.append((r, t, n))
216 l.append((r, t, n))
217 l.sort()
217 l.sort()
218 return [(t, n) for r, t, n in l]
218 return [(t, n) for r, t, n in l]
219
219
220 def nodetags(self, node):
220 def nodetags(self, node):
221 '''return the tags associated with a node'''
221 '''return the tags associated with a node'''
222 if not self.nodetagscache:
222 if not self.nodetagscache:
223 self.nodetagscache = {}
223 self.nodetagscache = {}
224 for t, n in self.tags().items():
224 for t, n in self.tags().items():
225 self.nodetagscache.setdefault(n, []).append(t)
225 self.nodetagscache.setdefault(n, []).append(t)
226 return self.nodetagscache.get(node, [])
226 return self.nodetagscache.get(node, [])
227
227
228 def lookup(self, key):
228 def lookup(self, key):
229 try:
229 try:
230 return self.tags()[key]
230 return self.tags()[key]
231 except KeyError:
231 except KeyError:
232 try:
232 try:
233 return self.changelog.lookup(key)
233 return self.changelog.lookup(key)
234 except:
234 except:
235 raise repo.RepoError(_("unknown revision '%s'") % key)
235 raise repo.RepoError(_("unknown revision '%s'") % key)
236
236
237 def dev(self):
237 def dev(self):
238 return os.stat(self.path).st_dev
238 return os.stat(self.path).st_dev
239
239
240 def local(self):
240 def local(self):
241 return True
241 return True
242
242
243 def join(self, f):
243 def join(self, f):
244 return os.path.join(self.path, f)
244 return os.path.join(self.path, f)
245
245
246 def wjoin(self, f):
246 def wjoin(self, f):
247 return os.path.join(self.root, f)
247 return os.path.join(self.root, f)
248
248
249 def file(self, f):
249 def file(self, f):
250 if f[0] == '/':
250 if f[0] == '/':
251 f = f[1:]
251 f = f[1:]
252 return filelog.filelog(self.opener, f, self.revlogversion)
252 return filelog.filelog(self.opener, f, self.revlogversion)
253
253
254 def getcwd(self):
254 def getcwd(self):
255 return self.dirstate.getcwd()
255 return self.dirstate.getcwd()
256
256
257 def wfile(self, f, mode='r'):
257 def wfile(self, f, mode='r'):
258 return self.wopener(f, mode)
258 return self.wopener(f, mode)
259
259
260 def wread(self, filename):
260 def wread(self, filename):
261 if self.encodepats == None:
261 if self.encodepats == None:
262 l = []
262 l = []
263 for pat, cmd in self.ui.configitems("encode"):
263 for pat, cmd in self.ui.configitems("encode"):
264 mf = util.matcher(self.root, "", [pat], [], [])[1]
264 mf = util.matcher(self.root, "", [pat], [], [])[1]
265 l.append((mf, cmd))
265 l.append((mf, cmd))
266 self.encodepats = l
266 self.encodepats = l
267
267
268 data = self.wopener(filename, 'r').read()
268 data = self.wopener(filename, 'r').read()
269
269
270 for mf, cmd in self.encodepats:
270 for mf, cmd in self.encodepats:
271 if mf(filename):
271 if mf(filename):
272 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
272 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
273 data = util.filter(data, cmd)
273 data = util.filter(data, cmd)
274 break
274 break
275
275
276 return data
276 return data
277
277
278 def wwrite(self, filename, data, fd=None):
278 def wwrite(self, filename, data, fd=None):
279 if self.decodepats == None:
279 if self.decodepats == None:
280 l = []
280 l = []
281 for pat, cmd in self.ui.configitems("decode"):
281 for pat, cmd in self.ui.configitems("decode"):
282 mf = util.matcher(self.root, "", [pat], [], [])[1]
282 mf = util.matcher(self.root, "", [pat], [], [])[1]
283 l.append((mf, cmd))
283 l.append((mf, cmd))
284 self.decodepats = l
284 self.decodepats = l
285
285
286 for mf, cmd in self.decodepats:
286 for mf, cmd in self.decodepats:
287 if mf(filename):
287 if mf(filename):
288 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
288 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
289 data = util.filter(data, cmd)
289 data = util.filter(data, cmd)
290 break
290 break
291
291
292 if fd:
292 if fd:
293 return fd.write(data)
293 return fd.write(data)
294 return self.wopener(filename, 'w').write(data)
294 return self.wopener(filename, 'w').write(data)
295
295
296 def transaction(self):
296 def transaction(self):
297 tr = self.transhandle
297 tr = self.transhandle
298 if tr != None and tr.running():
298 if tr != None and tr.running():
299 return tr.nest()
299 return tr.nest()
300
300
301 # save dirstate for undo
301 # save dirstate for undo
302 try:
302 try:
303 ds = self.opener("dirstate").read()
303 ds = self.opener("dirstate").read()
304 except IOError:
304 except IOError:
305 ds = ""
305 ds = ""
306 self.opener("journal.dirstate", "w").write(ds)
306 self.opener("journal.dirstate", "w").write(ds)
307
307
308 tr = transaction.transaction(self.ui.warn, self.opener,
308 tr = transaction.transaction(self.ui.warn, self.opener,
309 self.join("journal"),
309 self.join("journal"),
310 aftertrans(self.path))
310 aftertrans(self.path))
311 self.transhandle = tr
311 self.transhandle = tr
312 return tr
312 return tr
313
313
314 def recover(self):
314 def recover(self):
315 l = self.lock()
315 l = self.lock()
316 if os.path.exists(self.join("journal")):
316 if os.path.exists(self.join("journal")):
317 self.ui.status(_("rolling back interrupted transaction\n"))
317 self.ui.status(_("rolling back interrupted transaction\n"))
318 transaction.rollback(self.opener, self.join("journal"))
318 transaction.rollback(self.opener, self.join("journal"))
319 self.reload()
319 self.reload()
320 return True
320 return True
321 else:
321 else:
322 self.ui.warn(_("no interrupted transaction available\n"))
322 self.ui.warn(_("no interrupted transaction available\n"))
323 return False
323 return False
324
324
325 def undo(self, wlock=None):
325 def undo(self, wlock=None):
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 if os.path.exists(self.join("undo")):
329 if os.path.exists(self.join("undo")):
330 self.ui.status(_("rolling back last transaction\n"))
330 self.ui.status(_("rolling back last transaction\n"))
331 transaction.rollback(self.opener, self.join("undo"))
331 transaction.rollback(self.opener, self.join("undo"))
332 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
332 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
333 self.reload()
333 self.reload()
334 self.wreload()
334 self.wreload()
335 else:
335 else:
336 self.ui.warn(_("no undo information available\n"))
336 self.ui.warn(_("no undo information available\n"))
337
337
338 def wreload(self):
338 def wreload(self):
339 self.dirstate.read()
339 self.dirstate.read()
340
340
341 def reload(self):
341 def reload(self):
342 self.changelog.load()
342 self.changelog.load()
343 self.manifest.load()
343 self.manifest.load()
344 self.tagscache = None
344 self.tagscache = None
345 self.nodetagscache = None
345 self.nodetagscache = None
346
346
347 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None,
347 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None,
348 desc=None):
348 desc=None):
349 try:
349 try:
350 l = lock.lock(self.join(lockname), 0, releasefn, desc=desc)
350 l = lock.lock(self.join(lockname), 0, releasefn, desc=desc)
351 except lock.LockHeld, inst:
351 except lock.LockHeld, inst:
352 if not wait:
352 if not wait:
353 raise
353 raise
354 self.ui.warn(_("waiting for lock on %s held by %s\n") %
354 self.ui.warn(_("waiting for lock on %s held by %s\n") %
355 (desc, inst.args[0]))
355 (desc, inst.args[0]))
356 # default to 600 seconds timeout
356 # default to 600 seconds timeout
357 l = lock.lock(self.join(lockname),
357 l = lock.lock(self.join(lockname),
358 int(self.ui.config("ui", "timeout") or 600),
358 int(self.ui.config("ui", "timeout") or 600),
359 releasefn, desc=desc)
359 releasefn, desc=desc)
360 if acquirefn:
360 if acquirefn:
361 acquirefn()
361 acquirefn()
362 return l
362 return l
363
363
364 def lock(self, wait=1):
364 def lock(self, wait=1):
365 return self.do_lock("lock", wait, acquirefn=self.reload,
365 return self.do_lock("lock", wait, acquirefn=self.reload,
366 desc=_('repository %s') % self.origroot)
366 desc=_('repository %s') % self.origroot)
367
367
368 def wlock(self, wait=1):
368 def wlock(self, wait=1):
369 return self.do_lock("wlock", wait, self.dirstate.write,
369 return self.do_lock("wlock", wait, self.dirstate.write,
370 self.wreload,
370 self.wreload,
371 desc=_('working directory of %s') % self.origroot)
371 desc=_('working directory of %s') % self.origroot)
372
372
373 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
373 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
374 "determine whether a new filenode is needed"
374 "determine whether a new filenode is needed"
375 fp1 = manifest1.get(filename, nullid)
375 fp1 = manifest1.get(filename, nullid)
376 fp2 = manifest2.get(filename, nullid)
376 fp2 = manifest2.get(filename, nullid)
377
377
378 if fp2 != nullid:
378 if fp2 != nullid:
379 # is one parent an ancestor of the other?
379 # is one parent an ancestor of the other?
380 fpa = filelog.ancestor(fp1, fp2)
380 fpa = filelog.ancestor(fp1, fp2)
381 if fpa == fp1:
381 if fpa == fp1:
382 fp1, fp2 = fp2, nullid
382 fp1, fp2 = fp2, nullid
383 elif fpa == fp2:
383 elif fpa == fp2:
384 fp2 = nullid
384 fp2 = nullid
385
385
386 # is the file unmodified from the parent? report existing entry
386 # is the file unmodified from the parent? report existing entry
387 if fp2 == nullid and text == filelog.read(fp1):
387 if fp2 == nullid and text == filelog.read(fp1):
388 return (fp1, None, None)
388 return (fp1, None, None)
389
389
390 return (None, fp1, fp2)
390 return (None, fp1, fp2)
391
391
392 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
392 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
393 orig_parent = self.dirstate.parents()[0] or nullid
393 orig_parent = self.dirstate.parents()[0] or nullid
394 p1 = p1 or self.dirstate.parents()[0] or nullid
394 p1 = p1 or self.dirstate.parents()[0] or nullid
395 p2 = p2 or self.dirstate.parents()[1] or nullid
395 p2 = p2 or self.dirstate.parents()[1] or nullid
396 c1 = self.changelog.read(p1)
396 c1 = self.changelog.read(p1)
397 c2 = self.changelog.read(p2)
397 c2 = self.changelog.read(p2)
398 m1 = self.manifest.read(c1[0])
398 m1 = self.manifest.read(c1[0])
399 mf1 = self.manifest.readflags(c1[0])
399 mf1 = self.manifest.readflags(c1[0])
400 m2 = self.manifest.read(c2[0])
400 m2 = self.manifest.read(c2[0])
401 changed = []
401 changed = []
402
402
403 if orig_parent == p1:
403 if orig_parent == p1:
404 update_dirstate = 1
404 update_dirstate = 1
405 else:
405 else:
406 update_dirstate = 0
406 update_dirstate = 0
407
407
408 if not wlock:
408 if not wlock:
409 wlock = self.wlock()
409 wlock = self.wlock()
410 l = self.lock()
410 l = self.lock()
411 tr = self.transaction()
411 tr = self.transaction()
412 mm = m1.copy()
412 mm = m1.copy()
413 mfm = mf1.copy()
413 mfm = mf1.copy()
414 linkrev = self.changelog.count()
414 linkrev = self.changelog.count()
415 for f in files:
415 for f in files:
416 try:
416 try:
417 t = self.wread(f)
417 t = self.wread(f)
418 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
418 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
419 r = self.file(f)
419 r = self.file(f)
420 mfm[f] = tm
420 mfm[f] = tm
421
421
422 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
422 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
423 if entry:
423 if entry:
424 mm[f] = entry
424 mm[f] = entry
425 continue
425 continue
426
426
427 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
427 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
428 changed.append(f)
428 changed.append(f)
429 if update_dirstate:
429 if update_dirstate:
430 self.dirstate.update([f], "n")
430 self.dirstate.update([f], "n")
431 except IOError:
431 except IOError:
432 try:
432 try:
433 del mm[f]
433 del mm[f]
434 del mfm[f]
434 del mfm[f]
435 if update_dirstate:
435 if update_dirstate:
436 self.dirstate.forget([f])
436 self.dirstate.forget([f])
437 except:
437 except:
438 # deleted from p2?
438 # deleted from p2?
439 pass
439 pass
440
440
441 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
441 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
442 user = user or self.ui.username()
442 user = user or self.ui.username()
443 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
443 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
444 tr.close()
444 tr.close()
445 if update_dirstate:
445 if update_dirstate:
446 self.dirstate.setparents(n, nullid)
446 self.dirstate.setparents(n, nullid)
447
447
448 def commit(self, files=None, text="", user=None, date=None,
448 def commit(self, files=None, text="", user=None, date=None,
449 match=util.always, force=False, lock=None, wlock=None):
449 match=util.always, force=False, lock=None, wlock=None,
450 force_editor=False):
450 commit = []
451 commit = []
451 remove = []
452 remove = []
452 changed = []
453 changed = []
453
454
454 if files:
455 if files:
455 for f in files:
456 for f in files:
456 s = self.dirstate.state(f)
457 s = self.dirstate.state(f)
457 if s in 'nmai':
458 if s in 'nmai':
458 commit.append(f)
459 commit.append(f)
459 elif s == 'r':
460 elif s == 'r':
460 remove.append(f)
461 remove.append(f)
461 else:
462 else:
462 self.ui.warn(_("%s not tracked!\n") % f)
463 self.ui.warn(_("%s not tracked!\n") % f)
463 else:
464 else:
464 modified, added, removed, deleted, unknown = self.changes(match=match)
465 modified, added, removed, deleted, unknown = self.changes(match=match)
465 commit = modified + added
466 commit = modified + added
466 remove = removed
467 remove = removed
467
468
468 p1, p2 = self.dirstate.parents()
469 p1, p2 = self.dirstate.parents()
469 c1 = self.changelog.read(p1)
470 c1 = self.changelog.read(p1)
470 c2 = self.changelog.read(p2)
471 c2 = self.changelog.read(p2)
471 m1 = self.manifest.read(c1[0])
472 m1 = self.manifest.read(c1[0])
472 mf1 = self.manifest.readflags(c1[0])
473 mf1 = self.manifest.readflags(c1[0])
473 m2 = self.manifest.read(c2[0])
474 m2 = self.manifest.read(c2[0])
474
475
475 if not commit and not remove and not force and p2 == nullid:
476 if not commit and not remove and not force and p2 == nullid:
476 self.ui.status(_("nothing changed\n"))
477 self.ui.status(_("nothing changed\n"))
477 return None
478 return None
478
479
479 xp1 = hex(p1)
480 xp1 = hex(p1)
480 if p2 == nullid: xp2 = ''
481 if p2 == nullid: xp2 = ''
481 else: xp2 = hex(p2)
482 else: xp2 = hex(p2)
482
483
483 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
484 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
484
485
485 if not wlock:
486 if not wlock:
486 wlock = self.wlock()
487 wlock = self.wlock()
487 if not lock:
488 if not lock:
488 lock = self.lock()
489 lock = self.lock()
489 tr = self.transaction()
490 tr = self.transaction()
490
491
491 # check in files
492 # check in files
492 new = {}
493 new = {}
493 linkrev = self.changelog.count()
494 linkrev = self.changelog.count()
494 commit.sort()
495 commit.sort()
495 for f in commit:
496 for f in commit:
496 self.ui.note(f + "\n")
497 self.ui.note(f + "\n")
497 try:
498 try:
498 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
499 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
499 t = self.wread(f)
500 t = self.wread(f)
500 except IOError:
501 except IOError:
501 self.ui.warn(_("trouble committing %s!\n") % f)
502 self.ui.warn(_("trouble committing %s!\n") % f)
502 raise
503 raise
503
504
504 r = self.file(f)
505 r = self.file(f)
505
506
506 meta = {}
507 meta = {}
507 cp = self.dirstate.copied(f)
508 cp = self.dirstate.copied(f)
508 if cp:
509 if cp:
509 meta["copy"] = cp
510 meta["copy"] = cp
510 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
511 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
511 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
512 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
512 fp1, fp2 = nullid, nullid
513 fp1, fp2 = nullid, nullid
513 else:
514 else:
514 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
515 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
515 if entry:
516 if entry:
516 new[f] = entry
517 new[f] = entry
517 continue
518 continue
518
519
519 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
520 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
520 # remember what we've added so that we can later calculate
521 # remember what we've added so that we can later calculate
521 # the files to pull from a set of changesets
522 # the files to pull from a set of changesets
522 changed.append(f)
523 changed.append(f)
523
524
524 # update manifest
525 # update manifest
525 m1 = m1.copy()
526 m1 = m1.copy()
526 m1.update(new)
527 m1.update(new)
527 for f in remove:
528 for f in remove:
528 if f in m1:
529 if f in m1:
529 del m1[f]
530 del m1[f]
530 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
531 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
531 (new, remove))
532 (new, remove))
532
533
533 # add changeset
534 # add changeset
534 new = new.keys()
535 new = new.keys()
535 new.sort()
536 new.sort()
536
537
537 user = user or self.ui.username()
538 user = user or self.ui.username()
538 if not text:
539 if not text or force_editor:
539 edittext = [""]
540 edittext = []
541 if text:
542 edittext.append(text)
543 edittext.append("")
540 if p2 != nullid:
544 if p2 != nullid:
541 edittext.append("HG: branch merge")
545 edittext.append("HG: branch merge")
542 edittext.extend(["HG: changed %s" % f for f in changed])
546 edittext.extend(["HG: changed %s" % f for f in changed])
543 edittext.extend(["HG: removed %s" % f for f in remove])
547 edittext.extend(["HG: removed %s" % f for f in remove])
544 if not changed and not remove:
548 if not changed and not remove:
545 edittext.append("HG: no files changed")
549 edittext.append("HG: no files changed")
546 edittext.append("")
550 edittext.append("")
547 # run editor in the repository root
551 # run editor in the repository root
548 olddir = os.getcwd()
552 olddir = os.getcwd()
549 os.chdir(self.root)
553 os.chdir(self.root)
550 edittext = self.ui.edit("\n".join(edittext), user)
554 edittext = self.ui.edit("\n".join(edittext), user)
551 os.chdir(olddir)
555 os.chdir(olddir)
552 if not edittext.rstrip():
556 if not edittext.rstrip():
553 return None
557 return None
554 text = edittext
558 text = edittext
555
559
556 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
560 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
557 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
561 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
558 parent2=xp2)
562 parent2=xp2)
559 tr.close()
563 tr.close()
560
564
561 self.dirstate.setparents(n)
565 self.dirstate.setparents(n)
562 self.dirstate.update(new, "n")
566 self.dirstate.update(new, "n")
563 self.dirstate.forget(remove)
567 self.dirstate.forget(remove)
564
568
565 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
569 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
566 return n
570 return n
567
571
568 def walk(self, node=None, files=[], match=util.always, badmatch=None):
572 def walk(self, node=None, files=[], match=util.always, badmatch=None):
569 if node:
573 if node:
570 fdict = dict.fromkeys(files)
574 fdict = dict.fromkeys(files)
571 for fn in self.manifest.read(self.changelog.read(node)[0]):
575 for fn in self.manifest.read(self.changelog.read(node)[0]):
572 fdict.pop(fn, None)
576 fdict.pop(fn, None)
573 if match(fn):
577 if match(fn):
574 yield 'm', fn
578 yield 'm', fn
575 for fn in fdict:
579 for fn in fdict:
576 if badmatch and badmatch(fn):
580 if badmatch and badmatch(fn):
577 if match(fn):
581 if match(fn):
578 yield 'b', fn
582 yield 'b', fn
579 else:
583 else:
580 self.ui.warn(_('%s: No such file in rev %s\n') % (
584 self.ui.warn(_('%s: No such file in rev %s\n') % (
581 util.pathto(self.getcwd(), fn), short(node)))
585 util.pathto(self.getcwd(), fn), short(node)))
582 else:
586 else:
583 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
587 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
584 yield src, fn
588 yield src, fn
585
589
586 def changes(self, node1=None, node2=None, files=[], match=util.always,
590 def changes(self, node1=None, node2=None, files=[], match=util.always,
587 wlock=None, show_ignored=None):
591 wlock=None, show_ignored=None):
588 """return changes between two nodes or node and working directory
592 """return changes between two nodes or node and working directory
589
593
590 If node1 is None, use the first dirstate parent instead.
594 If node1 is None, use the first dirstate parent instead.
591 If node2 is None, compare node1 with working directory.
595 If node2 is None, compare node1 with working directory.
592 """
596 """
593
597
594 def fcmp(fn, mf):
598 def fcmp(fn, mf):
595 t1 = self.wread(fn)
599 t1 = self.wread(fn)
596 t2 = self.file(fn).read(mf.get(fn, nullid))
600 t2 = self.file(fn).read(mf.get(fn, nullid))
597 return cmp(t1, t2)
601 return cmp(t1, t2)
598
602
599 def mfmatches(node):
603 def mfmatches(node):
600 change = self.changelog.read(node)
604 change = self.changelog.read(node)
601 mf = dict(self.manifest.read(change[0]))
605 mf = dict(self.manifest.read(change[0]))
602 for fn in mf.keys():
606 for fn in mf.keys():
603 if not match(fn):
607 if not match(fn):
604 del mf[fn]
608 del mf[fn]
605 return mf
609 return mf
606
610
607 if node1:
611 if node1:
608 # read the manifest from node1 before the manifest from node2,
612 # read the manifest from node1 before the manifest from node2,
609 # so that we'll hit the manifest cache if we're going through
613 # so that we'll hit the manifest cache if we're going through
610 # all the revisions in parent->child order.
614 # all the revisions in parent->child order.
611 mf1 = mfmatches(node1)
615 mf1 = mfmatches(node1)
612
616
613 # are we comparing the working directory?
617 # are we comparing the working directory?
614 if not node2:
618 if not node2:
615 if not wlock:
619 if not wlock:
616 try:
620 try:
617 wlock = self.wlock(wait=0)
621 wlock = self.wlock(wait=0)
618 except lock.LockException:
622 except lock.LockException:
619 wlock = None
623 wlock = None
620 lookup, modified, added, removed, deleted, unknown, ignored = (
624 lookup, modified, added, removed, deleted, unknown, ignored = (
621 self.dirstate.changes(files, match, show_ignored))
625 self.dirstate.changes(files, match, show_ignored))
622
626
623 # are we comparing working dir against its parent?
627 # are we comparing working dir against its parent?
624 if not node1:
628 if not node1:
625 if lookup:
629 if lookup:
626 # do a full compare of any files that might have changed
630 # do a full compare of any files that might have changed
627 mf2 = mfmatches(self.dirstate.parents()[0])
631 mf2 = mfmatches(self.dirstate.parents()[0])
628 for f in lookup:
632 for f in lookup:
629 if fcmp(f, mf2):
633 if fcmp(f, mf2):
630 modified.append(f)
634 modified.append(f)
631 elif wlock is not None:
635 elif wlock is not None:
632 self.dirstate.update([f], "n")
636 self.dirstate.update([f], "n")
633 else:
637 else:
634 # we are comparing working dir against non-parent
638 # we are comparing working dir against non-parent
635 # generate a pseudo-manifest for the working dir
639 # generate a pseudo-manifest for the working dir
636 mf2 = mfmatches(self.dirstate.parents()[0])
640 mf2 = mfmatches(self.dirstate.parents()[0])
637 for f in lookup + modified + added:
641 for f in lookup + modified + added:
638 mf2[f] = ""
642 mf2[f] = ""
639 for f in removed:
643 for f in removed:
640 if f in mf2:
644 if f in mf2:
641 del mf2[f]
645 del mf2[f]
642 else:
646 else:
643 # we are comparing two revisions
647 # we are comparing two revisions
644 deleted, unknown, ignored = [], [], []
648 deleted, unknown, ignored = [], [], []
645 mf2 = mfmatches(node2)
649 mf2 = mfmatches(node2)
646
650
647 if node1:
651 if node1:
648 # flush lists from dirstate before comparing manifests
652 # flush lists from dirstate before comparing manifests
649 modified, added = [], []
653 modified, added = [], []
650
654
651 for fn in mf2:
655 for fn in mf2:
652 if mf1.has_key(fn):
656 if mf1.has_key(fn):
653 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
657 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
654 modified.append(fn)
658 modified.append(fn)
655 del mf1[fn]
659 del mf1[fn]
656 else:
660 else:
657 added.append(fn)
661 added.append(fn)
658
662
659 removed = mf1.keys()
663 removed = mf1.keys()
660
664
661 # sort and return results:
665 # sort and return results:
662 for l in modified, added, removed, deleted, unknown, ignored:
666 for l in modified, added, removed, deleted, unknown, ignored:
663 l.sort()
667 l.sort()
664 if show_ignored is None:
668 if show_ignored is None:
665 return (modified, added, removed, deleted, unknown)
669 return (modified, added, removed, deleted, unknown)
666 else:
670 else:
667 return (modified, added, removed, deleted, unknown, ignored)
671 return (modified, added, removed, deleted, unknown, ignored)
668
672
669 def add(self, list, wlock=None):
673 def add(self, list, wlock=None):
670 if not wlock:
674 if not wlock:
671 wlock = self.wlock()
675 wlock = self.wlock()
672 for f in list:
676 for f in list:
673 p = self.wjoin(f)
677 p = self.wjoin(f)
674 if not os.path.exists(p):
678 if not os.path.exists(p):
675 self.ui.warn(_("%s does not exist!\n") % f)
679 self.ui.warn(_("%s does not exist!\n") % f)
676 elif not os.path.isfile(p):
680 elif not os.path.isfile(p):
677 self.ui.warn(_("%s not added: only files supported currently\n")
681 self.ui.warn(_("%s not added: only files supported currently\n")
678 % f)
682 % f)
679 elif self.dirstate.state(f) in 'an':
683 elif self.dirstate.state(f) in 'an':
680 self.ui.warn(_("%s already tracked!\n") % f)
684 self.ui.warn(_("%s already tracked!\n") % f)
681 else:
685 else:
682 self.dirstate.update([f], "a")
686 self.dirstate.update([f], "a")
683
687
684 def forget(self, list, wlock=None):
688 def forget(self, list, wlock=None):
685 if not wlock:
689 if not wlock:
686 wlock = self.wlock()
690 wlock = self.wlock()
687 for f in list:
691 for f in list:
688 if self.dirstate.state(f) not in 'ai':
692 if self.dirstate.state(f) not in 'ai':
689 self.ui.warn(_("%s not added!\n") % f)
693 self.ui.warn(_("%s not added!\n") % f)
690 else:
694 else:
691 self.dirstate.forget([f])
695 self.dirstate.forget([f])
692
696
693 def remove(self, list, unlink=False, wlock=None):
697 def remove(self, list, unlink=False, wlock=None):
694 if unlink:
698 if unlink:
695 for f in list:
699 for f in list:
696 try:
700 try:
697 util.unlink(self.wjoin(f))
701 util.unlink(self.wjoin(f))
698 except OSError, inst:
702 except OSError, inst:
699 if inst.errno != errno.ENOENT:
703 if inst.errno != errno.ENOENT:
700 raise
704 raise
701 if not wlock:
705 if not wlock:
702 wlock = self.wlock()
706 wlock = self.wlock()
703 for f in list:
707 for f in list:
704 p = self.wjoin(f)
708 p = self.wjoin(f)
705 if os.path.exists(p):
709 if os.path.exists(p):
706 self.ui.warn(_("%s still exists!\n") % f)
710 self.ui.warn(_("%s still exists!\n") % f)
707 elif self.dirstate.state(f) == 'a':
711 elif self.dirstate.state(f) == 'a':
708 self.dirstate.forget([f])
712 self.dirstate.forget([f])
709 elif f not in self.dirstate:
713 elif f not in self.dirstate:
710 self.ui.warn(_("%s not tracked!\n") % f)
714 self.ui.warn(_("%s not tracked!\n") % f)
711 else:
715 else:
712 self.dirstate.update([f], "r")
716 self.dirstate.update([f], "r")
713
717
714 def undelete(self, list, wlock=None):
718 def undelete(self, list, wlock=None):
715 p = self.dirstate.parents()[0]
719 p = self.dirstate.parents()[0]
716 mn = self.changelog.read(p)[0]
720 mn = self.changelog.read(p)[0]
717 mf = self.manifest.readflags(mn)
721 mf = self.manifest.readflags(mn)
718 m = self.manifest.read(mn)
722 m = self.manifest.read(mn)
719 if not wlock:
723 if not wlock:
720 wlock = self.wlock()
724 wlock = self.wlock()
721 for f in list:
725 for f in list:
722 if self.dirstate.state(f) not in "r":
726 if self.dirstate.state(f) not in "r":
723 self.ui.warn("%s not removed!\n" % f)
727 self.ui.warn("%s not removed!\n" % f)
724 else:
728 else:
725 t = self.file(f).read(m[f])
729 t = self.file(f).read(m[f])
726 self.wwrite(f, t)
730 self.wwrite(f, t)
727 util.set_exec(self.wjoin(f), mf[f])
731 util.set_exec(self.wjoin(f), mf[f])
728 self.dirstate.update([f], "n")
732 self.dirstate.update([f], "n")
729
733
730 def copy(self, source, dest, wlock=None):
734 def copy(self, source, dest, wlock=None):
731 p = self.wjoin(dest)
735 p = self.wjoin(dest)
732 if not os.path.exists(p):
736 if not os.path.exists(p):
733 self.ui.warn(_("%s does not exist!\n") % dest)
737 self.ui.warn(_("%s does not exist!\n") % dest)
734 elif not os.path.isfile(p):
738 elif not os.path.isfile(p):
735 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
739 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
736 else:
740 else:
737 if not wlock:
741 if not wlock:
738 wlock = self.wlock()
742 wlock = self.wlock()
739 if self.dirstate.state(dest) == '?':
743 if self.dirstate.state(dest) == '?':
740 self.dirstate.update([dest], "a")
744 self.dirstate.update([dest], "a")
741 self.dirstate.copy(source, dest)
745 self.dirstate.copy(source, dest)
742
746
743 def heads(self, start=None):
747 def heads(self, start=None):
744 heads = self.changelog.heads(start)
748 heads = self.changelog.heads(start)
745 # sort the output in rev descending order
749 # sort the output in rev descending order
746 heads = [(-self.changelog.rev(h), h) for h in heads]
750 heads = [(-self.changelog.rev(h), h) for h in heads]
747 heads.sort()
751 heads.sort()
748 return [n for (r, n) in heads]
752 return [n for (r, n) in heads]
749
753
750 # branchlookup returns a dict giving a list of branches for
754 # branchlookup returns a dict giving a list of branches for
751 # each head. A branch is defined as the tag of a node or
755 # each head. A branch is defined as the tag of a node or
752 # the branch of the node's parents. If a node has multiple
756 # the branch of the node's parents. If a node has multiple
753 # branch tags, tags are eliminated if they are visible from other
757 # branch tags, tags are eliminated if they are visible from other
754 # branch tags.
758 # branch tags.
755 #
759 #
756 # So, for this graph: a->b->c->d->e
760 # So, for this graph: a->b->c->d->e
757 # \ /
761 # \ /
758 # aa -----/
762 # aa -----/
759 # a has tag 2.6.12
763 # a has tag 2.6.12
760 # d has tag 2.6.13
764 # d has tag 2.6.13
761 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
765 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
762 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
766 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
763 # from the list.
767 # from the list.
764 #
768 #
765 # It is possible that more than one head will have the same branch tag.
769 # It is possible that more than one head will have the same branch tag.
766 # callers need to check the result for multiple heads under the same
770 # callers need to check the result for multiple heads under the same
767 # branch tag if that is a problem for them (ie checkout of a specific
771 # branch tag if that is a problem for them (ie checkout of a specific
768 # branch).
772 # branch).
769 #
773 #
770 # passing in a specific branch will limit the depth of the search
774 # passing in a specific branch will limit the depth of the search
771 # through the parents. It won't limit the branches returned in the
775 # through the parents. It won't limit the branches returned in the
772 # result though.
776 # result though.
773 def branchlookup(self, heads=None, branch=None):
777 def branchlookup(self, heads=None, branch=None):
774 if not heads:
778 if not heads:
775 heads = self.heads()
779 heads = self.heads()
776 headt = [ h for h in heads ]
780 headt = [ h for h in heads ]
777 chlog = self.changelog
781 chlog = self.changelog
778 branches = {}
782 branches = {}
779 merges = []
783 merges = []
780 seenmerge = {}
784 seenmerge = {}
781
785
782 # traverse the tree once for each head, recording in the branches
786 # traverse the tree once for each head, recording in the branches
783 # dict which tags are visible from this head. The branches
787 # dict which tags are visible from this head. The branches
784 # dict also records which tags are visible from each tag
788 # dict also records which tags are visible from each tag
785 # while we traverse.
789 # while we traverse.
786 while headt or merges:
790 while headt or merges:
787 if merges:
791 if merges:
788 n, found = merges.pop()
792 n, found = merges.pop()
789 visit = [n]
793 visit = [n]
790 else:
794 else:
791 h = headt.pop()
795 h = headt.pop()
792 visit = [h]
796 visit = [h]
793 found = [h]
797 found = [h]
794 seen = {}
798 seen = {}
795 while visit:
799 while visit:
796 n = visit.pop()
800 n = visit.pop()
797 if n in seen:
801 if n in seen:
798 continue
802 continue
799 pp = chlog.parents(n)
803 pp = chlog.parents(n)
800 tags = self.nodetags(n)
804 tags = self.nodetags(n)
801 if tags:
805 if tags:
802 for x in tags:
806 for x in tags:
803 if x == 'tip':
807 if x == 'tip':
804 continue
808 continue
805 for f in found:
809 for f in found:
806 branches.setdefault(f, {})[n] = 1
810 branches.setdefault(f, {})[n] = 1
807 branches.setdefault(n, {})[n] = 1
811 branches.setdefault(n, {})[n] = 1
808 break
812 break
809 if n not in found:
813 if n not in found:
810 found.append(n)
814 found.append(n)
811 if branch in tags:
815 if branch in tags:
812 continue
816 continue
813 seen[n] = 1
817 seen[n] = 1
814 if pp[1] != nullid and n not in seenmerge:
818 if pp[1] != nullid and n not in seenmerge:
815 merges.append((pp[1], [x for x in found]))
819 merges.append((pp[1], [x for x in found]))
816 seenmerge[n] = 1
820 seenmerge[n] = 1
817 if pp[0] != nullid:
821 if pp[0] != nullid:
818 visit.append(pp[0])
822 visit.append(pp[0])
819 # traverse the branches dict, eliminating branch tags from each
823 # traverse the branches dict, eliminating branch tags from each
820 # head that are visible from another branch tag for that head.
824 # head that are visible from another branch tag for that head.
821 out = {}
825 out = {}
822 viscache = {}
826 viscache = {}
823 for h in heads:
827 for h in heads:
824 def visible(node):
828 def visible(node):
825 if node in viscache:
829 if node in viscache:
826 return viscache[node]
830 return viscache[node]
827 ret = {}
831 ret = {}
828 visit = [node]
832 visit = [node]
829 while visit:
833 while visit:
830 x = visit.pop()
834 x = visit.pop()
831 if x in viscache:
835 if x in viscache:
832 ret.update(viscache[x])
836 ret.update(viscache[x])
833 elif x not in ret:
837 elif x not in ret:
834 ret[x] = 1
838 ret[x] = 1
835 if x in branches:
839 if x in branches:
836 visit[len(visit):] = branches[x].keys()
840 visit[len(visit):] = branches[x].keys()
837 viscache[node] = ret
841 viscache[node] = ret
838 return ret
842 return ret
839 if h not in branches:
843 if h not in branches:
840 continue
844 continue
841 # O(n^2), but somewhat limited. This only searches the
845 # O(n^2), but somewhat limited. This only searches the
842 # tags visible from a specific head, not all the tags in the
846 # tags visible from a specific head, not all the tags in the
843 # whole repo.
847 # whole repo.
844 for b in branches[h]:
848 for b in branches[h]:
845 vis = False
849 vis = False
846 for bb in branches[h].keys():
850 for bb in branches[h].keys():
847 if b != bb:
851 if b != bb:
848 if b in visible(bb):
852 if b in visible(bb):
849 vis = True
853 vis = True
850 break
854 break
851 if not vis:
855 if not vis:
852 l = out.setdefault(h, [])
856 l = out.setdefault(h, [])
853 l[len(l):] = self.nodetags(b)
857 l[len(l):] = self.nodetags(b)
854 return out
858 return out
855
859
856 def branches(self, nodes):
860 def branches(self, nodes):
857 if not nodes:
861 if not nodes:
858 nodes = [self.changelog.tip()]
862 nodes = [self.changelog.tip()]
859 b = []
863 b = []
860 for n in nodes:
864 for n in nodes:
861 t = n
865 t = n
862 while n:
866 while n:
863 p = self.changelog.parents(n)
867 p = self.changelog.parents(n)
864 if p[1] != nullid or p[0] == nullid:
868 if p[1] != nullid or p[0] == nullid:
865 b.append((t, n, p[0], p[1]))
869 b.append((t, n, p[0], p[1]))
866 break
870 break
867 n = p[0]
871 n = p[0]
868 return b
872 return b
869
873
870 def between(self, pairs):
874 def between(self, pairs):
871 r = []
875 r = []
872
876
873 for top, bottom in pairs:
877 for top, bottom in pairs:
874 n, l, i = top, [], 0
878 n, l, i = top, [], 0
875 f = 1
879 f = 1
876
880
877 while n != bottom:
881 while n != bottom:
878 p = self.changelog.parents(n)[0]
882 p = self.changelog.parents(n)[0]
879 if i == f:
883 if i == f:
880 l.append(n)
884 l.append(n)
881 f = f * 2
885 f = f * 2
882 n = p
886 n = p
883 i += 1
887 i += 1
884
888
885 r.append(l)
889 r.append(l)
886
890
887 return r
891 return r
888
892
889 def findincoming(self, remote, base=None, heads=None, force=False):
893 def findincoming(self, remote, base=None, heads=None, force=False):
890 m = self.changelog.nodemap
894 m = self.changelog.nodemap
891 search = []
895 search = []
892 fetch = {}
896 fetch = {}
893 seen = {}
897 seen = {}
894 seenbranch = {}
898 seenbranch = {}
895 if base == None:
899 if base == None:
896 base = {}
900 base = {}
897
901
898 if not heads:
902 if not heads:
899 heads = remote.heads()
903 heads = remote.heads()
900
904
901 if self.changelog.tip() == nullid:
905 if self.changelog.tip() == nullid:
902 if heads != [nullid]:
906 if heads != [nullid]:
903 return [nullid]
907 return [nullid]
904 return []
908 return []
905
909
906 # assume we're closer to the tip than the root
910 # assume we're closer to the tip than the root
907 # and start by examining the heads
911 # and start by examining the heads
908 self.ui.status(_("searching for changes\n"))
912 self.ui.status(_("searching for changes\n"))
909
913
910 unknown = []
914 unknown = []
911 for h in heads:
915 for h in heads:
912 if h not in m:
916 if h not in m:
913 unknown.append(h)
917 unknown.append(h)
914 else:
918 else:
915 base[h] = 1
919 base[h] = 1
916
920
917 if not unknown:
921 if not unknown:
918 return []
922 return []
919
923
920 rep = {}
924 rep = {}
921 reqcnt = 0
925 reqcnt = 0
922
926
923 # search through remote branches
927 # search through remote branches
924 # a 'branch' here is a linear segment of history, with four parts:
928 # a 'branch' here is a linear segment of history, with four parts:
925 # head, root, first parent, second parent
929 # head, root, first parent, second parent
926 # (a branch always has two parents (or none) by definition)
930 # (a branch always has two parents (or none) by definition)
927 unknown = remote.branches(unknown)
931 unknown = remote.branches(unknown)
928 while unknown:
932 while unknown:
929 r = []
933 r = []
930 while unknown:
934 while unknown:
931 n = unknown.pop(0)
935 n = unknown.pop(0)
932 if n[0] in seen:
936 if n[0] in seen:
933 continue
937 continue
934
938
935 self.ui.debug(_("examining %s:%s\n")
939 self.ui.debug(_("examining %s:%s\n")
936 % (short(n[0]), short(n[1])))
940 % (short(n[0]), short(n[1])))
937 if n[0] == nullid:
941 if n[0] == nullid:
938 break
942 break
939 if n in seenbranch:
943 if n in seenbranch:
940 self.ui.debug(_("branch already found\n"))
944 self.ui.debug(_("branch already found\n"))
941 continue
945 continue
942 if n[1] and n[1] in m: # do we know the base?
946 if n[1] and n[1] in m: # do we know the base?
943 self.ui.debug(_("found incomplete branch %s:%s\n")
947 self.ui.debug(_("found incomplete branch %s:%s\n")
944 % (short(n[0]), short(n[1])))
948 % (short(n[0]), short(n[1])))
945 search.append(n) # schedule branch range for scanning
949 search.append(n) # schedule branch range for scanning
946 seenbranch[n] = 1
950 seenbranch[n] = 1
947 else:
951 else:
948 if n[1] not in seen and n[1] not in fetch:
952 if n[1] not in seen and n[1] not in fetch:
949 if n[2] in m and n[3] in m:
953 if n[2] in m and n[3] in m:
950 self.ui.debug(_("found new changeset %s\n") %
954 self.ui.debug(_("found new changeset %s\n") %
951 short(n[1]))
955 short(n[1]))
952 fetch[n[1]] = 1 # earliest unknown
956 fetch[n[1]] = 1 # earliest unknown
953 base[n[2]] = 1 # latest known
957 base[n[2]] = 1 # latest known
954 continue
958 continue
955
959
956 for a in n[2:4]:
960 for a in n[2:4]:
957 if a not in rep:
961 if a not in rep:
958 r.append(a)
962 r.append(a)
959 rep[a] = 1
963 rep[a] = 1
960
964
961 seen[n[0]] = 1
965 seen[n[0]] = 1
962
966
963 if r:
967 if r:
964 reqcnt += 1
968 reqcnt += 1
965 self.ui.debug(_("request %d: %s\n") %
969 self.ui.debug(_("request %d: %s\n") %
966 (reqcnt, " ".join(map(short, r))))
970 (reqcnt, " ".join(map(short, r))))
967 for p in range(0, len(r), 10):
971 for p in range(0, len(r), 10):
968 for b in remote.branches(r[p:p+10]):
972 for b in remote.branches(r[p:p+10]):
969 self.ui.debug(_("received %s:%s\n") %
973 self.ui.debug(_("received %s:%s\n") %
970 (short(b[0]), short(b[1])))
974 (short(b[0]), short(b[1])))
971 if b[0] in m:
975 if b[0] in m:
972 self.ui.debug(_("found base node %s\n")
976 self.ui.debug(_("found base node %s\n")
973 % short(b[0]))
977 % short(b[0]))
974 base[b[0]] = 1
978 base[b[0]] = 1
975 elif b[0] not in seen:
979 elif b[0] not in seen:
976 unknown.append(b)
980 unknown.append(b)
977
981
978 # do binary search on the branches we found
982 # do binary search on the branches we found
979 while search:
983 while search:
980 n = search.pop(0)
984 n = search.pop(0)
981 reqcnt += 1
985 reqcnt += 1
982 l = remote.between([(n[0], n[1])])[0]
986 l = remote.between([(n[0], n[1])])[0]
983 l.append(n[1])
987 l.append(n[1])
984 p = n[0]
988 p = n[0]
985 f = 1
989 f = 1
986 for i in l:
990 for i in l:
987 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
991 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
988 if i in m:
992 if i in m:
989 if f <= 2:
993 if f <= 2:
990 self.ui.debug(_("found new branch changeset %s\n") %
994 self.ui.debug(_("found new branch changeset %s\n") %
991 short(p))
995 short(p))
992 fetch[p] = 1
996 fetch[p] = 1
993 base[i] = 1
997 base[i] = 1
994 else:
998 else:
995 self.ui.debug(_("narrowed branch search to %s:%s\n")
999 self.ui.debug(_("narrowed branch search to %s:%s\n")
996 % (short(p), short(i)))
1000 % (short(p), short(i)))
997 search.append((p, i))
1001 search.append((p, i))
998 break
1002 break
999 p, f = i, f * 2
1003 p, f = i, f * 2
1000
1004
1001 # sanity check our fetch list
1005 # sanity check our fetch list
1002 for f in fetch.keys():
1006 for f in fetch.keys():
1003 if f in m:
1007 if f in m:
1004 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1008 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1005
1009
1006 if base.keys() == [nullid]:
1010 if base.keys() == [nullid]:
1007 if force:
1011 if force:
1008 self.ui.warn(_("warning: repository is unrelated\n"))
1012 self.ui.warn(_("warning: repository is unrelated\n"))
1009 else:
1013 else:
1010 raise util.Abort(_("repository is unrelated"))
1014 raise util.Abort(_("repository is unrelated"))
1011
1015
1012 self.ui.note(_("found new changesets starting at ") +
1016 self.ui.note(_("found new changesets starting at ") +
1013 " ".join([short(f) for f in fetch]) + "\n")
1017 " ".join([short(f) for f in fetch]) + "\n")
1014
1018
1015 self.ui.debug(_("%d total queries\n") % reqcnt)
1019 self.ui.debug(_("%d total queries\n") % reqcnt)
1016
1020
1017 return fetch.keys()
1021 return fetch.keys()
1018
1022
1019 def findoutgoing(self, remote, base=None, heads=None, force=False):
1023 def findoutgoing(self, remote, base=None, heads=None, force=False):
1020 """Return list of nodes that are roots of subsets not in remote
1024 """Return list of nodes that are roots of subsets not in remote
1021
1025
1022 If base dict is specified, assume that these nodes and their parents
1026 If base dict is specified, assume that these nodes and their parents
1023 exist on the remote side.
1027 exist on the remote side.
1024 If a list of heads is specified, return only nodes which are heads
1028 If a list of heads is specified, return only nodes which are heads
1025 or ancestors of these heads, and return a second element which
1029 or ancestors of these heads, and return a second element which
1026 contains all remote heads which get new children.
1030 contains all remote heads which get new children.
1027 """
1031 """
1028 if base == None:
1032 if base == None:
1029 base = {}
1033 base = {}
1030 self.findincoming(remote, base, heads, force=force)
1034 self.findincoming(remote, base, heads, force=force)
1031
1035
1032 self.ui.debug(_("common changesets up to ")
1036 self.ui.debug(_("common changesets up to ")
1033 + " ".join(map(short, base.keys())) + "\n")
1037 + " ".join(map(short, base.keys())) + "\n")
1034
1038
1035 remain = dict.fromkeys(self.changelog.nodemap)
1039 remain = dict.fromkeys(self.changelog.nodemap)
1036
1040
1037 # prune everything remote has from the tree
1041 # prune everything remote has from the tree
1038 del remain[nullid]
1042 del remain[nullid]
1039 remove = base.keys()
1043 remove = base.keys()
1040 while remove:
1044 while remove:
1041 n = remove.pop(0)
1045 n = remove.pop(0)
1042 if n in remain:
1046 if n in remain:
1043 del remain[n]
1047 del remain[n]
1044 for p in self.changelog.parents(n):
1048 for p in self.changelog.parents(n):
1045 remove.append(p)
1049 remove.append(p)
1046
1050
1047 # find every node whose parents have been pruned
1051 # find every node whose parents have been pruned
1048 subset = []
1052 subset = []
1049 # find every remote head that will get new children
1053 # find every remote head that will get new children
1050 updated_heads = {}
1054 updated_heads = {}
1051 for n in remain:
1055 for n in remain:
1052 p1, p2 = self.changelog.parents(n)
1056 p1, p2 = self.changelog.parents(n)
1053 if p1 not in remain and p2 not in remain:
1057 if p1 not in remain and p2 not in remain:
1054 subset.append(n)
1058 subset.append(n)
1055 if heads:
1059 if heads:
1056 if p1 in heads:
1060 if p1 in heads:
1057 updated_heads[p1] = True
1061 updated_heads[p1] = True
1058 if p2 in heads:
1062 if p2 in heads:
1059 updated_heads[p2] = True
1063 updated_heads[p2] = True
1060
1064
1061 # this is the set of all roots we have to push
1065 # this is the set of all roots we have to push
1062 if heads:
1066 if heads:
1063 return subset, updated_heads.keys()
1067 return subset, updated_heads.keys()
1064 else:
1068 else:
1065 return subset
1069 return subset
1066
1070
1067 def pull(self, remote, heads=None, force=False):
1071 def pull(self, remote, heads=None, force=False):
1068 l = self.lock()
1072 l = self.lock()
1069
1073
1070 fetch = self.findincoming(remote, force=force)
1074 fetch = self.findincoming(remote, force=force)
1071 if fetch == [nullid]:
1075 if fetch == [nullid]:
1072 self.ui.status(_("requesting all changes\n"))
1076 self.ui.status(_("requesting all changes\n"))
1073
1077
1074 if not fetch:
1078 if not fetch:
1075 self.ui.status(_("no changes found\n"))
1079 self.ui.status(_("no changes found\n"))
1076 return 0
1080 return 0
1077
1081
1078 if heads is None:
1082 if heads is None:
1079 cg = remote.changegroup(fetch, 'pull')
1083 cg = remote.changegroup(fetch, 'pull')
1080 else:
1084 else:
1081 cg = remote.changegroupsubset(fetch, heads, 'pull')
1085 cg = remote.changegroupsubset(fetch, heads, 'pull')
1082 return self.addchangegroup(cg, 'pull')
1086 return self.addchangegroup(cg, 'pull')
1083
1087
1084 def push(self, remote, force=False, revs=None):
1088 def push(self, remote, force=False, revs=None):
1085 lock = remote.lock()
1089 lock = remote.lock()
1086
1090
1087 base = {}
1091 base = {}
1088 remote_heads = remote.heads()
1092 remote_heads = remote.heads()
1089 inc = self.findincoming(remote, base, remote_heads, force=force)
1093 inc = self.findincoming(remote, base, remote_heads, force=force)
1090 if not force and inc:
1094 if not force and inc:
1091 self.ui.warn(_("abort: unsynced remote changes!\n"))
1095 self.ui.warn(_("abort: unsynced remote changes!\n"))
1092 self.ui.status(_("(did you forget to sync?"
1096 self.ui.status(_("(did you forget to sync?"
1093 " use push -f to force)\n"))
1097 " use push -f to force)\n"))
1094 return 1
1098 return 1
1095
1099
1096 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1100 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1097 if revs is not None:
1101 if revs is not None:
1098 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1102 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1099 else:
1103 else:
1100 bases, heads = update, self.changelog.heads()
1104 bases, heads = update, self.changelog.heads()
1101
1105
1102 if not bases:
1106 if not bases:
1103 self.ui.status(_("no changes found\n"))
1107 self.ui.status(_("no changes found\n"))
1104 return 1
1108 return 1
1105 elif not force:
1109 elif not force:
1106 # FIXME we don't properly detect creation of new heads
1110 # FIXME we don't properly detect creation of new heads
1107 # in the push -r case, assume the user knows what he's doing
1111 # in the push -r case, assume the user knows what he's doing
1108 if not revs and len(remote_heads) < len(heads) \
1112 if not revs and len(remote_heads) < len(heads) \
1109 and remote_heads != [nullid]:
1113 and remote_heads != [nullid]:
1110 self.ui.warn(_("abort: push creates new remote branches!\n"))
1114 self.ui.warn(_("abort: push creates new remote branches!\n"))
1111 self.ui.status(_("(did you forget to merge?"
1115 self.ui.status(_("(did you forget to merge?"
1112 " use push -f to force)\n"))
1116 " use push -f to force)\n"))
1113 return 1
1117 return 1
1114
1118
1115 if revs is None:
1119 if revs is None:
1116 cg = self.changegroup(update, 'push')
1120 cg = self.changegroup(update, 'push')
1117 else:
1121 else:
1118 cg = self.changegroupsubset(update, revs, 'push')
1122 cg = self.changegroupsubset(update, revs, 'push')
1119 return remote.addchangegroup(cg, 'push')
1123 return remote.addchangegroup(cg, 'push')
1120
1124
1121 def changegroupsubset(self, bases, heads, source):
1125 def changegroupsubset(self, bases, heads, source):
1122 """This function generates a changegroup consisting of all the nodes
1126 """This function generates a changegroup consisting of all the nodes
1123 that are descendents of any of the bases, and ancestors of any of
1127 that are descendents of any of the bases, and ancestors of any of
1124 the heads.
1128 the heads.
1125
1129
1126 It is fairly complex as determining which filenodes and which
1130 It is fairly complex as determining which filenodes and which
1127 manifest nodes need to be included for the changeset to be complete
1131 manifest nodes need to be included for the changeset to be complete
1128 is non-trivial.
1132 is non-trivial.
1129
1133
1130 Another wrinkle is doing the reverse, figuring out which changeset in
1134 Another wrinkle is doing the reverse, figuring out which changeset in
1131 the changegroup a particular filenode or manifestnode belongs to."""
1135 the changegroup a particular filenode or manifestnode belongs to."""
1132
1136
1133 self.hook('preoutgoing', throw=True, source=source)
1137 self.hook('preoutgoing', throw=True, source=source)
1134
1138
1135 # Set up some initial variables
1139 # Set up some initial variables
1136 # Make it easy to refer to self.changelog
1140 # Make it easy to refer to self.changelog
1137 cl = self.changelog
1141 cl = self.changelog
1138 # msng is short for missing - compute the list of changesets in this
1142 # msng is short for missing - compute the list of changesets in this
1139 # changegroup.
1143 # changegroup.
1140 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1144 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1141 # Some bases may turn out to be superfluous, and some heads may be
1145 # Some bases may turn out to be superfluous, and some heads may be
1142 # too. nodesbetween will return the minimal set of bases and heads
1146 # too. nodesbetween will return the minimal set of bases and heads
1143 # necessary to re-create the changegroup.
1147 # necessary to re-create the changegroup.
1144
1148
1145 # Known heads are the list of heads that it is assumed the recipient
1149 # Known heads are the list of heads that it is assumed the recipient
1146 # of this changegroup will know about.
1150 # of this changegroup will know about.
1147 knownheads = {}
1151 knownheads = {}
1148 # We assume that all parents of bases are known heads.
1152 # We assume that all parents of bases are known heads.
1149 for n in bases:
1153 for n in bases:
1150 for p in cl.parents(n):
1154 for p in cl.parents(n):
1151 if p != nullid:
1155 if p != nullid:
1152 knownheads[p] = 1
1156 knownheads[p] = 1
1153 knownheads = knownheads.keys()
1157 knownheads = knownheads.keys()
1154 if knownheads:
1158 if knownheads:
1155 # Now that we know what heads are known, we can compute which
1159 # Now that we know what heads are known, we can compute which
1156 # changesets are known. The recipient must know about all
1160 # changesets are known. The recipient must know about all
1157 # changesets required to reach the known heads from the null
1161 # changesets required to reach the known heads from the null
1158 # changeset.
1162 # changeset.
1159 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1163 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1160 junk = None
1164 junk = None
1161 # Transform the list into an ersatz set.
1165 # Transform the list into an ersatz set.
1162 has_cl_set = dict.fromkeys(has_cl_set)
1166 has_cl_set = dict.fromkeys(has_cl_set)
1163 else:
1167 else:
1164 # If there were no known heads, the recipient cannot be assumed to
1168 # If there were no known heads, the recipient cannot be assumed to
1165 # know about any changesets.
1169 # know about any changesets.
1166 has_cl_set = {}
1170 has_cl_set = {}
1167
1171
1168 # Make it easy to refer to self.manifest
1172 # Make it easy to refer to self.manifest
1169 mnfst = self.manifest
1173 mnfst = self.manifest
1170 # We don't know which manifests are missing yet
1174 # We don't know which manifests are missing yet
1171 msng_mnfst_set = {}
1175 msng_mnfst_set = {}
1172 # Nor do we know which filenodes are missing.
1176 # Nor do we know which filenodes are missing.
1173 msng_filenode_set = {}
1177 msng_filenode_set = {}
1174
1178
1175 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1179 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1176 junk = None
1180 junk = None
1177
1181
1178 # A changeset always belongs to itself, so the changenode lookup
1182 # A changeset always belongs to itself, so the changenode lookup
1179 # function for a changenode is identity.
1183 # function for a changenode is identity.
1180 def identity(x):
1184 def identity(x):
1181 return x
1185 return x
1182
1186
1183 # A function generating function. Sets up an environment for the
1187 # A function generating function. Sets up an environment for the
1184 # inner function.
1188 # inner function.
1185 def cmp_by_rev_func(revlog):
1189 def cmp_by_rev_func(revlog):
1186 # Compare two nodes by their revision number in the environment's
1190 # Compare two nodes by their revision number in the environment's
1187 # revision history. Since the revision number both represents the
1191 # revision history. Since the revision number both represents the
1188 # most efficient order to read the nodes in, and represents a
1192 # most efficient order to read the nodes in, and represents a
1189 # topological sorting of the nodes, this function is often useful.
1193 # topological sorting of the nodes, this function is often useful.
1190 def cmp_by_rev(a, b):
1194 def cmp_by_rev(a, b):
1191 return cmp(revlog.rev(a), revlog.rev(b))
1195 return cmp(revlog.rev(a), revlog.rev(b))
1192 return cmp_by_rev
1196 return cmp_by_rev
1193
1197
1194 # If we determine that a particular file or manifest node must be a
1198 # If we determine that a particular file or manifest node must be a
1195 # node that the recipient of the changegroup will already have, we can
1199 # node that the recipient of the changegroup will already have, we can
1196 # also assume the recipient will have all the parents. This function
1200 # also assume the recipient will have all the parents. This function
1197 # prunes them from the set of missing nodes.
1201 # prunes them from the set of missing nodes.
1198 def prune_parents(revlog, hasset, msngset):
1202 def prune_parents(revlog, hasset, msngset):
1199 haslst = hasset.keys()
1203 haslst = hasset.keys()
1200 haslst.sort(cmp_by_rev_func(revlog))
1204 haslst.sort(cmp_by_rev_func(revlog))
1201 for node in haslst:
1205 for node in haslst:
1202 parentlst = [p for p in revlog.parents(node) if p != nullid]
1206 parentlst = [p for p in revlog.parents(node) if p != nullid]
1203 while parentlst:
1207 while parentlst:
1204 n = parentlst.pop()
1208 n = parentlst.pop()
1205 if n not in hasset:
1209 if n not in hasset:
1206 hasset[n] = 1
1210 hasset[n] = 1
1207 p = [p for p in revlog.parents(n) if p != nullid]
1211 p = [p for p in revlog.parents(n) if p != nullid]
1208 parentlst.extend(p)
1212 parentlst.extend(p)
1209 for n in hasset:
1213 for n in hasset:
1210 msngset.pop(n, None)
1214 msngset.pop(n, None)
1211
1215
1212 # This is a function generating function used to set up an environment
1216 # This is a function generating function used to set up an environment
1213 # for the inner function to execute in.
1217 # for the inner function to execute in.
1214 def manifest_and_file_collector(changedfileset):
1218 def manifest_and_file_collector(changedfileset):
1215 # This is an information gathering function that gathers
1219 # This is an information gathering function that gathers
1216 # information from each changeset node that goes out as part of
1220 # information from each changeset node that goes out as part of
1217 # the changegroup. The information gathered is a list of which
1221 # the changegroup. The information gathered is a list of which
1218 # manifest nodes are potentially required (the recipient may
1222 # manifest nodes are potentially required (the recipient may
1219 # already have them) and total list of all files which were
1223 # already have them) and total list of all files which were
1220 # changed in any changeset in the changegroup.
1224 # changed in any changeset in the changegroup.
1221 #
1225 #
1222 # We also remember the first changenode we saw any manifest
1226 # We also remember the first changenode we saw any manifest
1223 # referenced by so we can later determine which changenode 'owns'
1227 # referenced by so we can later determine which changenode 'owns'
1224 # the manifest.
1228 # the manifest.
1225 def collect_manifests_and_files(clnode):
1229 def collect_manifests_and_files(clnode):
1226 c = cl.read(clnode)
1230 c = cl.read(clnode)
1227 for f in c[3]:
1231 for f in c[3]:
1228 # This is to make sure we only have one instance of each
1232 # This is to make sure we only have one instance of each
1229 # filename string for each filename.
1233 # filename string for each filename.
1230 changedfileset.setdefault(f, f)
1234 changedfileset.setdefault(f, f)
1231 msng_mnfst_set.setdefault(c[0], clnode)
1235 msng_mnfst_set.setdefault(c[0], clnode)
1232 return collect_manifests_and_files
1236 return collect_manifests_and_files
1233
1237
1234 # Figure out which manifest nodes (of the ones we think might be part
1238 # Figure out which manifest nodes (of the ones we think might be part
1235 # of the changegroup) the recipient must know about and remove them
1239 # of the changegroup) the recipient must know about and remove them
1236 # from the changegroup.
1240 # from the changegroup.
1237 def prune_manifests():
1241 def prune_manifests():
1238 has_mnfst_set = {}
1242 has_mnfst_set = {}
1239 for n in msng_mnfst_set:
1243 for n in msng_mnfst_set:
1240 # If a 'missing' manifest thinks it belongs to a changenode
1244 # If a 'missing' manifest thinks it belongs to a changenode
1241 # the recipient is assumed to have, obviously the recipient
1245 # the recipient is assumed to have, obviously the recipient
1242 # must have that manifest.
1246 # must have that manifest.
1243 linknode = cl.node(mnfst.linkrev(n))
1247 linknode = cl.node(mnfst.linkrev(n))
1244 if linknode in has_cl_set:
1248 if linknode in has_cl_set:
1245 has_mnfst_set[n] = 1
1249 has_mnfst_set[n] = 1
1246 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1250 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1247
1251
1248 # Use the information collected in collect_manifests_and_files to say
1252 # Use the information collected in collect_manifests_and_files to say
1249 # which changenode any manifestnode belongs to.
1253 # which changenode any manifestnode belongs to.
1250 def lookup_manifest_link(mnfstnode):
1254 def lookup_manifest_link(mnfstnode):
1251 return msng_mnfst_set[mnfstnode]
1255 return msng_mnfst_set[mnfstnode]
1252
1256
1253 # A function generating function that sets up the initial environment
1257 # A function generating function that sets up the initial environment
1254 # the inner function.
1258 # the inner function.
1255 def filenode_collector(changedfiles):
1259 def filenode_collector(changedfiles):
1256 next_rev = [0]
1260 next_rev = [0]
1257 # This gathers information from each manifestnode included in the
1261 # This gathers information from each manifestnode included in the
1258 # changegroup about which filenodes the manifest node references
1262 # changegroup about which filenodes the manifest node references
1259 # so we can include those in the changegroup too.
1263 # so we can include those in the changegroup too.
1260 #
1264 #
1261 # It also remembers which changenode each filenode belongs to. It
1265 # It also remembers which changenode each filenode belongs to. It
1262 # does this by assuming the a filenode belongs to the changenode
1266 # does this by assuming the a filenode belongs to the changenode
1263 # the first manifest that references it belongs to.
1267 # the first manifest that references it belongs to.
1264 def collect_msng_filenodes(mnfstnode):
1268 def collect_msng_filenodes(mnfstnode):
1265 r = mnfst.rev(mnfstnode)
1269 r = mnfst.rev(mnfstnode)
1266 if r == next_rev[0]:
1270 if r == next_rev[0]:
1267 # If the last rev we looked at was the one just previous,
1271 # If the last rev we looked at was the one just previous,
1268 # we only need to see a diff.
1272 # we only need to see a diff.
1269 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1273 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1270 # For each line in the delta
1274 # For each line in the delta
1271 for dline in delta.splitlines():
1275 for dline in delta.splitlines():
1272 # get the filename and filenode for that line
1276 # get the filename and filenode for that line
1273 f, fnode = dline.split('\0')
1277 f, fnode = dline.split('\0')
1274 fnode = bin(fnode[:40])
1278 fnode = bin(fnode[:40])
1275 f = changedfiles.get(f, None)
1279 f = changedfiles.get(f, None)
1276 # And if the file is in the list of files we care
1280 # And if the file is in the list of files we care
1277 # about.
1281 # about.
1278 if f is not None:
1282 if f is not None:
1279 # Get the changenode this manifest belongs to
1283 # Get the changenode this manifest belongs to
1280 clnode = msng_mnfst_set[mnfstnode]
1284 clnode = msng_mnfst_set[mnfstnode]
1281 # Create the set of filenodes for the file if
1285 # Create the set of filenodes for the file if
1282 # there isn't one already.
1286 # there isn't one already.
1283 ndset = msng_filenode_set.setdefault(f, {})
1287 ndset = msng_filenode_set.setdefault(f, {})
1284 # And set the filenode's changelog node to the
1288 # And set the filenode's changelog node to the
1285 # manifest's if it hasn't been set already.
1289 # manifest's if it hasn't been set already.
1286 ndset.setdefault(fnode, clnode)
1290 ndset.setdefault(fnode, clnode)
1287 else:
1291 else:
1288 # Otherwise we need a full manifest.
1292 # Otherwise we need a full manifest.
1289 m = mnfst.read(mnfstnode)
1293 m = mnfst.read(mnfstnode)
1290 # For every file in we care about.
1294 # For every file in we care about.
1291 for f in changedfiles:
1295 for f in changedfiles:
1292 fnode = m.get(f, None)
1296 fnode = m.get(f, None)
1293 # If it's in the manifest
1297 # If it's in the manifest
1294 if fnode is not None:
1298 if fnode is not None:
1295 # See comments above.
1299 # See comments above.
1296 clnode = msng_mnfst_set[mnfstnode]
1300 clnode = msng_mnfst_set[mnfstnode]
1297 ndset = msng_filenode_set.setdefault(f, {})
1301 ndset = msng_filenode_set.setdefault(f, {})
1298 ndset.setdefault(fnode, clnode)
1302 ndset.setdefault(fnode, clnode)
1299 # Remember the revision we hope to see next.
1303 # Remember the revision we hope to see next.
1300 next_rev[0] = r + 1
1304 next_rev[0] = r + 1
1301 return collect_msng_filenodes
1305 return collect_msng_filenodes
1302
1306
1303 # We have a list of filenodes we think we need for a file, lets remove
1307 # We have a list of filenodes we think we need for a file, lets remove
1304 # all those we now the recipient must have.
1308 # all those we now the recipient must have.
1305 def prune_filenodes(f, filerevlog):
1309 def prune_filenodes(f, filerevlog):
1306 msngset = msng_filenode_set[f]
1310 msngset = msng_filenode_set[f]
1307 hasset = {}
1311 hasset = {}
1308 # If a 'missing' filenode thinks it belongs to a changenode we
1312 # If a 'missing' filenode thinks it belongs to a changenode we
1309 # assume the recipient must have, then the recipient must have
1313 # assume the recipient must have, then the recipient must have
1310 # that filenode.
1314 # that filenode.
1311 for n in msngset:
1315 for n in msngset:
1312 clnode = cl.node(filerevlog.linkrev(n))
1316 clnode = cl.node(filerevlog.linkrev(n))
1313 if clnode in has_cl_set:
1317 if clnode in has_cl_set:
1314 hasset[n] = 1
1318 hasset[n] = 1
1315 prune_parents(filerevlog, hasset, msngset)
1319 prune_parents(filerevlog, hasset, msngset)
1316
1320
1317 # A function generator function that sets up the a context for the
1321 # A function generator function that sets up the a context for the
1318 # inner function.
1322 # inner function.
1319 def lookup_filenode_link_func(fname):
1323 def lookup_filenode_link_func(fname):
1320 msngset = msng_filenode_set[fname]
1324 msngset = msng_filenode_set[fname]
1321 # Lookup the changenode the filenode belongs to.
1325 # Lookup the changenode the filenode belongs to.
1322 def lookup_filenode_link(fnode):
1326 def lookup_filenode_link(fnode):
1323 return msngset[fnode]
1327 return msngset[fnode]
1324 return lookup_filenode_link
1328 return lookup_filenode_link
1325
1329
1326 # Now that we have all theses utility functions to help out and
1330 # Now that we have all theses utility functions to help out and
1327 # logically divide up the task, generate the group.
1331 # logically divide up the task, generate the group.
1328 def gengroup():
1332 def gengroup():
1329 # The set of changed files starts empty.
1333 # The set of changed files starts empty.
1330 changedfiles = {}
1334 changedfiles = {}
1331 # Create a changenode group generator that will call our functions
1335 # Create a changenode group generator that will call our functions
1332 # back to lookup the owning changenode and collect information.
1336 # back to lookup the owning changenode and collect information.
1333 group = cl.group(msng_cl_lst, identity,
1337 group = cl.group(msng_cl_lst, identity,
1334 manifest_and_file_collector(changedfiles))
1338 manifest_and_file_collector(changedfiles))
1335 for chnk in group:
1339 for chnk in group:
1336 yield chnk
1340 yield chnk
1337
1341
1338 # The list of manifests has been collected by the generator
1342 # The list of manifests has been collected by the generator
1339 # calling our functions back.
1343 # calling our functions back.
1340 prune_manifests()
1344 prune_manifests()
1341 msng_mnfst_lst = msng_mnfst_set.keys()
1345 msng_mnfst_lst = msng_mnfst_set.keys()
1342 # Sort the manifestnodes by revision number.
1346 # Sort the manifestnodes by revision number.
1343 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1347 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1344 # Create a generator for the manifestnodes that calls our lookup
1348 # Create a generator for the manifestnodes that calls our lookup
1345 # and data collection functions back.
1349 # and data collection functions back.
1346 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1350 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1347 filenode_collector(changedfiles))
1351 filenode_collector(changedfiles))
1348 for chnk in group:
1352 for chnk in group:
1349 yield chnk
1353 yield chnk
1350
1354
1351 # These are no longer needed, dereference and toss the memory for
1355 # These are no longer needed, dereference and toss the memory for
1352 # them.
1356 # them.
1353 msng_mnfst_lst = None
1357 msng_mnfst_lst = None
1354 msng_mnfst_set.clear()
1358 msng_mnfst_set.clear()
1355
1359
1356 changedfiles = changedfiles.keys()
1360 changedfiles = changedfiles.keys()
1357 changedfiles.sort()
1361 changedfiles.sort()
1358 # Go through all our files in order sorted by name.
1362 # Go through all our files in order sorted by name.
1359 for fname in changedfiles:
1363 for fname in changedfiles:
1360 filerevlog = self.file(fname)
1364 filerevlog = self.file(fname)
1361 # Toss out the filenodes that the recipient isn't really
1365 # Toss out the filenodes that the recipient isn't really
1362 # missing.
1366 # missing.
1363 if msng_filenode_set.has_key(fname):
1367 if msng_filenode_set.has_key(fname):
1364 prune_filenodes(fname, filerevlog)
1368 prune_filenodes(fname, filerevlog)
1365 msng_filenode_lst = msng_filenode_set[fname].keys()
1369 msng_filenode_lst = msng_filenode_set[fname].keys()
1366 else:
1370 else:
1367 msng_filenode_lst = []
1371 msng_filenode_lst = []
1368 # If any filenodes are left, generate the group for them,
1372 # If any filenodes are left, generate the group for them,
1369 # otherwise don't bother.
1373 # otherwise don't bother.
1370 if len(msng_filenode_lst) > 0:
1374 if len(msng_filenode_lst) > 0:
1371 yield changegroup.genchunk(fname)
1375 yield changegroup.genchunk(fname)
1372 # Sort the filenodes by their revision #
1376 # Sort the filenodes by their revision #
1373 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1377 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1374 # Create a group generator and only pass in a changenode
1378 # Create a group generator and only pass in a changenode
1375 # lookup function as we need to collect no information
1379 # lookup function as we need to collect no information
1376 # from filenodes.
1380 # from filenodes.
1377 group = filerevlog.group(msng_filenode_lst,
1381 group = filerevlog.group(msng_filenode_lst,
1378 lookup_filenode_link_func(fname))
1382 lookup_filenode_link_func(fname))
1379 for chnk in group:
1383 for chnk in group:
1380 yield chnk
1384 yield chnk
1381 if msng_filenode_set.has_key(fname):
1385 if msng_filenode_set.has_key(fname):
1382 # Don't need this anymore, toss it to free memory.
1386 # Don't need this anymore, toss it to free memory.
1383 del msng_filenode_set[fname]
1387 del msng_filenode_set[fname]
1384 # Signal that no more groups are left.
1388 # Signal that no more groups are left.
1385 yield changegroup.closechunk()
1389 yield changegroup.closechunk()
1386
1390
1387 if msng_cl_lst:
1391 if msng_cl_lst:
1388 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1392 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1389
1393
1390 return util.chunkbuffer(gengroup())
1394 return util.chunkbuffer(gengroup())
1391
1395
1392 def changegroup(self, basenodes, source):
1396 def changegroup(self, basenodes, source):
1393 """Generate a changegroup of all nodes that we have that a recipient
1397 """Generate a changegroup of all nodes that we have that a recipient
1394 doesn't.
1398 doesn't.
1395
1399
1396 This is much easier than the previous function as we can assume that
1400 This is much easier than the previous function as we can assume that
1397 the recipient has any changenode we aren't sending them."""
1401 the recipient has any changenode we aren't sending them."""
1398
1402
1399 self.hook('preoutgoing', throw=True, source=source)
1403 self.hook('preoutgoing', throw=True, source=source)
1400
1404
1401 cl = self.changelog
1405 cl = self.changelog
1402 nodes = cl.nodesbetween(basenodes, None)[0]
1406 nodes = cl.nodesbetween(basenodes, None)[0]
1403 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1407 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1404
1408
1405 def identity(x):
1409 def identity(x):
1406 return x
1410 return x
1407
1411
1408 def gennodelst(revlog):
1412 def gennodelst(revlog):
1409 for r in xrange(0, revlog.count()):
1413 for r in xrange(0, revlog.count()):
1410 n = revlog.node(r)
1414 n = revlog.node(r)
1411 if revlog.linkrev(n) in revset:
1415 if revlog.linkrev(n) in revset:
1412 yield n
1416 yield n
1413
1417
1414 def changed_file_collector(changedfileset):
1418 def changed_file_collector(changedfileset):
1415 def collect_changed_files(clnode):
1419 def collect_changed_files(clnode):
1416 c = cl.read(clnode)
1420 c = cl.read(clnode)
1417 for fname in c[3]:
1421 for fname in c[3]:
1418 changedfileset[fname] = 1
1422 changedfileset[fname] = 1
1419 return collect_changed_files
1423 return collect_changed_files
1420
1424
1421 def lookuprevlink_func(revlog):
1425 def lookuprevlink_func(revlog):
1422 def lookuprevlink(n):
1426 def lookuprevlink(n):
1423 return cl.node(revlog.linkrev(n))
1427 return cl.node(revlog.linkrev(n))
1424 return lookuprevlink
1428 return lookuprevlink
1425
1429
1426 def gengroup():
1430 def gengroup():
1427 # construct a list of all changed files
1431 # construct a list of all changed files
1428 changedfiles = {}
1432 changedfiles = {}
1429
1433
1430 for chnk in cl.group(nodes, identity,
1434 for chnk in cl.group(nodes, identity,
1431 changed_file_collector(changedfiles)):
1435 changed_file_collector(changedfiles)):
1432 yield chnk
1436 yield chnk
1433 changedfiles = changedfiles.keys()
1437 changedfiles = changedfiles.keys()
1434 changedfiles.sort()
1438 changedfiles.sort()
1435
1439
1436 mnfst = self.manifest
1440 mnfst = self.manifest
1437 nodeiter = gennodelst(mnfst)
1441 nodeiter = gennodelst(mnfst)
1438 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1442 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1439 yield chnk
1443 yield chnk
1440
1444
1441 for fname in changedfiles:
1445 for fname in changedfiles:
1442 filerevlog = self.file(fname)
1446 filerevlog = self.file(fname)
1443 nodeiter = gennodelst(filerevlog)
1447 nodeiter = gennodelst(filerevlog)
1444 nodeiter = list(nodeiter)
1448 nodeiter = list(nodeiter)
1445 if nodeiter:
1449 if nodeiter:
1446 yield changegroup.genchunk(fname)
1450 yield changegroup.genchunk(fname)
1447 lookup = lookuprevlink_func(filerevlog)
1451 lookup = lookuprevlink_func(filerevlog)
1448 for chnk in filerevlog.group(nodeiter, lookup):
1452 for chnk in filerevlog.group(nodeiter, lookup):
1449 yield chnk
1453 yield chnk
1450
1454
1451 yield changegroup.closechunk()
1455 yield changegroup.closechunk()
1452
1456
1453 if nodes:
1457 if nodes:
1454 self.hook('outgoing', node=hex(nodes[0]), source=source)
1458 self.hook('outgoing', node=hex(nodes[0]), source=source)
1455
1459
1456 return util.chunkbuffer(gengroup())
1460 return util.chunkbuffer(gengroup())
1457
1461
1458 def addchangegroup(self, source, srctype):
1462 def addchangegroup(self, source, srctype):
1459 """add changegroup to repo.
1463 """add changegroup to repo.
1460 returns number of heads modified or added + 1."""
1464 returns number of heads modified or added + 1."""
1461
1465
1462 def csmap(x):
1466 def csmap(x):
1463 self.ui.debug(_("add changeset %s\n") % short(x))
1467 self.ui.debug(_("add changeset %s\n") % short(x))
1464 return cl.count()
1468 return cl.count()
1465
1469
1466 def revmap(x):
1470 def revmap(x):
1467 return cl.rev(x)
1471 return cl.rev(x)
1468
1472
1469 if not source:
1473 if not source:
1470 return 0
1474 return 0
1471
1475
1472 self.hook('prechangegroup', throw=True, source=srctype)
1476 self.hook('prechangegroup', throw=True, source=srctype)
1473
1477
1474 changesets = files = revisions = 0
1478 changesets = files = revisions = 0
1475
1479
1476 tr = self.transaction()
1480 tr = self.transaction()
1477
1481
1478 # write changelog and manifest data to temp files so
1482 # write changelog and manifest data to temp files so
1479 # concurrent readers will not see inconsistent view
1483 # concurrent readers will not see inconsistent view
1480 cl = None
1484 cl = None
1481 try:
1485 try:
1482 cl = appendfile.appendchangelog(self.opener, self.changelog.version)
1486 cl = appendfile.appendchangelog(self.opener, self.changelog.version)
1483
1487
1484 oldheads = len(cl.heads())
1488 oldheads = len(cl.heads())
1485
1489
1486 # pull off the changeset group
1490 # pull off the changeset group
1487 self.ui.status(_("adding changesets\n"))
1491 self.ui.status(_("adding changesets\n"))
1488 co = cl.tip()
1492 co = cl.tip()
1489 chunkiter = changegroup.chunkiter(source)
1493 chunkiter = changegroup.chunkiter(source)
1490 cn = cl.addgroup(chunkiter, csmap, tr, 1) # unique
1494 cn = cl.addgroup(chunkiter, csmap, tr, 1) # unique
1491 cnr, cor = map(cl.rev, (cn, co))
1495 cnr, cor = map(cl.rev, (cn, co))
1492 if cn == nullid:
1496 if cn == nullid:
1493 cnr = cor
1497 cnr = cor
1494 changesets = cnr - cor
1498 changesets = cnr - cor
1495
1499
1496 mf = None
1500 mf = None
1497 try:
1501 try:
1498 mf = appendfile.appendmanifest(self.opener,
1502 mf = appendfile.appendmanifest(self.opener,
1499 self.manifest.version)
1503 self.manifest.version)
1500
1504
1501 # pull off the manifest group
1505 # pull off the manifest group
1502 self.ui.status(_("adding manifests\n"))
1506 self.ui.status(_("adding manifests\n"))
1503 mm = mf.tip()
1507 mm = mf.tip()
1504 chunkiter = changegroup.chunkiter(source)
1508 chunkiter = changegroup.chunkiter(source)
1505 mo = mf.addgroup(chunkiter, revmap, tr)
1509 mo = mf.addgroup(chunkiter, revmap, tr)
1506
1510
1507 # process the files
1511 # process the files
1508 self.ui.status(_("adding file changes\n"))
1512 self.ui.status(_("adding file changes\n"))
1509 while 1:
1513 while 1:
1510 f = changegroup.getchunk(source)
1514 f = changegroup.getchunk(source)
1511 if not f:
1515 if not f:
1512 break
1516 break
1513 self.ui.debug(_("adding %s revisions\n") % f)
1517 self.ui.debug(_("adding %s revisions\n") % f)
1514 fl = self.file(f)
1518 fl = self.file(f)
1515 o = fl.count()
1519 o = fl.count()
1516 chunkiter = changegroup.chunkiter(source)
1520 chunkiter = changegroup.chunkiter(source)
1517 n = fl.addgroup(chunkiter, revmap, tr)
1521 n = fl.addgroup(chunkiter, revmap, tr)
1518 revisions += fl.count() - o
1522 revisions += fl.count() - o
1519 files += 1
1523 files += 1
1520
1524
1521 # write order here is important so concurrent readers will see
1525 # write order here is important so concurrent readers will see
1522 # consistent view of repo
1526 # consistent view of repo
1523 mf.writedata()
1527 mf.writedata()
1524 finally:
1528 finally:
1525 if mf:
1529 if mf:
1526 mf.cleanup()
1530 mf.cleanup()
1527 cl.writedata()
1531 cl.writedata()
1528 finally:
1532 finally:
1529 if cl:
1533 if cl:
1530 cl.cleanup()
1534 cl.cleanup()
1531
1535
1532 # make changelog and manifest see real files again
1536 # make changelog and manifest see real files again
1533 self.changelog = changelog.changelog(self.opener, self.changelog.version)
1537 self.changelog = changelog.changelog(self.opener, self.changelog.version)
1534 self.manifest = manifest.manifest(self.opener, self.manifest.version)
1538 self.manifest = manifest.manifest(self.opener, self.manifest.version)
1535 self.changelog.checkinlinesize(tr)
1539 self.changelog.checkinlinesize(tr)
1536 self.manifest.checkinlinesize(tr)
1540 self.manifest.checkinlinesize(tr)
1537
1541
1538 newheads = len(self.changelog.heads())
1542 newheads = len(self.changelog.heads())
1539 heads = ""
1543 heads = ""
1540 if oldheads and newheads > oldheads:
1544 if oldheads and newheads > oldheads:
1541 heads = _(" (+%d heads)") % (newheads - oldheads)
1545 heads = _(" (+%d heads)") % (newheads - oldheads)
1542
1546
1543 self.ui.status(_("added %d changesets"
1547 self.ui.status(_("added %d changesets"
1544 " with %d changes to %d files%s\n")
1548 " with %d changes to %d files%s\n")
1545 % (changesets, revisions, files, heads))
1549 % (changesets, revisions, files, heads))
1546
1550
1547 if changesets > 0:
1551 if changesets > 0:
1548 self.hook('pretxnchangegroup', throw=True,
1552 self.hook('pretxnchangegroup', throw=True,
1549 node=hex(self.changelog.node(cor+1)), source=srctype)
1553 node=hex(self.changelog.node(cor+1)), source=srctype)
1550
1554
1551 tr.close()
1555 tr.close()
1552
1556
1553 if changesets > 0:
1557 if changesets > 0:
1554 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1558 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1555 source=srctype)
1559 source=srctype)
1556
1560
1557 for i in range(cor + 1, cnr + 1):
1561 for i in range(cor + 1, cnr + 1):
1558 self.hook("incoming", node=hex(self.changelog.node(i)),
1562 self.hook("incoming", node=hex(self.changelog.node(i)),
1559 source=srctype)
1563 source=srctype)
1560
1564
1561 return newheads - oldheads + 1
1565 return newheads - oldheads + 1
1562
1566
1563 def update(self, node, allow=False, force=False, choose=None,
1567 def update(self, node, allow=False, force=False, choose=None,
1564 moddirstate=True, forcemerge=False, wlock=None, show_stats=True):
1568 moddirstate=True, forcemerge=False, wlock=None, show_stats=True):
1565 pl = self.dirstate.parents()
1569 pl = self.dirstate.parents()
1566 if not force and pl[1] != nullid:
1570 if not force and pl[1] != nullid:
1567 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1571 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1568 return 1
1572 return 1
1569
1573
1570 err = False
1574 err = False
1571
1575
1572 p1, p2 = pl[0], node
1576 p1, p2 = pl[0], node
1573 pa = self.changelog.ancestor(p1, p2)
1577 pa = self.changelog.ancestor(p1, p2)
1574 m1n = self.changelog.read(p1)[0]
1578 m1n = self.changelog.read(p1)[0]
1575 m2n = self.changelog.read(p2)[0]
1579 m2n = self.changelog.read(p2)[0]
1576 man = self.manifest.ancestor(m1n, m2n)
1580 man = self.manifest.ancestor(m1n, m2n)
1577 m1 = self.manifest.read(m1n)
1581 m1 = self.manifest.read(m1n)
1578 mf1 = self.manifest.readflags(m1n)
1582 mf1 = self.manifest.readflags(m1n)
1579 m2 = self.manifest.read(m2n).copy()
1583 m2 = self.manifest.read(m2n).copy()
1580 mf2 = self.manifest.readflags(m2n)
1584 mf2 = self.manifest.readflags(m2n)
1581 ma = self.manifest.read(man)
1585 ma = self.manifest.read(man)
1582 mfa = self.manifest.readflags(man)
1586 mfa = self.manifest.readflags(man)
1583
1587
1584 modified, added, removed, deleted, unknown = self.changes()
1588 modified, added, removed, deleted, unknown = self.changes()
1585
1589
1586 # is this a jump, or a merge? i.e. is there a linear path
1590 # is this a jump, or a merge? i.e. is there a linear path
1587 # from p1 to p2?
1591 # from p1 to p2?
1588 linear_path = (pa == p1 or pa == p2)
1592 linear_path = (pa == p1 or pa == p2)
1589
1593
1590 if allow and linear_path:
1594 if allow and linear_path:
1591 raise util.Abort(_("there is nothing to merge, "
1595 raise util.Abort(_("there is nothing to merge, "
1592 "just use 'hg update'"))
1596 "just use 'hg update'"))
1593 if allow and not forcemerge:
1597 if allow and not forcemerge:
1594 if modified or added or removed:
1598 if modified or added or removed:
1595 raise util.Abort(_("outstanding uncommitted changes"))
1599 raise util.Abort(_("outstanding uncommitted changes"))
1596 if not forcemerge and not force:
1600 if not forcemerge and not force:
1597 for f in unknown:
1601 for f in unknown:
1598 if f in m2:
1602 if f in m2:
1599 t1 = self.wread(f)
1603 t1 = self.wread(f)
1600 t2 = self.file(f).read(m2[f])
1604 t2 = self.file(f).read(m2[f])
1601 if cmp(t1, t2) != 0:
1605 if cmp(t1, t2) != 0:
1602 raise util.Abort(_("'%s' already exists in the working"
1606 raise util.Abort(_("'%s' already exists in the working"
1603 " dir and differs from remote") % f)
1607 " dir and differs from remote") % f)
1604
1608
1605 # resolve the manifest to determine which files
1609 # resolve the manifest to determine which files
1606 # we care about merging
1610 # we care about merging
1607 self.ui.note(_("resolving manifests\n"))
1611 self.ui.note(_("resolving manifests\n"))
1608 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1612 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1609 (force, allow, moddirstate, linear_path))
1613 (force, allow, moddirstate, linear_path))
1610 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1614 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1611 (short(man), short(m1n), short(m2n)))
1615 (short(man), short(m1n), short(m2n)))
1612
1616
1613 merge = {}
1617 merge = {}
1614 get = {}
1618 get = {}
1615 remove = []
1619 remove = []
1616
1620
1617 # construct a working dir manifest
1621 # construct a working dir manifest
1618 mw = m1.copy()
1622 mw = m1.copy()
1619 mfw = mf1.copy()
1623 mfw = mf1.copy()
1620 umap = dict.fromkeys(unknown)
1624 umap = dict.fromkeys(unknown)
1621
1625
1622 for f in added + modified + unknown:
1626 for f in added + modified + unknown:
1623 mw[f] = ""
1627 mw[f] = ""
1624 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1628 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1625
1629
1626 if moddirstate and not wlock:
1630 if moddirstate and not wlock:
1627 wlock = self.wlock()
1631 wlock = self.wlock()
1628
1632
1629 for f in deleted + removed:
1633 for f in deleted + removed:
1630 if f in mw:
1634 if f in mw:
1631 del mw[f]
1635 del mw[f]
1632
1636
1633 # If we're jumping between revisions (as opposed to merging),
1637 # If we're jumping between revisions (as opposed to merging),
1634 # and if neither the working directory nor the target rev has
1638 # and if neither the working directory nor the target rev has
1635 # the file, then we need to remove it from the dirstate, to
1639 # the file, then we need to remove it from the dirstate, to
1636 # prevent the dirstate from listing the file when it is no
1640 # prevent the dirstate from listing the file when it is no
1637 # longer in the manifest.
1641 # longer in the manifest.
1638 if moddirstate and linear_path and f not in m2:
1642 if moddirstate and linear_path and f not in m2:
1639 self.dirstate.forget((f,))
1643 self.dirstate.forget((f,))
1640
1644
1641 # Compare manifests
1645 # Compare manifests
1642 for f, n in mw.iteritems():
1646 for f, n in mw.iteritems():
1643 if choose and not choose(f):
1647 if choose and not choose(f):
1644 continue
1648 continue
1645 if f in m2:
1649 if f in m2:
1646 s = 0
1650 s = 0
1647
1651
1648 # is the wfile new since m1, and match m2?
1652 # is the wfile new since m1, and match m2?
1649 if f not in m1:
1653 if f not in m1:
1650 t1 = self.wread(f)
1654 t1 = self.wread(f)
1651 t2 = self.file(f).read(m2[f])
1655 t2 = self.file(f).read(m2[f])
1652 if cmp(t1, t2) == 0:
1656 if cmp(t1, t2) == 0:
1653 n = m2[f]
1657 n = m2[f]
1654 del t1, t2
1658 del t1, t2
1655
1659
1656 # are files different?
1660 # are files different?
1657 if n != m2[f]:
1661 if n != m2[f]:
1658 a = ma.get(f, nullid)
1662 a = ma.get(f, nullid)
1659 # are both different from the ancestor?
1663 # are both different from the ancestor?
1660 if n != a and m2[f] != a:
1664 if n != a and m2[f] != a:
1661 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1665 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1662 # merge executable bits
1666 # merge executable bits
1663 # "if we changed or they changed, change in merge"
1667 # "if we changed or they changed, change in merge"
1664 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1668 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1665 mode = ((a^b) | (a^c)) ^ a
1669 mode = ((a^b) | (a^c)) ^ a
1666 merge[f] = (m1.get(f, nullid), m2[f], mode)
1670 merge[f] = (m1.get(f, nullid), m2[f], mode)
1667 s = 1
1671 s = 1
1668 # are we clobbering?
1672 # are we clobbering?
1669 # is remote's version newer?
1673 # is remote's version newer?
1670 # or are we going back in time?
1674 # or are we going back in time?
1671 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1675 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1672 self.ui.debug(_(" remote %s is newer, get\n") % f)
1676 self.ui.debug(_(" remote %s is newer, get\n") % f)
1673 get[f] = m2[f]
1677 get[f] = m2[f]
1674 s = 1
1678 s = 1
1675 elif f in umap or f in added:
1679 elif f in umap or f in added:
1676 # this unknown file is the same as the checkout
1680 # this unknown file is the same as the checkout
1677 # we need to reset the dirstate if the file was added
1681 # we need to reset the dirstate if the file was added
1678 get[f] = m2[f]
1682 get[f] = m2[f]
1679
1683
1680 if not s and mfw[f] != mf2[f]:
1684 if not s and mfw[f] != mf2[f]:
1681 if force:
1685 if force:
1682 self.ui.debug(_(" updating permissions for %s\n") % f)
1686 self.ui.debug(_(" updating permissions for %s\n") % f)
1683 util.set_exec(self.wjoin(f), mf2[f])
1687 util.set_exec(self.wjoin(f), mf2[f])
1684 else:
1688 else:
1685 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1689 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1686 mode = ((a^b) | (a^c)) ^ a
1690 mode = ((a^b) | (a^c)) ^ a
1687 if mode != b:
1691 if mode != b:
1688 self.ui.debug(_(" updating permissions for %s\n")
1692 self.ui.debug(_(" updating permissions for %s\n")
1689 % f)
1693 % f)
1690 util.set_exec(self.wjoin(f), mode)
1694 util.set_exec(self.wjoin(f), mode)
1691 del m2[f]
1695 del m2[f]
1692 elif f in ma:
1696 elif f in ma:
1693 if n != ma[f]:
1697 if n != ma[f]:
1694 r = _("d")
1698 r = _("d")
1695 if not force and (linear_path or allow):
1699 if not force and (linear_path or allow):
1696 r = self.ui.prompt(
1700 r = self.ui.prompt(
1697 (_(" local changed %s which remote deleted\n") % f) +
1701 (_(" local changed %s which remote deleted\n") % f) +
1698 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1702 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1699 if r == _("d"):
1703 if r == _("d"):
1700 remove.append(f)
1704 remove.append(f)
1701 else:
1705 else:
1702 self.ui.debug(_("other deleted %s\n") % f)
1706 self.ui.debug(_("other deleted %s\n") % f)
1703 remove.append(f) # other deleted it
1707 remove.append(f) # other deleted it
1704 else:
1708 else:
1705 # file is created on branch or in working directory
1709 # file is created on branch or in working directory
1706 if force and f not in umap:
1710 if force and f not in umap:
1707 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1711 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1708 remove.append(f)
1712 remove.append(f)
1709 elif n == m1.get(f, nullid): # same as parent
1713 elif n == m1.get(f, nullid): # same as parent
1710 if p2 == pa: # going backwards?
1714 if p2 == pa: # going backwards?
1711 self.ui.debug(_("remote deleted %s\n") % f)
1715 self.ui.debug(_("remote deleted %s\n") % f)
1712 remove.append(f)
1716 remove.append(f)
1713 else:
1717 else:
1714 self.ui.debug(_("local modified %s, keeping\n") % f)
1718 self.ui.debug(_("local modified %s, keeping\n") % f)
1715 else:
1719 else:
1716 self.ui.debug(_("working dir created %s, keeping\n") % f)
1720 self.ui.debug(_("working dir created %s, keeping\n") % f)
1717
1721
1718 for f, n in m2.iteritems():
1722 for f, n in m2.iteritems():
1719 if choose and not choose(f):
1723 if choose and not choose(f):
1720 continue
1724 continue
1721 if f[0] == "/":
1725 if f[0] == "/":
1722 continue
1726 continue
1723 if f in ma and n != ma[f]:
1727 if f in ma and n != ma[f]:
1724 r = _("k")
1728 r = _("k")
1725 if not force and (linear_path or allow):
1729 if not force and (linear_path or allow):
1726 r = self.ui.prompt(
1730 r = self.ui.prompt(
1727 (_("remote changed %s which local deleted\n") % f) +
1731 (_("remote changed %s which local deleted\n") % f) +
1728 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1732 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1729 if r == _("k"):
1733 if r == _("k"):
1730 get[f] = n
1734 get[f] = n
1731 elif f not in ma:
1735 elif f not in ma:
1732 self.ui.debug(_("remote created %s\n") % f)
1736 self.ui.debug(_("remote created %s\n") % f)
1733 get[f] = n
1737 get[f] = n
1734 else:
1738 else:
1735 if force or p2 == pa: # going backwards?
1739 if force or p2 == pa: # going backwards?
1736 self.ui.debug(_("local deleted %s, recreating\n") % f)
1740 self.ui.debug(_("local deleted %s, recreating\n") % f)
1737 get[f] = n
1741 get[f] = n
1738 else:
1742 else:
1739 self.ui.debug(_("local deleted %s\n") % f)
1743 self.ui.debug(_("local deleted %s\n") % f)
1740
1744
1741 del mw, m1, m2, ma
1745 del mw, m1, m2, ma
1742
1746
1743 if force:
1747 if force:
1744 for f in merge:
1748 for f in merge:
1745 get[f] = merge[f][1]
1749 get[f] = merge[f][1]
1746 merge = {}
1750 merge = {}
1747
1751
1748 if linear_path or force:
1752 if linear_path or force:
1749 # we don't need to do any magic, just jump to the new rev
1753 # we don't need to do any magic, just jump to the new rev
1750 branch_merge = False
1754 branch_merge = False
1751 p1, p2 = p2, nullid
1755 p1, p2 = p2, nullid
1752 else:
1756 else:
1753 if not allow:
1757 if not allow:
1754 self.ui.status(_("this update spans a branch"
1758 self.ui.status(_("this update spans a branch"
1755 " affecting the following files:\n"))
1759 " affecting the following files:\n"))
1756 fl = merge.keys() + get.keys()
1760 fl = merge.keys() + get.keys()
1757 fl.sort()
1761 fl.sort()
1758 for f in fl:
1762 for f in fl:
1759 cf = ""
1763 cf = ""
1760 if f in merge:
1764 if f in merge:
1761 cf = _(" (resolve)")
1765 cf = _(" (resolve)")
1762 self.ui.status(" %s%s\n" % (f, cf))
1766 self.ui.status(" %s%s\n" % (f, cf))
1763 self.ui.warn(_("aborting update spanning branches!\n"))
1767 self.ui.warn(_("aborting update spanning branches!\n"))
1764 self.ui.status(_("(use 'hg merge' to merge across branches"
1768 self.ui.status(_("(use 'hg merge' to merge across branches"
1765 " or 'hg update -C' to lose changes)\n"))
1769 " or 'hg update -C' to lose changes)\n"))
1766 return 1
1770 return 1
1767 branch_merge = True
1771 branch_merge = True
1768
1772
1769 # get the files we don't need to change
1773 # get the files we don't need to change
1770 files = get.keys()
1774 files = get.keys()
1771 files.sort()
1775 files.sort()
1772 for f in files:
1776 for f in files:
1773 if f[0] == "/":
1777 if f[0] == "/":
1774 continue
1778 continue
1775 self.ui.note(_("getting %s\n") % f)
1779 self.ui.note(_("getting %s\n") % f)
1776 t = self.file(f).read(get[f])
1780 t = self.file(f).read(get[f])
1777 self.wwrite(f, t)
1781 self.wwrite(f, t)
1778 util.set_exec(self.wjoin(f), mf2[f])
1782 util.set_exec(self.wjoin(f), mf2[f])
1779 if moddirstate:
1783 if moddirstate:
1780 if branch_merge:
1784 if branch_merge:
1781 self.dirstate.update([f], 'n', st_mtime=-1)
1785 self.dirstate.update([f], 'n', st_mtime=-1)
1782 else:
1786 else:
1783 self.dirstate.update([f], 'n')
1787 self.dirstate.update([f], 'n')
1784
1788
1785 # merge the tricky bits
1789 # merge the tricky bits
1786 failedmerge = []
1790 failedmerge = []
1787 files = merge.keys()
1791 files = merge.keys()
1788 files.sort()
1792 files.sort()
1789 xp1 = hex(p1)
1793 xp1 = hex(p1)
1790 xp2 = hex(p2)
1794 xp2 = hex(p2)
1791 for f in files:
1795 for f in files:
1792 self.ui.status(_("merging %s\n") % f)
1796 self.ui.status(_("merging %s\n") % f)
1793 my, other, flag = merge[f]
1797 my, other, flag = merge[f]
1794 ret = self.merge3(f, my, other, xp1, xp2)
1798 ret = self.merge3(f, my, other, xp1, xp2)
1795 if ret:
1799 if ret:
1796 err = True
1800 err = True
1797 failedmerge.append(f)
1801 failedmerge.append(f)
1798 util.set_exec(self.wjoin(f), flag)
1802 util.set_exec(self.wjoin(f), flag)
1799 if moddirstate:
1803 if moddirstate:
1800 if branch_merge:
1804 if branch_merge:
1801 # We've done a branch merge, mark this file as merged
1805 # We've done a branch merge, mark this file as merged
1802 # so that we properly record the merger later
1806 # so that we properly record the merger later
1803 self.dirstate.update([f], 'm')
1807 self.dirstate.update([f], 'm')
1804 else:
1808 else:
1805 # We've update-merged a locally modified file, so
1809 # We've update-merged a locally modified file, so
1806 # we set the dirstate to emulate a normal checkout
1810 # we set the dirstate to emulate a normal checkout
1807 # of that file some time in the past. Thus our
1811 # of that file some time in the past. Thus our
1808 # merge will appear as a normal local file
1812 # merge will appear as a normal local file
1809 # modification.
1813 # modification.
1810 f_len = len(self.file(f).read(other))
1814 f_len = len(self.file(f).read(other))
1811 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1815 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1812
1816
1813 remove.sort()
1817 remove.sort()
1814 for f in remove:
1818 for f in remove:
1815 self.ui.note(_("removing %s\n") % f)
1819 self.ui.note(_("removing %s\n") % f)
1816 util.audit_path(f)
1820 util.audit_path(f)
1817 try:
1821 try:
1818 util.unlink(self.wjoin(f))
1822 util.unlink(self.wjoin(f))
1819 except OSError, inst:
1823 except OSError, inst:
1820 if inst.errno != errno.ENOENT:
1824 if inst.errno != errno.ENOENT:
1821 self.ui.warn(_("update failed to remove %s: %s!\n") %
1825 self.ui.warn(_("update failed to remove %s: %s!\n") %
1822 (f, inst.strerror))
1826 (f, inst.strerror))
1823 if moddirstate:
1827 if moddirstate:
1824 if branch_merge:
1828 if branch_merge:
1825 self.dirstate.update(remove, 'r')
1829 self.dirstate.update(remove, 'r')
1826 else:
1830 else:
1827 self.dirstate.forget(remove)
1831 self.dirstate.forget(remove)
1828
1832
1829 if moddirstate:
1833 if moddirstate:
1830 self.dirstate.setparents(p1, p2)
1834 self.dirstate.setparents(p1, p2)
1831
1835
1832 if show_stats:
1836 if show_stats:
1833 stats = ((len(get), _("updated")),
1837 stats = ((len(get), _("updated")),
1834 (len(merge) - len(failedmerge), _("merged")),
1838 (len(merge) - len(failedmerge), _("merged")),
1835 (len(remove), _("removed")),
1839 (len(remove), _("removed")),
1836 (len(failedmerge), _("unresolved")))
1840 (len(failedmerge), _("unresolved")))
1837 note = ", ".join([_("%d files %s") % s for s in stats])
1841 note = ", ".join([_("%d files %s") % s for s in stats])
1838 self.ui.status("%s\n" % note)
1842 self.ui.status("%s\n" % note)
1839 if moddirstate:
1843 if moddirstate:
1840 if branch_merge:
1844 if branch_merge:
1841 if failedmerge:
1845 if failedmerge:
1842 self.ui.status(_("There are unresolved merges,"
1846 self.ui.status(_("There are unresolved merges,"
1843 " you can redo the full merge using:\n"
1847 " you can redo the full merge using:\n"
1844 " hg update -C %s\n"
1848 " hg update -C %s\n"
1845 " hg merge %s\n"
1849 " hg merge %s\n"
1846 % (self.changelog.rev(p1),
1850 % (self.changelog.rev(p1),
1847 self.changelog.rev(p2))))
1851 self.changelog.rev(p2))))
1848 else:
1852 else:
1849 self.ui.status(_("(branch merge, don't forget to commit)\n"))
1853 self.ui.status(_("(branch merge, don't forget to commit)\n"))
1850 elif failedmerge:
1854 elif failedmerge:
1851 self.ui.status(_("There are unresolved merges with"
1855 self.ui.status(_("There are unresolved merges with"
1852 " locally modified files.\n"))
1856 " locally modified files.\n"))
1853
1857
1854 return err
1858 return err
1855
1859
1856 def merge3(self, fn, my, other, p1, p2):
1860 def merge3(self, fn, my, other, p1, p2):
1857 """perform a 3-way merge in the working directory"""
1861 """perform a 3-way merge in the working directory"""
1858
1862
1859 def temp(prefix, node):
1863 def temp(prefix, node):
1860 pre = "%s~%s." % (os.path.basename(fn), prefix)
1864 pre = "%s~%s." % (os.path.basename(fn), prefix)
1861 (fd, name) = tempfile.mkstemp(prefix=pre)
1865 (fd, name) = tempfile.mkstemp(prefix=pre)
1862 f = os.fdopen(fd, "wb")
1866 f = os.fdopen(fd, "wb")
1863 self.wwrite(fn, fl.read(node), f)
1867 self.wwrite(fn, fl.read(node), f)
1864 f.close()
1868 f.close()
1865 return name
1869 return name
1866
1870
1867 fl = self.file(fn)
1871 fl = self.file(fn)
1868 base = fl.ancestor(my, other)
1872 base = fl.ancestor(my, other)
1869 a = self.wjoin(fn)
1873 a = self.wjoin(fn)
1870 b = temp("base", base)
1874 b = temp("base", base)
1871 c = temp("other", other)
1875 c = temp("other", other)
1872
1876
1873 self.ui.note(_("resolving %s\n") % fn)
1877 self.ui.note(_("resolving %s\n") % fn)
1874 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1878 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1875 (fn, short(my), short(other), short(base)))
1879 (fn, short(my), short(other), short(base)))
1876
1880
1877 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1881 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1878 or "hgmerge")
1882 or "hgmerge")
1879 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
1883 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
1880 environ={'HG_FILE': fn,
1884 environ={'HG_FILE': fn,
1881 'HG_MY_NODE': p1,
1885 'HG_MY_NODE': p1,
1882 'HG_OTHER_NODE': p2,
1886 'HG_OTHER_NODE': p2,
1883 'HG_FILE_MY_NODE': hex(my),
1887 'HG_FILE_MY_NODE': hex(my),
1884 'HG_FILE_OTHER_NODE': hex(other),
1888 'HG_FILE_OTHER_NODE': hex(other),
1885 'HG_FILE_BASE_NODE': hex(base)})
1889 'HG_FILE_BASE_NODE': hex(base)})
1886 if r:
1890 if r:
1887 self.ui.warn(_("merging %s failed!\n") % fn)
1891 self.ui.warn(_("merging %s failed!\n") % fn)
1888
1892
1889 os.unlink(b)
1893 os.unlink(b)
1890 os.unlink(c)
1894 os.unlink(c)
1891 return r
1895 return r
1892
1896
1893 def verify(self):
1897 def verify(self):
1894 filelinkrevs = {}
1898 filelinkrevs = {}
1895 filenodes = {}
1899 filenodes = {}
1896 changesets = revisions = files = 0
1900 changesets = revisions = files = 0
1897 errors = [0]
1901 errors = [0]
1898 warnings = [0]
1902 warnings = [0]
1899 neededmanifests = {}
1903 neededmanifests = {}
1900
1904
1901 def err(msg):
1905 def err(msg):
1902 self.ui.warn(msg + "\n")
1906 self.ui.warn(msg + "\n")
1903 errors[0] += 1
1907 errors[0] += 1
1904
1908
1905 def warn(msg):
1909 def warn(msg):
1906 self.ui.warn(msg + "\n")
1910 self.ui.warn(msg + "\n")
1907 warnings[0] += 1
1911 warnings[0] += 1
1908
1912
1909 def checksize(obj, name):
1913 def checksize(obj, name):
1910 d = obj.checksize()
1914 d = obj.checksize()
1911 if d[0]:
1915 if d[0]:
1912 err(_("%s data length off by %d bytes") % (name, d[0]))
1916 err(_("%s data length off by %d bytes") % (name, d[0]))
1913 if d[1]:
1917 if d[1]:
1914 err(_("%s index contains %d extra bytes") % (name, d[1]))
1918 err(_("%s index contains %d extra bytes") % (name, d[1]))
1915
1919
1916 def checkversion(obj, name):
1920 def checkversion(obj, name):
1917 if obj.version != revlog.REVLOGV0:
1921 if obj.version != revlog.REVLOGV0:
1918 if not revlogv1:
1922 if not revlogv1:
1919 warn(_("warning: `%s' uses revlog format 1") % name)
1923 warn(_("warning: `%s' uses revlog format 1") % name)
1920 elif revlogv1:
1924 elif revlogv1:
1921 warn(_("warning: `%s' uses revlog format 0") % name)
1925 warn(_("warning: `%s' uses revlog format 0") % name)
1922
1926
1923 revlogv1 = self.revlogversion != revlog.REVLOGV0
1927 revlogv1 = self.revlogversion != revlog.REVLOGV0
1924 if self.ui.verbose or revlogv1 != self.revlogv1:
1928 if self.ui.verbose or revlogv1 != self.revlogv1:
1925 self.ui.status(_("repository uses revlog format %d\n") %
1929 self.ui.status(_("repository uses revlog format %d\n") %
1926 (revlogv1 and 1 or 0))
1930 (revlogv1 and 1 or 0))
1927
1931
1928 seen = {}
1932 seen = {}
1929 self.ui.status(_("checking changesets\n"))
1933 self.ui.status(_("checking changesets\n"))
1930 checksize(self.changelog, "changelog")
1934 checksize(self.changelog, "changelog")
1931
1935
1932 for i in range(self.changelog.count()):
1936 for i in range(self.changelog.count()):
1933 changesets += 1
1937 changesets += 1
1934 n = self.changelog.node(i)
1938 n = self.changelog.node(i)
1935 l = self.changelog.linkrev(n)
1939 l = self.changelog.linkrev(n)
1936 if l != i:
1940 if l != i:
1937 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
1941 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
1938 if n in seen:
1942 if n in seen:
1939 err(_("duplicate changeset at revision %d") % i)
1943 err(_("duplicate changeset at revision %d") % i)
1940 seen[n] = 1
1944 seen[n] = 1
1941
1945
1942 for p in self.changelog.parents(n):
1946 for p in self.changelog.parents(n):
1943 if p not in self.changelog.nodemap:
1947 if p not in self.changelog.nodemap:
1944 err(_("changeset %s has unknown parent %s") %
1948 err(_("changeset %s has unknown parent %s") %
1945 (short(n), short(p)))
1949 (short(n), short(p)))
1946 try:
1950 try:
1947 changes = self.changelog.read(n)
1951 changes = self.changelog.read(n)
1948 except KeyboardInterrupt:
1952 except KeyboardInterrupt:
1949 self.ui.warn(_("interrupted"))
1953 self.ui.warn(_("interrupted"))
1950 raise
1954 raise
1951 except Exception, inst:
1955 except Exception, inst:
1952 err(_("unpacking changeset %s: %s") % (short(n), inst))
1956 err(_("unpacking changeset %s: %s") % (short(n), inst))
1953 continue
1957 continue
1954
1958
1955 neededmanifests[changes[0]] = n
1959 neededmanifests[changes[0]] = n
1956
1960
1957 for f in changes[3]:
1961 for f in changes[3]:
1958 filelinkrevs.setdefault(f, []).append(i)
1962 filelinkrevs.setdefault(f, []).append(i)
1959
1963
1960 seen = {}
1964 seen = {}
1961 self.ui.status(_("checking manifests\n"))
1965 self.ui.status(_("checking manifests\n"))
1962 checkversion(self.manifest, "manifest")
1966 checkversion(self.manifest, "manifest")
1963 checksize(self.manifest, "manifest")
1967 checksize(self.manifest, "manifest")
1964
1968
1965 for i in range(self.manifest.count()):
1969 for i in range(self.manifest.count()):
1966 n = self.manifest.node(i)
1970 n = self.manifest.node(i)
1967 l = self.manifest.linkrev(n)
1971 l = self.manifest.linkrev(n)
1968
1972
1969 if l < 0 or l >= self.changelog.count():
1973 if l < 0 or l >= self.changelog.count():
1970 err(_("bad manifest link (%d) at revision %d") % (l, i))
1974 err(_("bad manifest link (%d) at revision %d") % (l, i))
1971
1975
1972 if n in neededmanifests:
1976 if n in neededmanifests:
1973 del neededmanifests[n]
1977 del neededmanifests[n]
1974
1978
1975 if n in seen:
1979 if n in seen:
1976 err(_("duplicate manifest at revision %d") % i)
1980 err(_("duplicate manifest at revision %d") % i)
1977
1981
1978 seen[n] = 1
1982 seen[n] = 1
1979
1983
1980 for p in self.manifest.parents(n):
1984 for p in self.manifest.parents(n):
1981 if p not in self.manifest.nodemap:
1985 if p not in self.manifest.nodemap:
1982 err(_("manifest %s has unknown parent %s") %
1986 err(_("manifest %s has unknown parent %s") %
1983 (short(n), short(p)))
1987 (short(n), short(p)))
1984
1988
1985 try:
1989 try:
1986 delta = mdiff.patchtext(self.manifest.delta(n))
1990 delta = mdiff.patchtext(self.manifest.delta(n))
1987 except KeyboardInterrupt:
1991 except KeyboardInterrupt:
1988 self.ui.warn(_("interrupted"))
1992 self.ui.warn(_("interrupted"))
1989 raise
1993 raise
1990 except Exception, inst:
1994 except Exception, inst:
1991 err(_("unpacking manifest %s: %s") % (short(n), inst))
1995 err(_("unpacking manifest %s: %s") % (short(n), inst))
1992 continue
1996 continue
1993
1997
1994 try:
1998 try:
1995 ff = [ l.split('\0') for l in delta.splitlines() ]
1999 ff = [ l.split('\0') for l in delta.splitlines() ]
1996 for f, fn in ff:
2000 for f, fn in ff:
1997 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
2001 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1998 except (ValueError, TypeError), inst:
2002 except (ValueError, TypeError), inst:
1999 err(_("broken delta in manifest %s: %s") % (short(n), inst))
2003 err(_("broken delta in manifest %s: %s") % (short(n), inst))
2000
2004
2001 self.ui.status(_("crosschecking files in changesets and manifests\n"))
2005 self.ui.status(_("crosschecking files in changesets and manifests\n"))
2002
2006
2003 for m, c in neededmanifests.items():
2007 for m, c in neededmanifests.items():
2004 err(_("Changeset %s refers to unknown manifest %s") %
2008 err(_("Changeset %s refers to unknown manifest %s") %
2005 (short(m), short(c)))
2009 (short(m), short(c)))
2006 del neededmanifests
2010 del neededmanifests
2007
2011
2008 for f in filenodes:
2012 for f in filenodes:
2009 if f not in filelinkrevs:
2013 if f not in filelinkrevs:
2010 err(_("file %s in manifest but not in changesets") % f)
2014 err(_("file %s in manifest but not in changesets") % f)
2011
2015
2012 for f in filelinkrevs:
2016 for f in filelinkrevs:
2013 if f not in filenodes:
2017 if f not in filenodes:
2014 err(_("file %s in changeset but not in manifest") % f)
2018 err(_("file %s in changeset but not in manifest") % f)
2015
2019
2016 self.ui.status(_("checking files\n"))
2020 self.ui.status(_("checking files\n"))
2017 ff = filenodes.keys()
2021 ff = filenodes.keys()
2018 ff.sort()
2022 ff.sort()
2019 for f in ff:
2023 for f in ff:
2020 if f == "/dev/null":
2024 if f == "/dev/null":
2021 continue
2025 continue
2022 files += 1
2026 files += 1
2023 if not f:
2027 if not f:
2024 err(_("file without name in manifest %s") % short(n))
2028 err(_("file without name in manifest %s") % short(n))
2025 continue
2029 continue
2026 fl = self.file(f)
2030 fl = self.file(f)
2027 checkversion(fl, f)
2031 checkversion(fl, f)
2028 checksize(fl, f)
2032 checksize(fl, f)
2029
2033
2030 nodes = {nullid: 1}
2034 nodes = {nullid: 1}
2031 seen = {}
2035 seen = {}
2032 for i in range(fl.count()):
2036 for i in range(fl.count()):
2033 revisions += 1
2037 revisions += 1
2034 n = fl.node(i)
2038 n = fl.node(i)
2035
2039
2036 if n in seen:
2040 if n in seen:
2037 err(_("%s: duplicate revision %d") % (f, i))
2041 err(_("%s: duplicate revision %d") % (f, i))
2038 if n not in filenodes[f]:
2042 if n not in filenodes[f]:
2039 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
2043 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
2040 else:
2044 else:
2041 del filenodes[f][n]
2045 del filenodes[f][n]
2042
2046
2043 flr = fl.linkrev(n)
2047 flr = fl.linkrev(n)
2044 if flr not in filelinkrevs.get(f, []):
2048 if flr not in filelinkrevs.get(f, []):
2045 err(_("%s:%s points to unexpected changeset %d")
2049 err(_("%s:%s points to unexpected changeset %d")
2046 % (f, short(n), flr))
2050 % (f, short(n), flr))
2047 else:
2051 else:
2048 filelinkrevs[f].remove(flr)
2052 filelinkrevs[f].remove(flr)
2049
2053
2050 # verify contents
2054 # verify contents
2051 try:
2055 try:
2052 t = fl.read(n)
2056 t = fl.read(n)
2053 except KeyboardInterrupt:
2057 except KeyboardInterrupt:
2054 self.ui.warn(_("interrupted"))
2058 self.ui.warn(_("interrupted"))
2055 raise
2059 raise
2056 except Exception, inst:
2060 except Exception, inst:
2057 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
2061 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
2058
2062
2059 # verify parents
2063 # verify parents
2060 (p1, p2) = fl.parents(n)
2064 (p1, p2) = fl.parents(n)
2061 if p1 not in nodes:
2065 if p1 not in nodes:
2062 err(_("file %s:%s unknown parent 1 %s") %
2066 err(_("file %s:%s unknown parent 1 %s") %
2063 (f, short(n), short(p1)))
2067 (f, short(n), short(p1)))
2064 if p2 not in nodes:
2068 if p2 not in nodes:
2065 err(_("file %s:%s unknown parent 2 %s") %
2069 err(_("file %s:%s unknown parent 2 %s") %
2066 (f, short(n), short(p1)))
2070 (f, short(n), short(p1)))
2067 nodes[n] = 1
2071 nodes[n] = 1
2068
2072
2069 # cross-check
2073 # cross-check
2070 for node in filenodes[f]:
2074 for node in filenodes[f]:
2071 err(_("node %s in manifests not in %s") % (hex(node), f))
2075 err(_("node %s in manifests not in %s") % (hex(node), f))
2072
2076
2073 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
2077 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
2074 (files, changesets, revisions))
2078 (files, changesets, revisions))
2075
2079
2076 if warnings[0]:
2080 if warnings[0]:
2077 self.ui.warn(_("%d warnings encountered!\n") % warnings[0])
2081 self.ui.warn(_("%d warnings encountered!\n") % warnings[0])
2078 if errors[0]:
2082 if errors[0]:
2079 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
2083 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
2080 return 1
2084 return 1
2081
2085
2082 # used to avoid circular references so destructors work
2086 # used to avoid circular references so destructors work
2083 def aftertrans(base):
2087 def aftertrans(base):
2084 p = base
2088 p = base
2085 def a():
2089 def a():
2086 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
2090 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
2087 util.rename(os.path.join(p, "journal.dirstate"),
2091 util.rename(os.path.join(p, "journal.dirstate"),
2088 os.path.join(p, "undo.dirstate"))
2092 os.path.join(p, "undo.dirstate"))
2089 return a
2093 return a
2090
2094
General Comments 0
You need to be logged in to leave comments. Login now