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