##// END OF EJS Templates
Clean up walk and changes code to use normalised names properly....
Bryan O'Sullivan -
r820:89985a1b default
parent child Browse files
Show More
@@ -1,1427 +1,1445 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 demandload(globals(), "os re sys signal shutil")
9 demandload(globals(), "os re sys signal shutil")
10 demandload(globals(), "fancyopts ui hg util")
10 demandload(globals(), "fancyopts ui hg util")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 demandload(globals(), "errno socket version struct atexit")
12 demandload(globals(), "errno socket version struct atexit")
13
13
14 class UnknownCommand(Exception):
14 class UnknownCommand(Exception):
15 """Exception raised if command is not in the command table."""
15 """Exception raised if command is not in the command table."""
16
16
17 class Abort(Exception):
17 class Abort(Exception):
18 """Raised if a command needs to print an error and exit."""
18 """Raised if a command needs to print an error and exit."""
19
19
20 def filterfiles(filters, files):
20 def filterfiles(filters, files):
21 l = [x for x in files if x in filters]
21 l = [x for x in files if x in filters]
22
22
23 for t in filters:
23 for t in filters:
24 if t and t[-1] != "/":
24 if t and t[-1] != "/":
25 t += "/"
25 t += "/"
26 l += [x for x in files if x.startswith(t)]
26 l += [x for x in files if x.startswith(t)]
27 return l
27 return l
28
28
29 def relfilter(repo, files):
29 def relfilter(repo, files):
30 cwd = repo.getcwd()
30 cwd = repo.getcwd()
31 if cwd:
31 if cwd:
32 return filterfiles([util.pconvert(cwd)], files)
32 return filterfiles([util.pconvert(cwd)], files)
33 return files
33 return files
34
34
35 def relpath(repo, args):
35 def relpath(repo, args):
36 cwd = repo.getcwd()
36 cwd = repo.getcwd()
37 if cwd:
37 if cwd:
38 return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
38 return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
39 for x in args]
39 for x in args]
40 return args
40 return args
41
41
42 def matchpats(cwd, pats = [], opts = {}, head = ''):
42 def matchpats(cwd, pats = [], opts = {}, head = ''):
43 return util.matcher(cwd, pats, opts.get('include'),
43 return util.matcher(cwd, pats or ['.'], opts.get('include'),
44 opts.get('exclude'), head)
44 opts.get('exclude'), head)
45
45
46 def pathto(n1, n2):
47 '''return the relative path from one place to another'''
48 if not n1: return n2
49 a, b = n1.split(os.sep), n2.split(os.sep)
50 a.reverse(), b.reverse()
51 while a and b and a[-1] == b[-1]:
52 a.pop(), b.pop()
53 b.reverse()
54 return os.sep.join((['..'] * len(a)) + b)
55
46 def walk(repo, pats, opts, head = ''):
56 def walk(repo, pats, opts, head = ''):
47 cwd = repo.getcwd()
57 cwd = repo.getcwd()
48 c = 0
49 if cwd: c = len(cwd) + 1
50 files, matchfn = matchpats(cwd, pats, opts, head)
58 files, matchfn = matchpats(cwd, pats, opts, head)
51 for src, fn in repo.walk(files = files, match = matchfn):
59 for src, fn in repo.walk(files = files, match = matchfn):
52 yield src, fn, fn[c:]
60 yield src, fn, pathto(cwd, fn)
53
61
54 revrangesep = ':'
62 revrangesep = ':'
55
63
56 def revrange(ui, repo, revs, revlog=None):
64 def revrange(ui, repo, revs, revlog=None):
57 if revlog is None:
65 if revlog is None:
58 revlog = repo.changelog
66 revlog = repo.changelog
59 revcount = revlog.count()
67 revcount = revlog.count()
60 def fix(val, defval):
68 def fix(val, defval):
61 if not val:
69 if not val:
62 return defval
70 return defval
63 try:
71 try:
64 num = int(val)
72 num = int(val)
65 if str(num) != val:
73 if str(num) != val:
66 raise ValueError
74 raise ValueError
67 if num < 0:
75 if num < 0:
68 num += revcount
76 num += revcount
69 if not (0 <= num < revcount):
77 if not (0 <= num < revcount):
70 raise ValueError
78 raise ValueError
71 except ValueError:
79 except ValueError:
72 try:
80 try:
73 num = repo.changelog.rev(repo.lookup(val))
81 num = repo.changelog.rev(repo.lookup(val))
74 except KeyError:
82 except KeyError:
75 try:
83 try:
76 num = revlog.rev(revlog.lookup(val))
84 num = revlog.rev(revlog.lookup(val))
77 except KeyError:
85 except KeyError:
78 raise Abort('invalid revision identifier %s', val)
86 raise Abort('invalid revision identifier %s', val)
79 return num
87 return num
80 for spec in revs:
88 for spec in revs:
81 if spec.find(revrangesep) >= 0:
89 if spec.find(revrangesep) >= 0:
82 start, end = spec.split(revrangesep, 1)
90 start, end = spec.split(revrangesep, 1)
83 start = fix(start, 0)
91 start = fix(start, 0)
84 end = fix(end, revcount - 1)
92 end = fix(end, revcount - 1)
85 if end > start:
93 if end > start:
86 end += 1
94 end += 1
87 step = 1
95 step = 1
88 else:
96 else:
89 end -= 1
97 end -= 1
90 step = -1
98 step = -1
91 for rev in xrange(start, end, step):
99 for rev in xrange(start, end, step):
92 yield str(rev)
100 yield str(rev)
93 else:
101 else:
94 yield spec
102 yield spec
95
103
96 def make_filename(repo, r, pat, node=None,
104 def make_filename(repo, r, pat, node=None,
97 total=None, seqno=None, revwidth=None):
105 total=None, seqno=None, revwidth=None):
98 node_expander = {
106 node_expander = {
99 'H': lambda: hg.hex(node),
107 'H': lambda: hg.hex(node),
100 'R': lambda: str(r.rev(node)),
108 'R': lambda: str(r.rev(node)),
101 'h': lambda: hg.short(node),
109 'h': lambda: hg.short(node),
102 }
110 }
103 expander = {
111 expander = {
104 '%': lambda: '%',
112 '%': lambda: '%',
105 'b': lambda: os.path.basename(repo.root),
113 'b': lambda: os.path.basename(repo.root),
106 }
114 }
107
115
108 try:
116 try:
109 if node:
117 if node:
110 expander.update(node_expander)
118 expander.update(node_expander)
111 if node and revwidth is not None:
119 if node and revwidth is not None:
112 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
120 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
113 if total is not None:
121 if total is not None:
114 expander['N'] = lambda: str(total)
122 expander['N'] = lambda: str(total)
115 if seqno is not None:
123 if seqno is not None:
116 expander['n'] = lambda: str(seqno)
124 expander['n'] = lambda: str(seqno)
117 if total is not None and seqno is not None:
125 if total is not None and seqno is not None:
118 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
126 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
119
127
120 newname = []
128 newname = []
121 patlen = len(pat)
129 patlen = len(pat)
122 i = 0
130 i = 0
123 while i < patlen:
131 while i < patlen:
124 c = pat[i]
132 c = pat[i]
125 if c == '%':
133 if c == '%':
126 i += 1
134 i += 1
127 c = pat[i]
135 c = pat[i]
128 c = expander[c]()
136 c = expander[c]()
129 newname.append(c)
137 newname.append(c)
130 i += 1
138 i += 1
131 return ''.join(newname)
139 return ''.join(newname)
132 except KeyError, inst:
140 except KeyError, inst:
133 raise Abort("invalid format spec '%%%s' in output file name",
141 raise Abort("invalid format spec '%%%s' in output file name",
134 inst.args[0])
142 inst.args[0])
135
143
136 def make_file(repo, r, pat, node=None,
144 def make_file(repo, r, pat, node=None,
137 total=None, seqno=None, revwidth=None, mode='wb'):
145 total=None, seqno=None, revwidth=None, mode='wb'):
138 if not pat or pat == '-':
146 if not pat or pat == '-':
139 if 'w' in mode: return sys.stdout
147 if 'w' in mode: return sys.stdout
140 else: return sys.stdin
148 else: return sys.stdin
141 if hasattr(pat, 'write') and 'w' in mode:
149 if hasattr(pat, 'write') and 'w' in mode:
142 return pat
150 return pat
143 if hasattr(pat, 'read') and 'r' in mode:
151 if hasattr(pat, 'read') and 'r' in mode:
144 return pat
152 return pat
145 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
153 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
146 mode)
154 mode)
147
155
148 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
156 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
149 def date(c):
157 def date(c):
150 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
158 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
151
159
152 (c, a, d, u) = repo.changes(node1, node2, files)
160 (c, a, d, u) = repo.changes(node1, node2, files)
153 if files:
161 if files:
154 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
162 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
155
163
156 if not c and not a and not d:
164 if not c and not a and not d:
157 return
165 return
158
166
159 if node2:
167 if node2:
160 change = repo.changelog.read(node2)
168 change = repo.changelog.read(node2)
161 mmap2 = repo.manifest.read(change[0])
169 mmap2 = repo.manifest.read(change[0])
162 date2 = date(change)
170 date2 = date(change)
163 def read(f):
171 def read(f):
164 return repo.file(f).read(mmap2[f])
172 return repo.file(f).read(mmap2[f])
165 else:
173 else:
166 date2 = time.asctime()
174 date2 = time.asctime()
167 if not node1:
175 if not node1:
168 node1 = repo.dirstate.parents()[0]
176 node1 = repo.dirstate.parents()[0]
169 def read(f):
177 def read(f):
170 return repo.wfile(f).read()
178 return repo.wfile(f).read()
171
179
172 if ui.quiet:
180 if ui.quiet:
173 r = None
181 r = None
174 else:
182 else:
175 hexfunc = ui.verbose and hg.hex or hg.short
183 hexfunc = ui.verbose and hg.hex or hg.short
176 r = [hexfunc(node) for node in [node1, node2] if node]
184 r = [hexfunc(node) for node in [node1, node2] if node]
177
185
178 change = repo.changelog.read(node1)
186 change = repo.changelog.read(node1)
179 mmap = repo.manifest.read(change[0])
187 mmap = repo.manifest.read(change[0])
180 date1 = date(change)
188 date1 = date(change)
181
189
182 for f in c:
190 for f in c:
183 to = None
191 to = None
184 if f in mmap:
192 if f in mmap:
185 to = repo.file(f).read(mmap[f])
193 to = repo.file(f).read(mmap[f])
186 tn = read(f)
194 tn = read(f)
187 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
195 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
188 for f in a:
196 for f in a:
189 to = None
197 to = None
190 tn = read(f)
198 tn = read(f)
191 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
199 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
192 for f in d:
200 for f in d:
193 to = repo.file(f).read(mmap[f])
201 to = repo.file(f).read(mmap[f])
194 tn = None
202 tn = None
195 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
203 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
196
204
197 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
205 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
198 """show a single changeset or file revision"""
206 """show a single changeset or file revision"""
199 changelog = repo.changelog
207 changelog = repo.changelog
200 if filelog:
208 if filelog:
201 log = filelog
209 log = filelog
202 filerev = rev
210 filerev = rev
203 node = filenode = filelog.node(filerev)
211 node = filenode = filelog.node(filerev)
204 changerev = filelog.linkrev(filenode)
212 changerev = filelog.linkrev(filenode)
205 changenode = changenode or changelog.node(changerev)
213 changenode = changenode or changelog.node(changerev)
206 else:
214 else:
207 log = changelog
215 log = changelog
208 changerev = rev
216 changerev = rev
209 if changenode is None:
217 if changenode is None:
210 changenode = changelog.node(changerev)
218 changenode = changelog.node(changerev)
211 elif not changerev:
219 elif not changerev:
212 rev = changerev = changelog.rev(changenode)
220 rev = changerev = changelog.rev(changenode)
213 node = changenode
221 node = changenode
214
222
215 if ui.quiet:
223 if ui.quiet:
216 ui.write("%d:%s\n" % (rev, hg.hex(node)))
224 ui.write("%d:%s\n" % (rev, hg.hex(node)))
217 return
225 return
218
226
219 changes = changelog.read(changenode)
227 changes = changelog.read(changenode)
220
228
221 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
229 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
222 for p in log.parents(node)
230 for p in log.parents(node)
223 if ui.debugflag or p != hg.nullid]
231 if ui.debugflag or p != hg.nullid]
224 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
232 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
225 parents = []
233 parents = []
226
234
227 if ui.verbose:
235 if ui.verbose:
228 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
236 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
229 else:
237 else:
230 ui.write("changeset: %d:%s\n" % (changerev, hg.short(changenode)))
238 ui.write("changeset: %d:%s\n" % (changerev, hg.short(changenode)))
231
239
232 for tag in repo.nodetags(changenode):
240 for tag in repo.nodetags(changenode):
233 ui.status("tag: %s\n" % tag)
241 ui.status("tag: %s\n" % tag)
234 for parent in parents:
242 for parent in parents:
235 ui.write("parent: %d:%s\n" % parent)
243 ui.write("parent: %d:%s\n" % parent)
236 if filelog:
244 if filelog:
237 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
245 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
238
246
239 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
247 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
240 hg.hex(changes[0])))
248 hg.hex(changes[0])))
241 ui.status("user: %s\n" % changes[1])
249 ui.status("user: %s\n" % changes[1])
242 ui.status("date: %s\n" % time.asctime(
250 ui.status("date: %s\n" % time.asctime(
243 time.localtime(float(changes[2].split(' ')[0]))))
251 time.localtime(float(changes[2].split(' ')[0]))))
244
252
245 if ui.debugflag:
253 if ui.debugflag:
246 files = repo.changes(changelog.parents(changenode)[0], changenode)
254 files = repo.changes(changelog.parents(changenode)[0], changenode)
247 for key, value in zip(["files:", "files+:", "files-:"], files):
255 for key, value in zip(["files:", "files+:", "files-:"], files):
248 if value:
256 if value:
249 ui.note("%-12s %s\n" % (key, " ".join(value)))
257 ui.note("%-12s %s\n" % (key, " ".join(value)))
250 else:
258 else:
251 ui.note("files: %s\n" % " ".join(changes[3]))
259 ui.note("files: %s\n" % " ".join(changes[3]))
252
260
253 description = changes[4].strip()
261 description = changes[4].strip()
254 if description:
262 if description:
255 if ui.verbose:
263 if ui.verbose:
256 ui.status("description:\n")
264 ui.status("description:\n")
257 ui.status(description)
265 ui.status(description)
258 ui.status("\n\n")
266 ui.status("\n\n")
259 else:
267 else:
260 ui.status("summary: %s\n" % description.splitlines()[0])
268 ui.status("summary: %s\n" % description.splitlines()[0])
261 ui.status("\n")
269 ui.status("\n")
262
270
263 def show_version(ui):
271 def show_version(ui):
264 """output version and copyright information"""
272 """output version and copyright information"""
265 ui.write("Mercurial version %s\n" % version.get_version())
273 ui.write("Mercurial version %s\n" % version.get_version())
266 ui.status(
274 ui.status(
267 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
275 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
268 "This is free software; see the source for copying conditions. "
276 "This is free software; see the source for copying conditions. "
269 "There is NO\nwarranty; "
277 "There is NO\nwarranty; "
270 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
278 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
271 )
279 )
272
280
273 def help_(ui, cmd=None):
281 def help_(ui, cmd=None):
274 """show help for a given command or all commands"""
282 """show help for a given command or all commands"""
275 if cmd:
283 if cmd:
276 try:
284 try:
277 i = find(cmd)
285 i = find(cmd)
278 ui.write("%s\n\n" % i[2])
286 ui.write("%s\n\n" % i[2])
279
287
280 if i[1]:
288 if i[1]:
281 for s, l, d, c in i[1]:
289 for s, l, d, c in i[1]:
282 opt = ' '
290 opt = ' '
283 if s:
291 if s:
284 opt = opt + '-' + s + ' '
292 opt = opt + '-' + s + ' '
285 if l:
293 if l:
286 opt = opt + '--' + l + ' '
294 opt = opt + '--' + l + ' '
287 if d:
295 if d:
288 opt = opt + '(' + str(d) + ')'
296 opt = opt + '(' + str(d) + ')'
289 ui.write(opt, "\n")
297 ui.write(opt, "\n")
290 if c:
298 if c:
291 ui.write(' %s\n' % c)
299 ui.write(' %s\n' % c)
292 ui.write("\n")
300 ui.write("\n")
293
301
294 ui.write(i[0].__doc__, "\n")
302 ui.write(i[0].__doc__, "\n")
295 except UnknownCommand:
303 except UnknownCommand:
296 ui.warn("hg: unknown command %s\n" % cmd)
304 ui.warn("hg: unknown command %s\n" % cmd)
297 sys.exit(0)
305 sys.exit(0)
298 else:
306 else:
299 if ui.verbose:
307 if ui.verbose:
300 show_version(ui)
308 show_version(ui)
301 ui.write('\n')
309 ui.write('\n')
302 if ui.verbose:
310 if ui.verbose:
303 ui.write('hg commands:\n\n')
311 ui.write('hg commands:\n\n')
304 else:
312 else:
305 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
313 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
306
314
307 h = {}
315 h = {}
308 for c, e in table.items():
316 for c, e in table.items():
309 f = c.split("|")[0]
317 f = c.split("|")[0]
310 if not ui.verbose and not f.startswith("^"):
318 if not ui.verbose and not f.startswith("^"):
311 continue
319 continue
312 if not ui.debugflag and f.startswith("debug"):
320 if not ui.debugflag and f.startswith("debug"):
313 continue
321 continue
314 f = f.lstrip("^")
322 f = f.lstrip("^")
315 d = ""
323 d = ""
316 if e[0].__doc__:
324 if e[0].__doc__:
317 d = e[0].__doc__.splitlines(0)[0].rstrip()
325 d = e[0].__doc__.splitlines(0)[0].rstrip()
318 h[f] = d
326 h[f] = d
319
327
320 fns = h.keys()
328 fns = h.keys()
321 fns.sort()
329 fns.sort()
322 m = max(map(len, fns))
330 m = max(map(len, fns))
323 for f in fns:
331 for f in fns:
324 ui.write(' %-*s %s\n' % (m, f, h[f]))
332 ui.write(' %-*s %s\n' % (m, f, h[f]))
325
333
326 # Commands start here, listed alphabetically
334 # Commands start here, listed alphabetically
327
335
328 def add(ui, repo, *pats, **opts):
336 def add(ui, repo, *pats, **opts):
329 '''add the specified files on the next commit'''
337 '''add the specified files on the next commit'''
330 names = []
338 names = []
331 q = dict(zip(pats, pats))
339 q = dict(zip(pats, pats))
332 for src, abs, rel in walk(repo, pats, opts):
340 for src, abs, rel in walk(repo, pats, opts):
333 if rel in q or abs in q:
341 if rel in q or abs in q:
334 names.append(abs)
342 names.append(abs)
335 elif repo.dirstate.state(abs) == '?':
343 elif repo.dirstate.state(abs) == '?':
336 ui.status('adding %s\n' % rel)
344 ui.status('adding %s\n' % rel)
337 names.append(abs)
345 names.append(abs)
338 repo.add(names)
346 repo.add(names)
339
347
340 def addremove(ui, repo, *pats, **opts):
348 def addremove(ui, repo, *pats, **opts):
341 """add all new files, delete all missing files"""
349 """add all new files, delete all missing files"""
342 q = dict(zip(pats, pats))
350 q = dict(zip(pats, pats))
343 add, remove = [], []
351 add, remove = [], []
344 for src, abs, rel in walk(repo, pats, opts):
352 for src, abs, rel in walk(repo, pats, opts):
345 if src == 'f':
353 if src == 'f':
346 if repo.dirstate.state(abs) == '?':
354 if repo.dirstate.state(abs) == '?':
347 add.append(abs)
355 add.append(abs)
348 if rel not in q: ui.status('adding ', rel, '\n')
356 if rel not in q: ui.status('adding ', rel, '\n')
349 elif repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
357 elif repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
350 remove.append(abs)
358 remove.append(abs)
351 if rel not in q: ui.status('removing ', rel, '\n')
359 if rel not in q: ui.status('removing ', rel, '\n')
352 repo.add(add)
360 repo.add(add)
353 repo.remove(remove)
361 repo.remove(remove)
354
362
355 def annotate(ui, repo, *pats, **opts):
363 def annotate(ui, repo, *pats, **opts):
356 """show changeset information per file line"""
364 """show changeset information per file line"""
357 def getnode(rev):
365 def getnode(rev):
358 return hg.short(repo.changelog.node(rev))
366 return hg.short(repo.changelog.node(rev))
359
367
360 def getname(rev):
368 def getname(rev):
361 try:
369 try:
362 return bcache[rev]
370 return bcache[rev]
363 except KeyError:
371 except KeyError:
364 cl = repo.changelog.read(repo.changelog.node(rev))
372 cl = repo.changelog.read(repo.changelog.node(rev))
365 name = cl[1]
373 name = cl[1]
366 f = name.find('@')
374 f = name.find('@')
367 if f >= 0:
375 if f >= 0:
368 name = name[:f]
376 name = name[:f]
369 f = name.find('<')
377 f = name.find('<')
370 if f >= 0:
378 if f >= 0:
371 name = name[f+1:]
379 name = name[f+1:]
372 bcache[rev] = name
380 bcache[rev] = name
373 return name
381 return name
374
382
375 if not pats:
383 if not pats:
376 raise Abort('at least one file name or pattern required')
384 raise Abort('at least one file name or pattern required')
377
385
378 bcache = {}
386 bcache = {}
379 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
387 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
380 if not opts['user'] and not opts['changeset']:
388 if not opts['user'] and not opts['changeset']:
381 opts['number'] = 1
389 opts['number'] = 1
382
390
383 if opts['rev']:
391 if opts['rev']:
384 node = repo.changelog.lookup(opts['rev'])
392 node = repo.changelog.lookup(opts['rev'])
385 else:
393 else:
386 node = repo.dirstate.parents()[0]
394 node = repo.dirstate.parents()[0]
387 change = repo.changelog.read(node)
395 change = repo.changelog.read(node)
388 mmap = repo.manifest.read(change[0])
396 mmap = repo.manifest.read(change[0])
389 for src, abs, rel in walk(repo, pats, opts):
397 for src, abs, rel in walk(repo, pats, opts):
390 if abs not in mmap:
398 if abs not in mmap:
391 ui.warn("warning: %s is not in the repository!\n" % rel)
399 ui.warn("warning: %s is not in the repository!\n" % rel)
392 continue
400 continue
393
401
394 lines = repo.file(abs).annotate(mmap[abs])
402 lines = repo.file(abs).annotate(mmap[abs])
395 pieces = []
403 pieces = []
396
404
397 for o, f in opmap:
405 for o, f in opmap:
398 if opts[o]:
406 if opts[o]:
399 l = [f(n) for n, dummy in lines]
407 l = [f(n) for n, dummy in lines]
400 if l:
408 if l:
401 m = max(map(len, l))
409 m = max(map(len, l))
402 pieces.append(["%*s" % (m, x) for x in l])
410 pieces.append(["%*s" % (m, x) for x in l])
403
411
404 if pieces:
412 if pieces:
405 for p, l in zip(zip(*pieces), lines):
413 for p, l in zip(zip(*pieces), lines):
406 ui.write("%s: %s" % (" ".join(p), l[1]))
414 ui.write("%s: %s" % (" ".join(p), l[1]))
407
415
408 def cat(ui, repo, file1, rev=None, **opts):
416 def cat(ui, repo, file1, rev=None, **opts):
409 """output the latest or given revision of a file"""
417 """output the latest or given revision of a file"""
410 r = repo.file(relpath(repo, [file1])[0])
418 r = repo.file(relpath(repo, [file1])[0])
411 if rev:
419 if rev:
412 n = r.lookup(rev)
420 n = r.lookup(rev)
413 else:
421 else:
414 n = r.tip()
422 n = r.tip()
415 fp = make_file(repo, r, opts['output'], node=n)
423 fp = make_file(repo, r, opts['output'], node=n)
416 fp.write(r.read(n))
424 fp.write(r.read(n))
417
425
418 def clone(ui, source, dest=None, **opts):
426 def clone(ui, source, dest=None, **opts):
419 """make a copy of an existing repository"""
427 """make a copy of an existing repository"""
420 if dest is None:
428 if dest is None:
421 dest = os.path.basename(os.path.normpath(source))
429 dest = os.path.basename(os.path.normpath(source))
422
430
423 if os.path.exists(dest):
431 if os.path.exists(dest):
424 ui.warn("abort: destination '%s' already exists\n" % dest)
432 ui.warn("abort: destination '%s' already exists\n" % dest)
425 return 1
433 return 1
426
434
427 class Dircleanup:
435 class Dircleanup:
428 def __init__(self, dir_):
436 def __init__(self, dir_):
429 self.rmtree = shutil.rmtree
437 self.rmtree = shutil.rmtree
430 self.dir_ = dir_
438 self.dir_ = dir_
431 os.mkdir(dir_)
439 os.mkdir(dir_)
432 def close(self):
440 def close(self):
433 self.dir_ = None
441 self.dir_ = None
434 def __del__(self):
442 def __del__(self):
435 if self.dir_:
443 if self.dir_:
436 self.rmtree(self.dir_, True)
444 self.rmtree(self.dir_, True)
437
445
438 d = Dircleanup(dest)
446 d = Dircleanup(dest)
439 abspath = source
447 abspath = source
440 source = ui.expandpath(source)
448 source = ui.expandpath(source)
441 other = hg.repository(ui, source)
449 other = hg.repository(ui, source)
442
450
443 if other.dev() != -1:
451 if other.dev() != -1:
444 abspath = os.path.abspath(source)
452 abspath = os.path.abspath(source)
445 copyfile = (os.stat(dest).st_dev == other.dev()
453 copyfile = (os.stat(dest).st_dev == other.dev()
446 and getattr(os, 'link', None) or shutil.copy2)
454 and getattr(os, 'link', None) or shutil.copy2)
447 if copyfile is not shutil.copy2:
455 if copyfile is not shutil.copy2:
448 ui.note("cloning by hardlink\n")
456 ui.note("cloning by hardlink\n")
449 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
457 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
450 copyfile)
458 copyfile)
451 try:
459 try:
452 os.unlink(os.path.join(dest, ".hg", "dirstate"))
460 os.unlink(os.path.join(dest, ".hg", "dirstate"))
453 except OSError:
461 except OSError:
454 pass
462 pass
455
463
456 repo = hg.repository(ui, dest)
464 repo = hg.repository(ui, dest)
457
465
458 else:
466 else:
459 repo = hg.repository(ui, dest, create=1)
467 repo = hg.repository(ui, dest, create=1)
460 repo.pull(other)
468 repo.pull(other)
461
469
462 f = repo.opener("hgrc", "w")
470 f = repo.opener("hgrc", "w")
463 f.write("[paths]\n")
471 f.write("[paths]\n")
464 f.write("default = %s\n" % abspath)
472 f.write("default = %s\n" % abspath)
465
473
466 if not opts['noupdate']:
474 if not opts['noupdate']:
467 update(ui, repo)
475 update(ui, repo)
468
476
469 d.close()
477 d.close()
470
478
471 def commit(ui, repo, *pats, **opts):
479 def commit(ui, repo, *pats, **opts):
472 """commit the specified files or all outstanding changes"""
480 """commit the specified files or all outstanding changes"""
473 if opts['text']:
481 if opts['text']:
474 ui.warn("Warning: -t and --text is deprecated,"
482 ui.warn("Warning: -t and --text is deprecated,"
475 " please use -m or --message instead.\n")
483 " please use -m or --message instead.\n")
476 message = opts['message'] or opts['text']
484 message = opts['message'] or opts['text']
477 logfile = opts['logfile']
485 logfile = opts['logfile']
478 if not message and logfile:
486 if not message and logfile:
479 try:
487 try:
480 message = open(logfile).read()
488 message = open(logfile).read()
481 except IOError, why:
489 except IOError, why:
482 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
490 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
483
491
484 if opts['addremove']:
492 if opts['addremove']:
485 addremove(ui, repo, *pats, **opts)
493 addremove(ui, repo, *pats, **opts)
486 cwd = repo.getcwd()
494 cwd = repo.getcwd()
487 if not pats and cwd:
495 if not pats and cwd:
488 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
496 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
489 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
497 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
490 fns, match = matchpats((pats and repo.getcwd()) or '', pats, opts)
498 fns, match = matchpats((pats and repo.getcwd()) or '', pats, opts)
491 if pats:
499 if pats:
492 c, a, d, u = repo.changes(files = fns, match = match)
500 c, a, d, u = repo.changes(files = fns, match = match)
493 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
501 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
494 else:
502 else:
495 files = []
503 files = []
496 repo.commit(files, message, opts['user'], opts['date'], match)
504 repo.commit(files, message, opts['user'], opts['date'], match)
497
505
498 def copy(ui, repo, source, dest):
506 def copy(ui, repo, source, dest):
499 """mark a file as copied or renamed for the next commit"""
507 """mark a file as copied or renamed for the next commit"""
500 return repo.copy(*relpath(repo, (source, dest)))
508 return repo.copy(*relpath(repo, (source, dest)))
501
509
502 def debugcheckstate(ui, repo):
510 def debugcheckstate(ui, repo):
503 """validate the correctness of the current dirstate"""
511 """validate the correctness of the current dirstate"""
504 parent1, parent2 = repo.dirstate.parents()
512 parent1, parent2 = repo.dirstate.parents()
505 repo.dirstate.read()
513 repo.dirstate.read()
506 dc = repo.dirstate.map
514 dc = repo.dirstate.map
507 keys = dc.keys()
515 keys = dc.keys()
508 keys.sort()
516 keys.sort()
509 m1n = repo.changelog.read(parent1)[0]
517 m1n = repo.changelog.read(parent1)[0]
510 m2n = repo.changelog.read(parent2)[0]
518 m2n = repo.changelog.read(parent2)[0]
511 m1 = repo.manifest.read(m1n)
519 m1 = repo.manifest.read(m1n)
512 m2 = repo.manifest.read(m2n)
520 m2 = repo.manifest.read(m2n)
513 errors = 0
521 errors = 0
514 for f in dc:
522 for f in dc:
515 state = repo.dirstate.state(f)
523 state = repo.dirstate.state(f)
516 if state in "nr" and f not in m1:
524 if state in "nr" and f not in m1:
517 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
525 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
518 errors += 1
526 errors += 1
519 if state in "a" and f in m1:
527 if state in "a" and f in m1:
520 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
528 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
521 errors += 1
529 errors += 1
522 if state in "m" and f not in m1 and f not in m2:
530 if state in "m" and f not in m1 and f not in m2:
523 ui.warn("%s in state %s, but not in either manifest\n" %
531 ui.warn("%s in state %s, but not in either manifest\n" %
524 (f, state))
532 (f, state))
525 errors += 1
533 errors += 1
526 for f in m1:
534 for f in m1:
527 state = repo.dirstate.state(f)
535 state = repo.dirstate.state(f)
528 if state not in "nrm":
536 if state not in "nrm":
529 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
537 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
530 errors += 1
538 errors += 1
531 if errors:
539 if errors:
532 raise Abort(".hg/dirstate inconsistent with current parent's manifest")
540 raise Abort(".hg/dirstate inconsistent with current parent's manifest")
533
541
534 def debugstate(ui, repo):
542 def debugstate(ui, repo):
535 """show the contents of the current dirstate"""
543 """show the contents of the current dirstate"""
536 repo.dirstate.read()
544 repo.dirstate.read()
537 dc = repo.dirstate.map
545 dc = repo.dirstate.map
538 keys = dc.keys()
546 keys = dc.keys()
539 keys.sort()
547 keys.sort()
540 for file_ in keys:
548 for file_ in keys:
541 ui.write("%c %3o %10d %s %s\n"
549 ui.write("%c %3o %10d %s %s\n"
542 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
550 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
543 time.strftime("%x %X",
551 time.strftime("%x %X",
544 time.localtime(dc[file_][3])), file_))
552 time.localtime(dc[file_][3])), file_))
545
553
546 def debugindex(ui, file_):
554 def debugindex(ui, file_):
547 """dump the contents of an index file"""
555 """dump the contents of an index file"""
548 r = hg.revlog(hg.opener(""), file_, "")
556 r = hg.revlog(hg.opener(""), file_, "")
549 ui.write(" rev offset length base linkrev" +
557 ui.write(" rev offset length base linkrev" +
550 " p1 p2 nodeid\n")
558 " p1 p2 nodeid\n")
551 for i in range(r.count()):
559 for i in range(r.count()):
552 e = r.index[i]
560 e = r.index[i]
553 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
561 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
554 i, e[0], e[1], e[2], e[3],
562 i, e[0], e[1], e[2], e[3],
555 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
563 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
556
564
557 def debugindexdot(ui, file_):
565 def debugindexdot(ui, file_):
558 """dump an index DAG as a .dot file"""
566 """dump an index DAG as a .dot file"""
559 r = hg.revlog(hg.opener(""), file_, "")
567 r = hg.revlog(hg.opener(""), file_, "")
560 ui.write("digraph G {\n")
568 ui.write("digraph G {\n")
561 for i in range(r.count()):
569 for i in range(r.count()):
562 e = r.index[i]
570 e = r.index[i]
563 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
571 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
564 if e[5] != hg.nullid:
572 if e[5] != hg.nullid:
565 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
573 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
566 ui.write("}\n")
574 ui.write("}\n")
567
575
576 def debugwalk(ui, repo, *pats, **opts):
577 items = list(walk(repo, pats, opts))
578 fmt = '%%s %%-%ds %%s' % max([len(abs) for (src, abs, rel) in items])
579 for i in items: print fmt % i
580
568 def diff(ui, repo, *pats, **opts):
581 def diff(ui, repo, *pats, **opts):
569 """diff working directory (or selected files)"""
582 """diff working directory (or selected files)"""
570 revs = []
583 revs = []
571 if opts['rev']:
584 if opts['rev']:
572 revs = map(lambda x: repo.lookup(x), opts['rev'])
585 revs = map(lambda x: repo.lookup(x), opts['rev'])
573
586
574 if len(revs) > 2:
587 if len(revs) > 2:
575 raise Abort("too many revisions to diff")
588 raise Abort("too many revisions to diff")
576
589
577 files = []
590 files = []
578 for src, abs, rel in walk(repo, pats, opts):
591 for src, abs, rel in walk(repo, pats, opts):
579 files.append(abs)
592 files.append(abs)
580 dodiff(sys.stdout, ui, repo, files, *revs)
593 dodiff(sys.stdout, ui, repo, files, *revs)
581
594
582 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
595 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
583 node = repo.lookup(changeset)
596 node = repo.lookup(changeset)
584 prev, other = repo.changelog.parents(node)
597 prev, other = repo.changelog.parents(node)
585 change = repo.changelog.read(node)
598 change = repo.changelog.read(node)
586
599
587 fp = make_file(repo, repo.changelog, opts['output'],
600 fp = make_file(repo, repo.changelog, opts['output'],
588 node=node, total=total, seqno=seqno,
601 node=node, total=total, seqno=seqno,
589 revwidth=revwidth)
602 revwidth=revwidth)
590 if fp != sys.stdout:
603 if fp != sys.stdout:
591 ui.note("%s\n" % fp.name)
604 ui.note("%s\n" % fp.name)
592
605
593 fp.write("# HG changeset patch\n")
606 fp.write("# HG changeset patch\n")
594 fp.write("# User %s\n" % change[1])
607 fp.write("# User %s\n" % change[1])
595 fp.write("# Node ID %s\n" % hg.hex(node))
608 fp.write("# Node ID %s\n" % hg.hex(node))
596 fp.write("# Parent %s\n" % hg.hex(prev))
609 fp.write("# Parent %s\n" % hg.hex(prev))
597 if other != hg.nullid:
610 if other != hg.nullid:
598 fp.write("# Parent %s\n" % hg.hex(other))
611 fp.write("# Parent %s\n" % hg.hex(other))
599 fp.write(change[4].rstrip())
612 fp.write(change[4].rstrip())
600 fp.write("\n\n")
613 fp.write("\n\n")
601
614
602 dodiff(fp, ui, repo, None, prev, node)
615 dodiff(fp, ui, repo, None, prev, node)
603 if fp != sys.stdout: fp.close()
616 if fp != sys.stdout: fp.close()
604
617
605 def export(ui, repo, *changesets, **opts):
618 def export(ui, repo, *changesets, **opts):
606 """dump the header and diffs for one or more changesets"""
619 """dump the header and diffs for one or more changesets"""
607 if not changesets:
620 if not changesets:
608 raise Abort("export requires at least one changeset")
621 raise Abort("export requires at least one changeset")
609 seqno = 0
622 seqno = 0
610 revs = list(revrange(ui, repo, changesets))
623 revs = list(revrange(ui, repo, changesets))
611 total = len(revs)
624 total = len(revs)
612 revwidth = max(len(revs[0]), len(revs[-1]))
625 revwidth = max(len(revs[0]), len(revs[-1]))
613 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
626 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
614 for cset in revs:
627 for cset in revs:
615 seqno += 1
628 seqno += 1
616 doexport(ui, repo, cset, seqno, total, revwidth, opts)
629 doexport(ui, repo, cset, seqno, total, revwidth, opts)
617
630
618 def forget(ui, repo, *pats, **opts):
631 def forget(ui, repo, *pats, **opts):
619 """don't add the specified files on the next commit"""
632 """don't add the specified files on the next commit"""
620 q = dict(zip(pats, pats))
633 q = dict(zip(pats, pats))
621 forget = []
634 forget = []
622 for src, abs, rel in walk(repo, pats, opts):
635 for src, abs, rel in walk(repo, pats, opts):
623 if repo.dirstate.state(abs) == 'a':
636 if repo.dirstate.state(abs) == 'a':
624 forget.append(abs)
637 forget.append(abs)
625 if rel not in q: ui.status('forgetting ', rel, '\n')
638 if rel not in q: ui.status('forgetting ', rel, '\n')
626 repo.forget(forget)
639 repo.forget(forget)
627
640
628 def heads(ui, repo):
641 def heads(ui, repo):
629 """show current repository heads"""
642 """show current repository heads"""
630 for n in repo.changelog.heads():
643 for n in repo.changelog.heads():
631 show_changeset(ui, repo, changenode=n)
644 show_changeset(ui, repo, changenode=n)
632
645
633 def identify(ui, repo):
646 def identify(ui, repo):
634 """print information about the working copy"""
647 """print information about the working copy"""
635 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
648 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
636 if not parents:
649 if not parents:
637 ui.write("unknown\n")
650 ui.write("unknown\n")
638 return
651 return
639
652
640 hexfunc = ui.verbose and hg.hex or hg.short
653 hexfunc = ui.verbose and hg.hex or hg.short
641 (c, a, d, u) = repo.changes()
654 (c, a, d, u) = repo.changes()
642 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
655 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
643 (c or a or d) and "+" or "")]
656 (c or a or d) and "+" or "")]
644
657
645 if not ui.quiet:
658 if not ui.quiet:
646 # multiple tags for a single parent separated by '/'
659 # multiple tags for a single parent separated by '/'
647 parenttags = ['/'.join(tags)
660 parenttags = ['/'.join(tags)
648 for tags in map(repo.nodetags, parents) if tags]
661 for tags in map(repo.nodetags, parents) if tags]
649 # tags for multiple parents separated by ' + '
662 # tags for multiple parents separated by ' + '
650 if parenttags:
663 if parenttags:
651 output.append(' + '.join(parenttags))
664 output.append(' + '.join(parenttags))
652
665
653 ui.write("%s\n" % ' '.join(output))
666 ui.write("%s\n" % ' '.join(output))
654
667
655 def import_(ui, repo, patch1, *patches, **opts):
668 def import_(ui, repo, patch1, *patches, **opts):
656 """import an ordered set of patches"""
669 """import an ordered set of patches"""
657 patches = (patch1,) + patches
670 patches = (patch1,) + patches
658
671
659 d = opts["base"]
672 d = opts["base"]
660 strip = opts["strip"]
673 strip = opts["strip"]
661
674
662 for patch in patches:
675 for patch in patches:
663 ui.status("applying %s\n" % patch)
676 ui.status("applying %s\n" % patch)
664 pf = os.path.join(d, patch)
677 pf = os.path.join(d, patch)
665
678
666 message = []
679 message = []
667 user = None
680 user = None
668 hgpatch = False
681 hgpatch = False
669 for line in file(pf):
682 for line in file(pf):
670 line = line.rstrip()
683 line = line.rstrip()
671 if line.startswith("--- ") or line.startswith("diff -r"):
684 if line.startswith("--- ") or line.startswith("diff -r"):
672 break
685 break
673 elif hgpatch:
686 elif hgpatch:
674 # parse values when importing the result of an hg export
687 # parse values when importing the result of an hg export
675 if line.startswith("# User "):
688 if line.startswith("# User "):
676 user = line[7:]
689 user = line[7:]
677 ui.debug('User: %s\n' % user)
690 ui.debug('User: %s\n' % user)
678 elif not line.startswith("# ") and line:
691 elif not line.startswith("# ") and line:
679 message.append(line)
692 message.append(line)
680 hgpatch = False
693 hgpatch = False
681 elif line == '# HG changeset patch':
694 elif line == '# HG changeset patch':
682 hgpatch = True
695 hgpatch = True
683 else:
696 else:
684 message.append(line)
697 message.append(line)
685
698
686 # make sure message isn't empty
699 # make sure message isn't empty
687 if not message:
700 if not message:
688 message = "imported patch %s\n" % patch
701 message = "imported patch %s\n" % patch
689 else:
702 else:
690 message = "%s\n" % '\n'.join(message)
703 message = "%s\n" % '\n'.join(message)
691 ui.debug('message:\n%s\n' % message)
704 ui.debug('message:\n%s\n' % message)
692
705
693 f = os.popen("patch -p%d < %s" % (strip, pf))
706 f = os.popen("patch -p%d < %s" % (strip, pf))
694 files = []
707 files = []
695 for l in f.read().splitlines():
708 for l in f.read().splitlines():
696 l.rstrip('\r\n');
709 l.rstrip('\r\n');
697 ui.status("%s\n" % l)
710 ui.status("%s\n" % l)
698 if l.startswith('patching file '):
711 if l.startswith('patching file '):
699 pf = l[14:]
712 pf = l[14:]
700 if pf not in files:
713 if pf not in files:
701 files.append(pf)
714 files.append(pf)
702 patcherr = f.close()
715 patcherr = f.close()
703 if patcherr:
716 if patcherr:
704 raise Abort("patch failed")
717 raise Abort("patch failed")
705
718
706 if len(files) > 0:
719 if len(files) > 0:
707 addremove(ui, repo, *files)
720 addremove(ui, repo, *files)
708 repo.commit(files, message, user)
721 repo.commit(files, message, user)
709
722
710 def init(ui, source=None):
723 def init(ui, source=None):
711 """create a new repository in the current directory"""
724 """create a new repository in the current directory"""
712
725
713 if source:
726 if source:
714 raise Abort("no longer supported: use \"hg clone\" instead")
727 raise Abort("no longer supported: use \"hg clone\" instead")
715 hg.repository(ui, ".", create=1)
728 hg.repository(ui, ".", create=1)
716
729
717 def locate(ui, repo, *pats, **opts):
730 def locate(ui, repo, *pats, **opts):
718 """locate files matching specific patterns"""
731 """locate files matching specific patterns"""
719 end = '\n'
732 end = '\n'
720 if opts['print0']: end = '\0'
733 if opts['print0']: end = '\0'
721
734
722 for src, abs, rel in walk(repo, pats, opts, '(?:.*/|)'):
735 for src, abs, rel in walk(repo, pats, opts, '(?:.*/|)'):
723 if repo.dirstate.state(abs) == '?': continue
736 if repo.dirstate.state(abs) == '?': continue
724 if opts['fullpath']:
737 if opts['fullpath']:
725 ui.write(os.path.join(repo.root, abs), end)
738 ui.write(os.path.join(repo.root, abs), end)
726 else:
739 else:
727 ui.write(rel, end)
740 ui.write(rel, end)
728
741
729 def log(ui, repo, f=None, **opts):
742 def log(ui, repo, f=None, **opts):
730 """show the revision history of the repository or a single file"""
743 """show the revision history of the repository or a single file"""
731 if f:
744 if f:
732 files = relpath(repo, [f])
745 files = relpath(repo, [f])
733 filelog = repo.file(files[0])
746 filelog = repo.file(files[0])
734 log = filelog
747 log = filelog
735 lookup = filelog.lookup
748 lookup = filelog.lookup
736 else:
749 else:
737 files = None
750 files = None
738 filelog = None
751 filelog = None
739 log = repo.changelog
752 log = repo.changelog
740 lookup = repo.lookup
753 lookup = repo.lookup
741 revlist = []
754 revlist = []
742 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
755 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
743 while revs:
756 while revs:
744 if len(revs) == 1:
757 if len(revs) == 1:
745 revlist.append(revs.pop(0))
758 revlist.append(revs.pop(0))
746 else:
759 else:
747 a = revs.pop(0)
760 a = revs.pop(0)
748 b = revs.pop(0)
761 b = revs.pop(0)
749 off = a > b and -1 or 1
762 off = a > b and -1 or 1
750 revlist.extend(range(a, b + off, off))
763 revlist.extend(range(a, b + off, off))
751
764
752 for i in revlist or range(log.count() - 1, -1, -1):
765 for i in revlist or range(log.count() - 1, -1, -1):
753 show_changeset(ui, repo, filelog=filelog, rev=i)
766 show_changeset(ui, repo, filelog=filelog, rev=i)
754 if opts['patch']:
767 if opts['patch']:
755 if filelog:
768 if filelog:
756 filenode = filelog.node(i)
769 filenode = filelog.node(i)
757 i = filelog.linkrev(filenode)
770 i = filelog.linkrev(filenode)
758 changenode = repo.changelog.node(i)
771 changenode = repo.changelog.node(i)
759 prev, other = repo.changelog.parents(changenode)
772 prev, other = repo.changelog.parents(changenode)
760 dodiff(sys.stdout, ui, repo, files, prev, changenode)
773 dodiff(sys.stdout, ui, repo, files, prev, changenode)
761 ui.write("\n\n")
774 ui.write("\n\n")
762
775
763 def manifest(ui, repo, rev=None):
776 def manifest(ui, repo, rev=None):
764 """output the latest or given revision of the project manifest"""
777 """output the latest or given revision of the project manifest"""
765 if rev:
778 if rev:
766 try:
779 try:
767 # assume all revision numbers are for changesets
780 # assume all revision numbers are for changesets
768 n = repo.lookup(rev)
781 n = repo.lookup(rev)
769 change = repo.changelog.read(n)
782 change = repo.changelog.read(n)
770 n = change[0]
783 n = change[0]
771 except hg.RepoError:
784 except hg.RepoError:
772 n = repo.manifest.lookup(rev)
785 n = repo.manifest.lookup(rev)
773 else:
786 else:
774 n = repo.manifest.tip()
787 n = repo.manifest.tip()
775 m = repo.manifest.read(n)
788 m = repo.manifest.read(n)
776 mf = repo.manifest.readflags(n)
789 mf = repo.manifest.readflags(n)
777 files = m.keys()
790 files = m.keys()
778 files.sort()
791 files.sort()
779
792
780 for f in files:
793 for f in files:
781 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
794 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
782
795
783 def parents(ui, repo, rev=None):
796 def parents(ui, repo, rev=None):
784 """show the parents of the working dir or revision"""
797 """show the parents of the working dir or revision"""
785 if rev:
798 if rev:
786 p = repo.changelog.parents(repo.lookup(rev))
799 p = repo.changelog.parents(repo.lookup(rev))
787 else:
800 else:
788 p = repo.dirstate.parents()
801 p = repo.dirstate.parents()
789
802
790 for n in p:
803 for n in p:
791 if n != hg.nullid:
804 if n != hg.nullid:
792 show_changeset(ui, repo, changenode=n)
805 show_changeset(ui, repo, changenode=n)
793
806
794 def paths(ui, repo, search = None):
807 def paths(ui, repo, search = None):
795 """show path or list of available paths"""
808 """show path or list of available paths"""
796 if search:
809 if search:
797 for name, path in ui.configitems("paths"):
810 for name, path in ui.configitems("paths"):
798 if name == search:
811 if name == search:
799 ui.write("%s\n" % path)
812 ui.write("%s\n" % path)
800 return
813 return
801 ui.warn("not found!\n")
814 ui.warn("not found!\n")
802 return 1
815 return 1
803 else:
816 else:
804 for name, path in ui.configitems("paths"):
817 for name, path in ui.configitems("paths"):
805 ui.write("%s = %s\n" % (name, path))
818 ui.write("%s = %s\n" % (name, path))
806
819
807 def pull(ui, repo, source="default", **opts):
820 def pull(ui, repo, source="default", **opts):
808 """pull changes from the specified source"""
821 """pull changes from the specified source"""
809 source = ui.expandpath(source)
822 source = ui.expandpath(source)
810 ui.status('pulling from %s\n' % (source))
823 ui.status('pulling from %s\n' % (source))
811
824
812 other = hg.repository(ui, source)
825 other = hg.repository(ui, source)
813 r = repo.pull(other)
826 r = repo.pull(other)
814 if not r:
827 if not r:
815 if opts['update']:
828 if opts['update']:
816 return update(ui, repo)
829 return update(ui, repo)
817 else:
830 else:
818 ui.status("(run 'hg update' to get a working copy)\n")
831 ui.status("(run 'hg update' to get a working copy)\n")
819
832
820 return r
833 return r
821
834
822 def push(ui, repo, dest="default-push"):
835 def push(ui, repo, dest="default-push"):
823 """push changes to the specified destination"""
836 """push changes to the specified destination"""
824 dest = ui.expandpath(dest)
837 dest = ui.expandpath(dest)
825 ui.status('pushing to %s\n' % (dest))
838 ui.status('pushing to %s\n' % (dest))
826
839
827 other = hg.repository(ui, dest)
840 other = hg.repository(ui, dest)
828 r = repo.push(other)
841 r = repo.push(other)
829 return r
842 return r
830
843
831 def rawcommit(ui, repo, *flist, **rc):
844 def rawcommit(ui, repo, *flist, **rc):
832 "raw commit interface"
845 "raw commit interface"
833 if rc['text']:
846 if rc['text']:
834 ui.warn("Warning: -t and --text is deprecated,"
847 ui.warn("Warning: -t and --text is deprecated,"
835 " please use -m or --message instead.\n")
848 " please use -m or --message instead.\n")
836 message = rc['message'] or rc['text']
849 message = rc['message'] or rc['text']
837 if not message and rc['logfile']:
850 if not message and rc['logfile']:
838 try:
851 try:
839 message = open(rc['logfile']).read()
852 message = open(rc['logfile']).read()
840 except IOError:
853 except IOError:
841 pass
854 pass
842 if not message and not rc['logfile']:
855 if not message and not rc['logfile']:
843 ui.warn("abort: missing commit message\n")
856 ui.warn("abort: missing commit message\n")
844 return 1
857 return 1
845
858
846 files = relpath(repo, list(flist))
859 files = relpath(repo, list(flist))
847 if rc['files']:
860 if rc['files']:
848 files += open(rc['files']).read().splitlines()
861 files += open(rc['files']).read().splitlines()
849
862
850 rc['parent'] = map(repo.lookup, rc['parent'])
863 rc['parent'] = map(repo.lookup, rc['parent'])
851
864
852 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
865 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
853
866
854 def recover(ui, repo):
867 def recover(ui, repo):
855 """roll back an interrupted transaction"""
868 """roll back an interrupted transaction"""
856 repo.recover()
869 repo.recover()
857
870
858 def remove(ui, repo, file1, *files):
871 def remove(ui, repo, file1, *files):
859 """remove the specified files on the next commit"""
872 """remove the specified files on the next commit"""
860 repo.remove(relpath(repo, (file1,) + files))
873 repo.remove(relpath(repo, (file1,) + files))
861
874
862 def revert(ui, repo, *names, **opts):
875 def revert(ui, repo, *names, **opts):
863 """revert modified files or dirs back to their unmodified states"""
876 """revert modified files or dirs back to their unmodified states"""
864 node = opts['rev'] and repo.lookup(opts['rev']) or \
877 node = opts['rev'] and repo.lookup(opts['rev']) or \
865 repo.dirstate.parents()[0]
878 repo.dirstate.parents()[0]
866 root = os.path.realpath(repo.root)
879 root = os.path.realpath(repo.root)
867
880
868 def trimpath(p):
881 def trimpath(p):
869 p = os.path.realpath(p)
882 p = os.path.realpath(p)
870 if p.startswith(root):
883 if p.startswith(root):
871 rest = p[len(root):]
884 rest = p[len(root):]
872 if not rest:
885 if not rest:
873 return rest
886 return rest
874 if p.startswith(os.sep):
887 if p.startswith(os.sep):
875 return rest[1:]
888 return rest[1:]
876 return p
889 return p
877
890
878 relnames = map(trimpath, names or [os.getcwd()])
891 relnames = map(trimpath, names or [os.getcwd()])
879 chosen = {}
892 chosen = {}
880
893
881 def choose(name):
894 def choose(name):
882 def body(name):
895 def body(name):
883 for r in relnames:
896 for r in relnames:
884 if not name.startswith(r):
897 if not name.startswith(r):
885 continue
898 continue
886 rest = name[len(r):]
899 rest = name[len(r):]
887 if not rest:
900 if not rest:
888 return r, True
901 return r, True
889 depth = rest.count(os.sep)
902 depth = rest.count(os.sep)
890 if not r:
903 if not r:
891 if depth == 0 or not opts['nonrecursive']:
904 if depth == 0 or not opts['nonrecursive']:
892 return r, True
905 return r, True
893 elif rest[0] == os.sep:
906 elif rest[0] == os.sep:
894 if depth == 1 or not opts['nonrecursive']:
907 if depth == 1 or not opts['nonrecursive']:
895 return r, True
908 return r, True
896 return None, False
909 return None, False
897 relname, ret = body(name)
910 relname, ret = body(name)
898 if ret:
911 if ret:
899 chosen[relname] = 1
912 chosen[relname] = 1
900 return ret
913 return ret
901
914
902 r = repo.update(node, False, True, choose, False)
915 r = repo.update(node, False, True, choose, False)
903 for n in relnames:
916 for n in relnames:
904 if n not in chosen:
917 if n not in chosen:
905 ui.warn('error: no matches for %s\n' % n)
918 ui.warn('error: no matches for %s\n' % n)
906 r = 1
919 r = 1
907 sys.stdout.flush()
920 sys.stdout.flush()
908 return r
921 return r
909
922
910 def root(ui, repo):
923 def root(ui, repo):
911 """print the root (top) of the current working dir"""
924 """print the root (top) of the current working dir"""
912 ui.write(repo.root + "\n")
925 ui.write(repo.root + "\n")
913
926
914 def serve(ui, repo, **opts):
927 def serve(ui, repo, **opts):
915 """export the repository via HTTP"""
928 """export the repository via HTTP"""
916
929
917 if opts["stdio"]:
930 if opts["stdio"]:
918 fin, fout = sys.stdin, sys.stdout
931 fin, fout = sys.stdin, sys.stdout
919 sys.stdout = sys.stderr
932 sys.stdout = sys.stderr
920
933
921 def getarg():
934 def getarg():
922 argline = fin.readline()[:-1]
935 argline = fin.readline()[:-1]
923 arg, l = argline.split()
936 arg, l = argline.split()
924 val = fin.read(int(l))
937 val = fin.read(int(l))
925 return arg, val
938 return arg, val
926 def respond(v):
939 def respond(v):
927 fout.write("%d\n" % len(v))
940 fout.write("%d\n" % len(v))
928 fout.write(v)
941 fout.write(v)
929 fout.flush()
942 fout.flush()
930
943
931 lock = None
944 lock = None
932
945
933 while 1:
946 while 1:
934 cmd = fin.readline()[:-1]
947 cmd = fin.readline()[:-1]
935 if cmd == '':
948 if cmd == '':
936 return
949 return
937 if cmd == "heads":
950 if cmd == "heads":
938 h = repo.heads()
951 h = repo.heads()
939 respond(" ".join(map(hg.hex, h)) + "\n")
952 respond(" ".join(map(hg.hex, h)) + "\n")
940 if cmd == "lock":
953 if cmd == "lock":
941 lock = repo.lock()
954 lock = repo.lock()
942 respond("")
955 respond("")
943 if cmd == "unlock":
956 if cmd == "unlock":
944 if lock:
957 if lock:
945 lock.release()
958 lock.release()
946 lock = None
959 lock = None
947 respond("")
960 respond("")
948 elif cmd == "branches":
961 elif cmd == "branches":
949 arg, nodes = getarg()
962 arg, nodes = getarg()
950 nodes = map(hg.bin, nodes.split(" "))
963 nodes = map(hg.bin, nodes.split(" "))
951 r = []
964 r = []
952 for b in repo.branches(nodes):
965 for b in repo.branches(nodes):
953 r.append(" ".join(map(hg.hex, b)) + "\n")
966 r.append(" ".join(map(hg.hex, b)) + "\n")
954 respond("".join(r))
967 respond("".join(r))
955 elif cmd == "between":
968 elif cmd == "between":
956 arg, pairs = getarg()
969 arg, pairs = getarg()
957 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
970 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
958 r = []
971 r = []
959 for b in repo.between(pairs):
972 for b in repo.between(pairs):
960 r.append(" ".join(map(hg.hex, b)) + "\n")
973 r.append(" ".join(map(hg.hex, b)) + "\n")
961 respond("".join(r))
974 respond("".join(r))
962 elif cmd == "changegroup":
975 elif cmd == "changegroup":
963 nodes = []
976 nodes = []
964 arg, roots = getarg()
977 arg, roots = getarg()
965 nodes = map(hg.bin, roots.split(" "))
978 nodes = map(hg.bin, roots.split(" "))
966
979
967 cg = repo.changegroup(nodes)
980 cg = repo.changegroup(nodes)
968 while 1:
981 while 1:
969 d = cg.read(4096)
982 d = cg.read(4096)
970 if not d:
983 if not d:
971 break
984 break
972 fout.write(d)
985 fout.write(d)
973
986
974 fout.flush()
987 fout.flush()
975
988
976 elif cmd == "addchangegroup":
989 elif cmd == "addchangegroup":
977 if not lock:
990 if not lock:
978 respond("not locked")
991 respond("not locked")
979 continue
992 continue
980 respond("")
993 respond("")
981
994
982 r = repo.addchangegroup(fin)
995 r = repo.addchangegroup(fin)
983 respond("")
996 respond("")
984
997
985 def openlog(opt, default):
998 def openlog(opt, default):
986 if opts[opt] and opts[opt] != '-':
999 if opts[opt] and opts[opt] != '-':
987 return open(opts[opt], 'w')
1000 return open(opts[opt], 'w')
988 else:
1001 else:
989 return default
1002 return default
990
1003
991 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
1004 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
992 opts["address"], opts["port"],
1005 opts["address"], opts["port"],
993 openlog('accesslog', sys.stdout),
1006 openlog('accesslog', sys.stdout),
994 openlog('errorlog', sys.stderr))
1007 openlog('errorlog', sys.stderr))
995 if ui.verbose:
1008 if ui.verbose:
996 addr, port = httpd.socket.getsockname()
1009 addr, port = httpd.socket.getsockname()
997 if addr == '0.0.0.0':
1010 if addr == '0.0.0.0':
998 addr = socket.gethostname()
1011 addr = socket.gethostname()
999 else:
1012 else:
1000 try:
1013 try:
1001 addr = socket.gethostbyaddr(addr)[0]
1014 addr = socket.gethostbyaddr(addr)[0]
1002 except socket.error:
1015 except socket.error:
1003 pass
1016 pass
1004 if port != 80:
1017 if port != 80:
1005 ui.status('listening at http://%s:%d/\n' % (addr, port))
1018 ui.status('listening at http://%s:%d/\n' % (addr, port))
1006 else:
1019 else:
1007 ui.status('listening at http://%s/\n' % addr)
1020 ui.status('listening at http://%s/\n' % addr)
1008 httpd.serve_forever()
1021 httpd.serve_forever()
1009
1022
1010 def status(ui, repo, *pats, **opts):
1023 def status(ui, repo, *pats, **opts):
1011 '''show changed files in the working directory
1024 '''show changed files in the working directory
1012
1025
1013 M = modified
1026 M = modified
1014 A = added
1027 A = added
1015 R = removed
1028 R = removed
1016 ? = not tracked'''
1029 ? = not tracked'''
1017
1030
1018 files, matchfn = matchpats(repo.getcwd(), pats, opts)
1031 cwd = repo.getcwd()
1032 files, matchfn = matchpats(cwd, pats, opts)
1019 (c, a, d, u) = repo.changes(files = files, match = matchfn)
1033 (c, a, d, u) = repo.changes(files = files, match = matchfn)
1020 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
1034 (c, a, d, u) = [map(lambda x: pathto(cwd, x), n) for n in c, a, d, u]
1021
1035
1022 for f in c:
1036 for f in c:
1023 ui.write("M ", f, "\n")
1037 ui.write("M ", f, "\n")
1024 for f in a:
1038 for f in a:
1025 ui.write("A ", f, "\n")
1039 ui.write("A ", f, "\n")
1026 for f in d:
1040 for f in d:
1027 ui.write("R ", f, "\n")
1041 ui.write("R ", f, "\n")
1028 for f in u:
1042 for f in u:
1029 ui.write("? ", f, "\n")
1043 ui.write("? ", f, "\n")
1030
1044
1031 def tag(ui, repo, name, rev=None, **opts):
1045 def tag(ui, repo, name, rev=None, **opts):
1032 """add a tag for the current tip or a given revision"""
1046 """add a tag for the current tip or a given revision"""
1033 if opts['text']:
1047 if opts['text']:
1034 ui.warn("Warning: -t and --text is deprecated,"
1048 ui.warn("Warning: -t and --text is deprecated,"
1035 " please use -m or --message instead.\n")
1049 " please use -m or --message instead.\n")
1036 if name == "tip":
1050 if name == "tip":
1037 ui.warn("abort: 'tip' is a reserved name!\n")
1051 ui.warn("abort: 'tip' is a reserved name!\n")
1038 return -1
1052 return -1
1039 if rev:
1053 if rev:
1040 r = hg.hex(repo.lookup(rev))
1054 r = hg.hex(repo.lookup(rev))
1041 else:
1055 else:
1042 r = hg.hex(repo.changelog.tip())
1056 r = hg.hex(repo.changelog.tip())
1043
1057
1044 if name.find(revrangesep) >= 0:
1058 if name.find(revrangesep) >= 0:
1045 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1059 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1046 return -1
1060 return -1
1047
1061
1048 if opts['local']:
1062 if opts['local']:
1049 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1063 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1050 return
1064 return
1051
1065
1052 (c, a, d, u) = repo.changes()
1066 (c, a, d, u) = repo.changes()
1053 for x in (c, a, d, u):
1067 for x in (c, a, d, u):
1054 if ".hgtags" in x:
1068 if ".hgtags" in x:
1055 ui.warn("abort: working copy of .hgtags is changed!\n")
1069 ui.warn("abort: working copy of .hgtags is changed!\n")
1056 ui.status("(please commit .hgtags manually)\n")
1070 ui.status("(please commit .hgtags manually)\n")
1057 return -1
1071 return -1
1058
1072
1059 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1073 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1060 if repo.dirstate.state(".hgtags") == '?':
1074 if repo.dirstate.state(".hgtags") == '?':
1061 repo.add([".hgtags"])
1075 repo.add([".hgtags"])
1062
1076
1063 message = (opts['message'] or opts['text'] or
1077 message = (opts['message'] or opts['text'] or
1064 "Added tag %s for changeset %s" % (name, r))
1078 "Added tag %s for changeset %s" % (name, r))
1065 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1079 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1066
1080
1067 def tags(ui, repo):
1081 def tags(ui, repo):
1068 """list repository tags"""
1082 """list repository tags"""
1069
1083
1070 l = repo.tagslist()
1084 l = repo.tagslist()
1071 l.reverse()
1085 l.reverse()
1072 for t, n in l:
1086 for t, n in l:
1073 try:
1087 try:
1074 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1088 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1075 except KeyError:
1089 except KeyError:
1076 r = " ?:?"
1090 r = " ?:?"
1077 ui.write("%-30s %s\n" % (t, r))
1091 ui.write("%-30s %s\n" % (t, r))
1078
1092
1079 def tip(ui, repo):
1093 def tip(ui, repo):
1080 """show the tip revision"""
1094 """show the tip revision"""
1081 n = repo.changelog.tip()
1095 n = repo.changelog.tip()
1082 show_changeset(ui, repo, changenode=n)
1096 show_changeset(ui, repo, changenode=n)
1083
1097
1084 def undo(ui, repo):
1098 def undo(ui, repo):
1085 """undo the last commit or pull
1099 """undo the last commit or pull
1086
1100
1087 Roll back the last pull or commit transaction on the
1101 Roll back the last pull or commit transaction on the
1088 repository, restoring the project to its earlier state.
1102 repository, restoring the project to its earlier state.
1089
1103
1090 This command should be used with care. There is only one level of
1104 This command should be used with care. There is only one level of
1091 undo and there is no redo.
1105 undo and there is no redo.
1092
1106
1093 This command is not intended for use on public repositories. Once
1107 This command is not intended for use on public repositories. Once
1094 a change is visible for pull by other users, undoing it locally is
1108 a change is visible for pull by other users, undoing it locally is
1095 ineffective.
1109 ineffective.
1096 """
1110 """
1097 repo.undo()
1111 repo.undo()
1098
1112
1099 def update(ui, repo, node=None, merge=False, clean=False):
1113 def update(ui, repo, node=None, merge=False, clean=False):
1100 '''update or merge working directory
1114 '''update or merge working directory
1101
1115
1102 If there are no outstanding changes in the working directory and
1116 If there are no outstanding changes in the working directory and
1103 there is a linear relationship between the current version and the
1117 there is a linear relationship between the current version and the
1104 requested version, the result is the requested version.
1118 requested version, the result is the requested version.
1105
1119
1106 Otherwise the result is a merge between the contents of the
1120 Otherwise the result is a merge between the contents of the
1107 current working directory and the requested version. Files that
1121 current working directory and the requested version. Files that
1108 changed between either parent are marked as changed for the next
1122 changed between either parent are marked as changed for the next
1109 commit and a commit must be performed before any further updates
1123 commit and a commit must be performed before any further updates
1110 are allowed.
1124 are allowed.
1111 '''
1125 '''
1112 node = node and repo.lookup(node) or repo.changelog.tip()
1126 node = node and repo.lookup(node) or repo.changelog.tip()
1113 return repo.update(node, allow=merge, force=clean)
1127 return repo.update(node, allow=merge, force=clean)
1114
1128
1115 def verify(ui, repo):
1129 def verify(ui, repo):
1116 """verify the integrity of the repository"""
1130 """verify the integrity of the repository"""
1117 return repo.verify()
1131 return repo.verify()
1118
1132
1119 # Command options and aliases are listed here, alphabetically
1133 # Command options and aliases are listed here, alphabetically
1120
1134
1121 table = {
1135 table = {
1122 "^add": (add,
1136 "^add": (add,
1123 [('I', 'include', [], 'include path in search'),
1137 [('I', 'include', [], 'include path in search'),
1124 ('X', 'exclude', [], 'exclude path from search')],
1138 ('X', 'exclude', [], 'exclude path from search')],
1125 "hg add [FILE]..."),
1139 "hg add [FILE]..."),
1126 "addremove": (addremove,
1140 "addremove": (addremove,
1127 [('I', 'include', [], 'include path in search'),
1141 [('I', 'include', [], 'include path in search'),
1128 ('X', 'exclude', [], 'exclude path from search')],
1142 ('X', 'exclude', [], 'exclude path from search')],
1129 "hg addremove [OPTION]... [FILE]..."),
1143 "hg addremove [OPTION]... [FILE]..."),
1130 "^annotate":
1144 "^annotate":
1131 (annotate,
1145 (annotate,
1132 [('r', 'rev', '', 'revision'),
1146 [('r', 'rev', '', 'revision'),
1133 ('u', 'user', None, 'show user'),
1147 ('u', 'user', None, 'show user'),
1134 ('n', 'number', None, 'show revision number'),
1148 ('n', 'number', None, 'show revision number'),
1135 ('c', 'changeset', None, 'show changeset'),
1149 ('c', 'changeset', None, 'show changeset'),
1136 ('I', 'include', [], 'include path in search'),
1150 ('I', 'include', [], 'include path in search'),
1137 ('X', 'exclude', [], 'exclude path from search')],
1151 ('X', 'exclude', [], 'exclude path from search')],
1138 'hg annotate [-r REV] [-u] [-n] [-c] FILE...'),
1152 'hg annotate [-r REV] [-u] [-n] [-c] FILE...'),
1139 "cat":
1153 "cat":
1140 (cat,
1154 (cat,
1141 [('o', 'output', "", 'output to file')],
1155 [('o', 'output', "", 'output to file')],
1142 'hg cat [-o OUTFILE] FILE [REV]'),
1156 'hg cat [-o OUTFILE] FILE [REV]'),
1143 "^clone":
1157 "^clone":
1144 (clone,
1158 (clone,
1145 [('U', 'noupdate', None, 'skip update after cloning')],
1159 [('U', 'noupdate', None, 'skip update after cloning')],
1146 'hg clone [-U] SOURCE [DEST]'),
1160 'hg clone [-U] SOURCE [DEST]'),
1147 "^commit|ci":
1161 "^commit|ci":
1148 (commit,
1162 (commit,
1149 [('A', 'addremove', None, 'run add/remove during commit'),
1163 [('A', 'addremove', None, 'run add/remove during commit'),
1150 ('I', 'include', [], 'include path in search'),
1164 ('I', 'include', [], 'include path in search'),
1151 ('X', 'exclude', [], 'exclude path from search'),
1165 ('X', 'exclude', [], 'exclude path from search'),
1152 ('m', 'message', "", 'commit message'),
1166 ('m', 'message', "", 'commit message'),
1153 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1167 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1154 ('l', 'logfile', "", 'commit message file'),
1168 ('l', 'logfile', "", 'commit message file'),
1155 ('d', 'date', "", 'date code'),
1169 ('d', 'date', "", 'date code'),
1156 ('u', 'user', "", 'user')],
1170 ('u', 'user', "", 'user')],
1157 'hg commit [OPTION]... [FILE]...'),
1171 'hg commit [OPTION]... [FILE]...'),
1158 "copy": (copy, [], 'hg copy SOURCE DEST'),
1172 "copy": (copy, [], 'hg copy SOURCE DEST'),
1159 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1173 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1160 "debugstate": (debugstate, [], 'debugstate'),
1174 "debugstate": (debugstate, [], 'debugstate'),
1161 "debugindex": (debugindex, [], 'debugindex FILE'),
1175 "debugindex": (debugindex, [], 'debugindex FILE'),
1162 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1176 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1177 "debugwalk": (debugwalk,
1178 [('I', 'include', [], 'include path in search'),
1179 ('X', 'exclude', [], 'exclude path from search')],
1180 'debugwalk [OPTIONS]... [FILE]...'),
1163 "^diff":
1181 "^diff":
1164 (diff,
1182 (diff,
1165 [('r', 'rev', [], 'revision'),
1183 [('r', 'rev', [], 'revision'),
1166 ('I', 'include', [], 'include path in search'),
1184 ('I', 'include', [], 'include path in search'),
1167 ('X', 'exclude', [], 'exclude path from search')],
1185 ('X', 'exclude', [], 'exclude path from search')],
1168 'hg diff [-r REV1 [-r REV2]] [FILE]...'),
1186 'hg diff [-r REV1 [-r REV2]] [FILE]...'),
1169 "^export":
1187 "^export":
1170 (export,
1188 (export,
1171 [('o', 'output', "", 'output to file')],
1189 [('o', 'output', "", 'output to file')],
1172 "hg export [-o OUTFILE] REV..."),
1190 "hg export [-o OUTFILE] REV..."),
1173 "forget": (forget,
1191 "forget": (forget,
1174 [('I', 'include', [], 'include path in search'),
1192 [('I', 'include', [], 'include path in search'),
1175 ('X', 'exclude', [], 'exclude path from search')],
1193 ('X', 'exclude', [], 'exclude path from search')],
1176 "hg forget FILE..."),
1194 "hg forget FILE..."),
1177 "heads": (heads, [], 'hg heads'),
1195 "heads": (heads, [], 'hg heads'),
1178 "help": (help_, [], 'hg help [COMMAND]'),
1196 "help": (help_, [], 'hg help [COMMAND]'),
1179 "identify|id": (identify, [], 'hg identify'),
1197 "identify|id": (identify, [], 'hg identify'),
1180 "import|patch":
1198 "import|patch":
1181 (import_,
1199 (import_,
1182 [('p', 'strip', 1, 'path strip'),
1200 [('p', 'strip', 1, 'path strip'),
1183 ('b', 'base', "", 'base path')],
1201 ('b', 'base', "", 'base path')],
1184 "hg import [-p NUM] [-b BASE] PATCH..."),
1202 "hg import [-p NUM] [-b BASE] PATCH..."),
1185 "^init": (init, [], 'hg init'),
1203 "^init": (init, [], 'hg init'),
1186 "locate":
1204 "locate":
1187 (locate,
1205 (locate,
1188 [('r', 'rev', '', 'revision'),
1206 [('r', 'rev', '', 'revision'),
1189 ('0', 'print0', None, 'end records with NUL'),
1207 ('0', 'print0', None, 'end records with NUL'),
1190 ('f', 'fullpath', None, 'print complete paths'),
1208 ('f', 'fullpath', None, 'print complete paths'),
1191 ('I', 'include', [], 'include path in search'),
1209 ('I', 'include', [], 'include path in search'),
1192 ('X', 'exclude', [], 'exclude path from search')],
1210 ('X', 'exclude', [], 'exclude path from search')],
1193 'hg locate [-r REV] [-f] [-0] [PATTERN]...'),
1211 'hg locate [-r REV] [-f] [-0] [PATTERN]...'),
1194 "^log|history":
1212 "^log|history":
1195 (log,
1213 (log,
1196 [('r', 'rev', [], 'revision'),
1214 [('r', 'rev', [], 'revision'),
1197 ('p', 'patch', None, 'show patch')],
1215 ('p', 'patch', None, 'show patch')],
1198 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1216 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1199 "manifest": (manifest, [], 'hg manifest [REV]'),
1217 "manifest": (manifest, [], 'hg manifest [REV]'),
1200 "parents": (parents, [], 'hg parents [REV]'),
1218 "parents": (parents, [], 'hg parents [REV]'),
1201 "paths": (paths, [], 'hg paths [name]'),
1219 "paths": (paths, [], 'hg paths [name]'),
1202 "^pull":
1220 "^pull":
1203 (pull,
1221 (pull,
1204 [('u', 'update', None, 'update working directory')],
1222 [('u', 'update', None, 'update working directory')],
1205 'hg pull [-u] [SOURCE]'),
1223 'hg pull [-u] [SOURCE]'),
1206 "^push": (push, [], 'hg push [DEST]'),
1224 "^push": (push, [], 'hg push [DEST]'),
1207 "rawcommit":
1225 "rawcommit":
1208 (rawcommit,
1226 (rawcommit,
1209 [('p', 'parent', [], 'parent'),
1227 [('p', 'parent', [], 'parent'),
1210 ('d', 'date', "", 'date code'),
1228 ('d', 'date', "", 'date code'),
1211 ('u', 'user', "", 'user'),
1229 ('u', 'user', "", 'user'),
1212 ('F', 'files', "", 'file list'),
1230 ('F', 'files', "", 'file list'),
1213 ('m', 'message', "", 'commit message'),
1231 ('m', 'message', "", 'commit message'),
1214 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1232 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1215 ('l', 'logfile', "", 'commit message file')],
1233 ('l', 'logfile', "", 'commit message file')],
1216 'hg rawcommit [OPTION]... [FILE]...'),
1234 'hg rawcommit [OPTION]... [FILE]...'),
1217 "recover": (recover, [], "hg recover"),
1235 "recover": (recover, [], "hg recover"),
1218 "^remove|rm": (remove, [], "hg remove FILE..."),
1236 "^remove|rm": (remove, [], "hg remove FILE..."),
1219 "^revert":
1237 "^revert":
1220 (revert,
1238 (revert,
1221 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1239 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1222 ("r", "rev", "", "revision")],
1240 ("r", "rev", "", "revision")],
1223 "hg revert [-n] [-r REV] [NAME]..."),
1241 "hg revert [-n] [-r REV] [NAME]..."),
1224 "root": (root, [], "hg root"),
1242 "root": (root, [], "hg root"),
1225 "^serve":
1243 "^serve":
1226 (serve,
1244 (serve,
1227 [('A', 'accesslog', '', 'access log file'),
1245 [('A', 'accesslog', '', 'access log file'),
1228 ('E', 'errorlog', '', 'error log file'),
1246 ('E', 'errorlog', '', 'error log file'),
1229 ('p', 'port', 8000, 'listen port'),
1247 ('p', 'port', 8000, 'listen port'),
1230 ('a', 'address', '', 'interface address'),
1248 ('a', 'address', '', 'interface address'),
1231 ('n', 'name', os.getcwd(), 'repository name'),
1249 ('n', 'name', os.getcwd(), 'repository name'),
1232 ('', 'stdio', None, 'for remote clients'),
1250 ('', 'stdio', None, 'for remote clients'),
1233 ('t', 'templates', "", 'template map')],
1251 ('t', 'templates', "", 'template map')],
1234 "hg serve [OPTION]..."),
1252 "hg serve [OPTION]..."),
1235 "^status": (status,
1253 "^status": (status,
1236 [('I', 'include', [], 'include path in search'),
1254 [('I', 'include', [], 'include path in search'),
1237 ('X', 'exclude', [], 'exclude path from search')],
1255 ('X', 'exclude', [], 'exclude path from search')],
1238 'hg status [FILE]...'),
1256 'hg status [FILE]...'),
1239 "tag":
1257 "tag":
1240 (tag,
1258 (tag,
1241 [('l', 'local', None, 'make the tag local'),
1259 [('l', 'local', None, 'make the tag local'),
1242 ('m', 'message', "", 'commit message'),
1260 ('m', 'message', "", 'commit message'),
1243 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1261 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1244 ('d', 'date', "", 'date code'),
1262 ('d', 'date', "", 'date code'),
1245 ('u', 'user', "", 'user')],
1263 ('u', 'user', "", 'user')],
1246 'hg tag [OPTION]... NAME [REV]'),
1264 'hg tag [OPTION]... NAME [REV]'),
1247 "tags": (tags, [], 'hg tags'),
1265 "tags": (tags, [], 'hg tags'),
1248 "tip": (tip, [], 'hg tip'),
1266 "tip": (tip, [], 'hg tip'),
1249 "undo": (undo, [], 'hg undo'),
1267 "undo": (undo, [], 'hg undo'),
1250 "^update|up|checkout|co":
1268 "^update|up|checkout|co":
1251 (update,
1269 (update,
1252 [('m', 'merge', None, 'allow merging of conflicts'),
1270 [('m', 'merge', None, 'allow merging of conflicts'),
1253 ('C', 'clean', None, 'overwrite locally modified files')],
1271 ('C', 'clean', None, 'overwrite locally modified files')],
1254 'hg update [-m] [-C] [REV]'),
1272 'hg update [-m] [-C] [REV]'),
1255 "verify": (verify, [], 'hg verify'),
1273 "verify": (verify, [], 'hg verify'),
1256 "version": (show_version, [], 'hg version'),
1274 "version": (show_version, [], 'hg version'),
1257 }
1275 }
1258
1276
1259 globalopts = [('v', 'verbose', None, 'verbose'),
1277 globalopts = [('v', 'verbose', None, 'verbose'),
1260 ('', 'debug', None, 'debug'),
1278 ('', 'debug', None, 'debug'),
1261 ('q', 'quiet', None, 'quiet'),
1279 ('q', 'quiet', None, 'quiet'),
1262 ('', 'profile', None, 'profile'),
1280 ('', 'profile', None, 'profile'),
1263 ('R', 'repository', "", 'repository root directory'),
1281 ('R', 'repository', "", 'repository root directory'),
1264 ('', 'traceback', None, 'print traceback on exception'),
1282 ('', 'traceback', None, 'print traceback on exception'),
1265 ('y', 'noninteractive', None, 'run non-interactively'),
1283 ('y', 'noninteractive', None, 'run non-interactively'),
1266 ('', 'version', None, 'output version information and exit'),
1284 ('', 'version', None, 'output version information and exit'),
1267 ('', 'time', None, 'time how long the command takes'),
1285 ('', 'time', None, 'time how long the command takes'),
1268 ]
1286 ]
1269
1287
1270 norepo = "clone init version help debugindex debugindexdot"
1288 norepo = "clone init version help debugindex debugindexdot"
1271
1289
1272 def find(cmd):
1290 def find(cmd):
1273 for e in table.keys():
1291 for e in table.keys():
1274 if re.match("(%s)$" % e, cmd):
1292 if re.match("(%s)$" % e, cmd):
1275 return table[e]
1293 return table[e]
1276
1294
1277 raise UnknownCommand(cmd)
1295 raise UnknownCommand(cmd)
1278
1296
1279 class SignalInterrupt(Exception):
1297 class SignalInterrupt(Exception):
1280 """Exception raised on SIGTERM and SIGHUP."""
1298 """Exception raised on SIGTERM and SIGHUP."""
1281
1299
1282 def catchterm(*args):
1300 def catchterm(*args):
1283 raise SignalInterrupt
1301 raise SignalInterrupt
1284
1302
1285 def run():
1303 def run():
1286 sys.exit(dispatch(sys.argv[1:]))
1304 sys.exit(dispatch(sys.argv[1:]))
1287
1305
1288 class ParseError(Exception):
1306 class ParseError(Exception):
1289 """Exception raised on errors in parsing the command line."""
1307 """Exception raised on errors in parsing the command line."""
1290
1308
1291 def parse(args):
1309 def parse(args):
1292 options = {}
1310 options = {}
1293 cmdoptions = {}
1311 cmdoptions = {}
1294
1312
1295 try:
1313 try:
1296 args = fancyopts.fancyopts(args, globalopts, options)
1314 args = fancyopts.fancyopts(args, globalopts, options)
1297 except fancyopts.getopt.GetoptError, inst:
1315 except fancyopts.getopt.GetoptError, inst:
1298 raise ParseError(None, inst)
1316 raise ParseError(None, inst)
1299
1317
1300 if options["version"]:
1318 if options["version"]:
1301 return ("version", show_version, [], options, cmdoptions)
1319 return ("version", show_version, [], options, cmdoptions)
1302 elif not args:
1320 elif not args:
1303 return ("help", help_, [], options, cmdoptions)
1321 return ("help", help_, [], options, cmdoptions)
1304 else:
1322 else:
1305 cmd, args = args[0], args[1:]
1323 cmd, args = args[0], args[1:]
1306
1324
1307 i = find(cmd)
1325 i = find(cmd)
1308
1326
1309 # combine global options into local
1327 # combine global options into local
1310 c = list(i[1])
1328 c = list(i[1])
1311 for o in globalopts:
1329 for o in globalopts:
1312 c.append((o[0], o[1], options[o[1]], o[3]))
1330 c.append((o[0], o[1], options[o[1]], o[3]))
1313
1331
1314 try:
1332 try:
1315 args = fancyopts.fancyopts(args, c, cmdoptions)
1333 args = fancyopts.fancyopts(args, c, cmdoptions)
1316 except fancyopts.getopt.GetoptError, inst:
1334 except fancyopts.getopt.GetoptError, inst:
1317 raise ParseError(cmd, inst)
1335 raise ParseError(cmd, inst)
1318
1336
1319 # separate global options back out
1337 # separate global options back out
1320 for o in globalopts:
1338 for o in globalopts:
1321 n = o[1]
1339 n = o[1]
1322 options[n] = cmdoptions[n]
1340 options[n] = cmdoptions[n]
1323 del cmdoptions[n]
1341 del cmdoptions[n]
1324
1342
1325 return (cmd, i[0], args, options, cmdoptions)
1343 return (cmd, i[0], args, options, cmdoptions)
1326
1344
1327 def dispatch(args):
1345 def dispatch(args):
1328 signal.signal(signal.SIGTERM, catchterm)
1346 signal.signal(signal.SIGTERM, catchterm)
1329 try:
1347 try:
1330 signal.signal(signal.SIGHUP, catchterm)
1348 signal.signal(signal.SIGHUP, catchterm)
1331 except AttributeError:
1349 except AttributeError:
1332 pass
1350 pass
1333
1351
1334 try:
1352 try:
1335 cmd, func, args, options, cmdoptions = parse(args)
1353 cmd, func, args, options, cmdoptions = parse(args)
1336 except ParseError, inst:
1354 except ParseError, inst:
1337 u = ui.ui()
1355 u = ui.ui()
1338 if inst.args[0]:
1356 if inst.args[0]:
1339 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1357 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1340 help_(u, inst.args[0])
1358 help_(u, inst.args[0])
1341 else:
1359 else:
1342 u.warn("hg: %s\n" % inst.args[1])
1360 u.warn("hg: %s\n" % inst.args[1])
1343 help_(u)
1361 help_(u)
1344 sys.exit(-1)
1362 sys.exit(-1)
1345 except UnknownCommand, inst:
1363 except UnknownCommand, inst:
1346 u = ui.ui()
1364 u = ui.ui()
1347 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1365 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1348 help_(u)
1366 help_(u)
1349 sys.exit(1)
1367 sys.exit(1)
1350
1368
1351 if options["time"]:
1369 if options["time"]:
1352 def get_times():
1370 def get_times():
1353 t = os.times()
1371 t = os.times()
1354 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1372 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1355 t = (t[0], t[1], t[2], t[3], time.clock())
1373 t = (t[0], t[1], t[2], t[3], time.clock())
1356 return t
1374 return t
1357 s = get_times()
1375 s = get_times()
1358 def print_time():
1376 def print_time():
1359 t = get_times()
1377 t = get_times()
1360 u = ui.ui()
1378 u = ui.ui()
1361 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1379 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1362 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1380 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1363 atexit.register(print_time)
1381 atexit.register(print_time)
1364
1382
1365 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1383 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1366 not options["noninteractive"])
1384 not options["noninteractive"])
1367
1385
1368 try:
1386 try:
1369 try:
1387 try:
1370 if cmd not in norepo.split():
1388 if cmd not in norepo.split():
1371 path = options["repository"] or ""
1389 path = options["repository"] or ""
1372 repo = hg.repository(ui=u, path=path)
1390 repo = hg.repository(ui=u, path=path)
1373 d = lambda: func(u, repo, *args, **cmdoptions)
1391 d = lambda: func(u, repo, *args, **cmdoptions)
1374 else:
1392 else:
1375 d = lambda: func(u, *args, **cmdoptions)
1393 d = lambda: func(u, *args, **cmdoptions)
1376
1394
1377 if options['profile']:
1395 if options['profile']:
1378 import hotshot, hotshot.stats
1396 import hotshot, hotshot.stats
1379 prof = hotshot.Profile("hg.prof")
1397 prof = hotshot.Profile("hg.prof")
1380 r = prof.runcall(d)
1398 r = prof.runcall(d)
1381 prof.close()
1399 prof.close()
1382 stats = hotshot.stats.load("hg.prof")
1400 stats = hotshot.stats.load("hg.prof")
1383 stats.strip_dirs()
1401 stats.strip_dirs()
1384 stats.sort_stats('time', 'calls')
1402 stats.sort_stats('time', 'calls')
1385 stats.print_stats(40)
1403 stats.print_stats(40)
1386 return r
1404 return r
1387 else:
1405 else:
1388 return d()
1406 return d()
1389 except:
1407 except:
1390 if options['traceback']:
1408 if options['traceback']:
1391 traceback.print_exc()
1409 traceback.print_exc()
1392 raise
1410 raise
1393 except util.CommandError, inst:
1411 except util.CommandError, inst:
1394 u.warn("abort: %s\n" % inst.args)
1412 u.warn("abort: %s\n" % inst.args)
1395 except hg.RepoError, inst:
1413 except hg.RepoError, inst:
1396 u.warn("abort: ", inst, "!\n")
1414 u.warn("abort: ", inst, "!\n")
1397 except SignalInterrupt:
1415 except SignalInterrupt:
1398 u.warn("killed!\n")
1416 u.warn("killed!\n")
1399 except KeyboardInterrupt:
1417 except KeyboardInterrupt:
1400 u.warn("interrupted!\n")
1418 u.warn("interrupted!\n")
1401 except IOError, inst:
1419 except IOError, inst:
1402 if hasattr(inst, "code"):
1420 if hasattr(inst, "code"):
1403 u.warn("abort: %s\n" % inst)
1421 u.warn("abort: %s\n" % inst)
1404 elif hasattr(inst, "reason"):
1422 elif hasattr(inst, "reason"):
1405 u.warn("abort: error: %s\n" % inst.reason[1])
1423 u.warn("abort: error: %s\n" % inst.reason[1])
1406 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1424 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1407 if u.debugflag: u.warn("broken pipe\n")
1425 if u.debugflag: u.warn("broken pipe\n")
1408 else:
1426 else:
1409 raise
1427 raise
1410 except OSError, inst:
1428 except OSError, inst:
1411 if hasattr(inst, "filename"):
1429 if hasattr(inst, "filename"):
1412 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1430 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1413 else:
1431 else:
1414 u.warn("abort: %s\n" % inst.strerror)
1432 u.warn("abort: %s\n" % inst.strerror)
1415 except Abort, inst:
1433 except Abort, inst:
1416 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1434 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1417 sys.exit(1)
1435 sys.exit(1)
1418 except TypeError, inst:
1436 except TypeError, inst:
1419 # was this an argument error?
1437 # was this an argument error?
1420 tb = traceback.extract_tb(sys.exc_info()[2])
1438 tb = traceback.extract_tb(sys.exc_info()[2])
1421 if len(tb) > 2: # no
1439 if len(tb) > 2: # no
1422 raise
1440 raise
1423 u.debug(inst, "\n")
1441 u.debug(inst, "\n")
1424 u.warn("%s: invalid arguments\n" % cmd)
1442 u.warn("%s: invalid arguments\n" % cmd)
1425 help_(u, cmd)
1443 help_(u, cmd)
1426
1444
1427 sys.exit(-1)
1445 sys.exit(-1)
@@ -1,1981 +1,1983 b''
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes 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 sys, struct, os
8 import sys, struct, os
9 import util
9 import util
10 from revlog import *
10 from revlog import *
11 from demandload import *
11 from demandload import *
12 demandload(globals(), "re lock urllib urllib2 transaction time socket")
12 demandload(globals(), "re lock urllib urllib2 transaction time socket")
13 demandload(globals(), "tempfile httprangereader bdiff urlparse")
13 demandload(globals(), "tempfile httprangereader bdiff urlparse")
14 demandload(globals(), "bisect select")
14 demandload(globals(), "bisect select")
15
15
16 class filelog(revlog):
16 class filelog(revlog):
17 def __init__(self, opener, path):
17 def __init__(self, opener, path):
18 revlog.__init__(self, opener,
18 revlog.__init__(self, opener,
19 os.path.join("data", self.encodedir(path + ".i")),
19 os.path.join("data", self.encodedir(path + ".i")),
20 os.path.join("data", self.encodedir(path + ".d")))
20 os.path.join("data", self.encodedir(path + ".d")))
21
21
22 # This avoids a collision between a file named foo and a dir named
22 # This avoids a collision between a file named foo and a dir named
23 # foo.i or foo.d
23 # foo.i or foo.d
24 def encodedir(self, path):
24 def encodedir(self, path):
25 path.replace(".hg/", ".hg.hg/")
25 path.replace(".hg/", ".hg.hg/")
26 path.replace(".i/", ".i.hg/")
26 path.replace(".i/", ".i.hg/")
27 path.replace(".d/", ".i.hg/")
27 path.replace(".d/", ".i.hg/")
28 return path
28 return path
29
29
30 def decodedir(self, path):
30 def decodedir(self, path):
31 path.replace(".d.hg/", ".d/")
31 path.replace(".d.hg/", ".d/")
32 path.replace(".i.hg/", ".i/")
32 path.replace(".i.hg/", ".i/")
33 path.replace(".hg.hg/", ".hg/")
33 path.replace(".hg.hg/", ".hg/")
34 return path
34 return path
35
35
36 def read(self, node):
36 def read(self, node):
37 t = self.revision(node)
37 t = self.revision(node)
38 if not t.startswith('\1\n'):
38 if not t.startswith('\1\n'):
39 return t
39 return t
40 s = t.find('\1\n', 2)
40 s = t.find('\1\n', 2)
41 return t[s+2:]
41 return t[s+2:]
42
42
43 def readmeta(self, node):
43 def readmeta(self, node):
44 t = self.revision(node)
44 t = self.revision(node)
45 if not t.startswith('\1\n'):
45 if not t.startswith('\1\n'):
46 return t
46 return t
47 s = t.find('\1\n', 2)
47 s = t.find('\1\n', 2)
48 mt = t[2:s]
48 mt = t[2:s]
49 for l in mt.splitlines():
49 for l in mt.splitlines():
50 k, v = l.split(": ", 1)
50 k, v = l.split(": ", 1)
51 m[k] = v
51 m[k] = v
52 return m
52 return m
53
53
54 def add(self, text, meta, transaction, link, p1=None, p2=None):
54 def add(self, text, meta, transaction, link, p1=None, p2=None):
55 if meta or text.startswith('\1\n'):
55 if meta or text.startswith('\1\n'):
56 mt = ""
56 mt = ""
57 if meta:
57 if meta:
58 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
58 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
59 text = "\1\n" + "".join(mt) + "\1\n" + text
59 text = "\1\n" + "".join(mt) + "\1\n" + text
60 return self.addrevision(text, transaction, link, p1, p2)
60 return self.addrevision(text, transaction, link, p1, p2)
61
61
62 def annotate(self, node):
62 def annotate(self, node):
63
63
64 def decorate(text, rev):
64 def decorate(text, rev):
65 return ([rev] * len(text.splitlines()), text)
65 return ([rev] * len(text.splitlines()), text)
66
66
67 def pair(parent, child):
67 def pair(parent, child):
68 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
68 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
69 child[0][b1:b2] = parent[0][a1:a2]
69 child[0][b1:b2] = parent[0][a1:a2]
70 return child
70 return child
71
71
72 # find all ancestors
72 # find all ancestors
73 needed = {node:1}
73 needed = {node:1}
74 visit = [node]
74 visit = [node]
75 while visit:
75 while visit:
76 n = visit.pop(0)
76 n = visit.pop(0)
77 for p in self.parents(n):
77 for p in self.parents(n):
78 if p not in needed:
78 if p not in needed:
79 needed[p] = 1
79 needed[p] = 1
80 visit.append(p)
80 visit.append(p)
81 else:
81 else:
82 # count how many times we'll use this
82 # count how many times we'll use this
83 needed[p] += 1
83 needed[p] += 1
84
84
85 # sort by revision which is a topological order
85 # sort by revision which is a topological order
86 visit = [ (self.rev(n), n) for n in needed.keys() ]
86 visit = [ (self.rev(n), n) for n in needed.keys() ]
87 visit.sort()
87 visit.sort()
88 hist = {}
88 hist = {}
89
89
90 for r,n in visit:
90 for r,n in visit:
91 curr = decorate(self.read(n), self.linkrev(n))
91 curr = decorate(self.read(n), self.linkrev(n))
92 for p in self.parents(n):
92 for p in self.parents(n):
93 if p != nullid:
93 if p != nullid:
94 curr = pair(hist[p], curr)
94 curr = pair(hist[p], curr)
95 # trim the history of unneeded revs
95 # trim the history of unneeded revs
96 needed[p] -= 1
96 needed[p] -= 1
97 if not needed[p]:
97 if not needed[p]:
98 del hist[p]
98 del hist[p]
99 hist[n] = curr
99 hist[n] = curr
100
100
101 return zip(hist[n][0], hist[n][1].splitlines(1))
101 return zip(hist[n][0], hist[n][1].splitlines(1))
102
102
103 class manifest(revlog):
103 class manifest(revlog):
104 def __init__(self, opener):
104 def __init__(self, opener):
105 self.mapcache = None
105 self.mapcache = None
106 self.listcache = None
106 self.listcache = None
107 self.addlist = None
107 self.addlist = None
108 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
108 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
109
109
110 def read(self, node):
110 def read(self, node):
111 if node == nullid: return {} # don't upset local cache
111 if node == nullid: return {} # don't upset local cache
112 if self.mapcache and self.mapcache[0] == node:
112 if self.mapcache and self.mapcache[0] == node:
113 return self.mapcache[1]
113 return self.mapcache[1]
114 text = self.revision(node)
114 text = self.revision(node)
115 map = {}
115 map = {}
116 flag = {}
116 flag = {}
117 self.listcache = (text, text.splitlines(1))
117 self.listcache = (text, text.splitlines(1))
118 for l in self.listcache[1]:
118 for l in self.listcache[1]:
119 (f, n) = l.split('\0')
119 (f, n) = l.split('\0')
120 map[f] = bin(n[:40])
120 map[f] = bin(n[:40])
121 flag[f] = (n[40:-1] == "x")
121 flag[f] = (n[40:-1] == "x")
122 self.mapcache = (node, map, flag)
122 self.mapcache = (node, map, flag)
123 return map
123 return map
124
124
125 def readflags(self, node):
125 def readflags(self, node):
126 if node == nullid: return {} # don't upset local cache
126 if node == nullid: return {} # don't upset local cache
127 if not self.mapcache or self.mapcache[0] != node:
127 if not self.mapcache or self.mapcache[0] != node:
128 self.read(node)
128 self.read(node)
129 return self.mapcache[2]
129 return self.mapcache[2]
130
130
131 def diff(self, a, b):
131 def diff(self, a, b):
132 # this is sneaky, as we're not actually using a and b
132 # this is sneaky, as we're not actually using a and b
133 if self.listcache and self.addlist and self.listcache[0] == a:
133 if self.listcache and self.addlist and self.listcache[0] == a:
134 d = mdiff.diff(self.listcache[1], self.addlist, 1)
134 d = mdiff.diff(self.listcache[1], self.addlist, 1)
135 if mdiff.patch(a, d) != b:
135 if mdiff.patch(a, d) != b:
136 sys.stderr.write("*** sortdiff failed, falling back ***\n")
136 sys.stderr.write("*** sortdiff failed, falling back ***\n")
137 return mdiff.textdiff(a, b)
137 return mdiff.textdiff(a, b)
138 return d
138 return d
139 else:
139 else:
140 return mdiff.textdiff(a, b)
140 return mdiff.textdiff(a, b)
141
141
142 def add(self, map, flags, transaction, link, p1=None, p2=None,
142 def add(self, map, flags, transaction, link, p1=None, p2=None,
143 changed=None):
143 changed=None):
144 # directly generate the mdiff delta from the data collected during
144 # directly generate the mdiff delta from the data collected during
145 # the bisect loop below
145 # the bisect loop below
146 def gendelta(delta):
146 def gendelta(delta):
147 i = 0
147 i = 0
148 result = []
148 result = []
149 while i < len(delta):
149 while i < len(delta):
150 start = delta[i][2]
150 start = delta[i][2]
151 end = delta[i][3]
151 end = delta[i][3]
152 l = delta[i][4]
152 l = delta[i][4]
153 if l == None:
153 if l == None:
154 l = ""
154 l = ""
155 while i < len(delta) - 1 and start <= delta[i+1][2] \
155 while i < len(delta) - 1 and start <= delta[i+1][2] \
156 and end >= delta[i+1][2]:
156 and end >= delta[i+1][2]:
157 if delta[i+1][3] > end:
157 if delta[i+1][3] > end:
158 end = delta[i+1][3]
158 end = delta[i+1][3]
159 if delta[i+1][4]:
159 if delta[i+1][4]:
160 l += delta[i+1][4]
160 l += delta[i+1][4]
161 i += 1
161 i += 1
162 result.append(struct.pack(">lll", start, end, len(l)) + l)
162 result.append(struct.pack(">lll", start, end, len(l)) + l)
163 i += 1
163 i += 1
164 return result
164 return result
165
165
166 # apply the changes collected during the bisect loop to our addlist
166 # apply the changes collected during the bisect loop to our addlist
167 def addlistdelta(addlist, delta):
167 def addlistdelta(addlist, delta):
168 # apply the deltas to the addlist. start from the bottom up
168 # apply the deltas to the addlist. start from the bottom up
169 # so changes to the offsets don't mess things up.
169 # so changes to the offsets don't mess things up.
170 i = len(delta)
170 i = len(delta)
171 while i > 0:
171 while i > 0:
172 i -= 1
172 i -= 1
173 start = delta[i][0]
173 start = delta[i][0]
174 end = delta[i][1]
174 end = delta[i][1]
175 if delta[i][4]:
175 if delta[i][4]:
176 addlist[start:end] = [delta[i][4]]
176 addlist[start:end] = [delta[i][4]]
177 else:
177 else:
178 del addlist[start:end]
178 del addlist[start:end]
179 return addlist
179 return addlist
180
180
181 # calculate the byte offset of the start of each line in the
181 # calculate the byte offset of the start of each line in the
182 # manifest
182 # manifest
183 def calcoffsets(addlist):
183 def calcoffsets(addlist):
184 offsets = [0] * (len(addlist) + 1)
184 offsets = [0] * (len(addlist) + 1)
185 offset = 0
185 offset = 0
186 i = 0
186 i = 0
187 while i < len(addlist):
187 while i < len(addlist):
188 offsets[i] = offset
188 offsets[i] = offset
189 offset += len(addlist[i])
189 offset += len(addlist[i])
190 i += 1
190 i += 1
191 offsets[i] = offset
191 offsets[i] = offset
192 return offsets
192 return offsets
193
193
194 # if we're using the listcache, make sure it is valid and
194 # if we're using the listcache, make sure it is valid and
195 # parented by the same node we're diffing against
195 # parented by the same node we're diffing against
196 if not changed or not self.listcache or not p1 or \
196 if not changed or not self.listcache or not p1 or \
197 self.mapcache[0] != p1:
197 self.mapcache[0] != p1:
198 files = map.keys()
198 files = map.keys()
199 files.sort()
199 files.sort()
200
200
201 self.addlist = ["%s\000%s%s\n" %
201 self.addlist = ["%s\000%s%s\n" %
202 (f, hex(map[f]), flags[f] and "x" or '')
202 (f, hex(map[f]), flags[f] and "x" or '')
203 for f in files]
203 for f in files]
204 cachedelta = None
204 cachedelta = None
205 else:
205 else:
206 addlist = self.listcache[1]
206 addlist = self.listcache[1]
207
207
208 # find the starting offset for each line in the add list
208 # find the starting offset for each line in the add list
209 offsets = calcoffsets(addlist)
209 offsets = calcoffsets(addlist)
210
210
211 # combine the changed lists into one list for sorting
211 # combine the changed lists into one list for sorting
212 work = [[x, 0] for x in changed[0]]
212 work = [[x, 0] for x in changed[0]]
213 work[len(work):] = [[x, 1] for x in changed[1]]
213 work[len(work):] = [[x, 1] for x in changed[1]]
214 work.sort()
214 work.sort()
215
215
216 delta = []
216 delta = []
217 bs = 0
217 bs = 0
218
218
219 for w in work:
219 for w in work:
220 f = w[0]
220 f = w[0]
221 # bs will either be the index of the item or the insert point
221 # bs will either be the index of the item or the insert point
222 bs = bisect.bisect(addlist, f, bs)
222 bs = bisect.bisect(addlist, f, bs)
223 if bs < len(addlist):
223 if bs < len(addlist):
224 fn = addlist[bs][:addlist[bs].index('\0')]
224 fn = addlist[bs][:addlist[bs].index('\0')]
225 else:
225 else:
226 fn = None
226 fn = None
227 if w[1] == 0:
227 if w[1] == 0:
228 l = "%s\000%s%s\n" % (f, hex(map[f]),
228 l = "%s\000%s%s\n" % (f, hex(map[f]),
229 flags[f] and "x" or '')
229 flags[f] and "x" or '')
230 else:
230 else:
231 l = None
231 l = None
232 start = bs
232 start = bs
233 if fn != f:
233 if fn != f:
234 # item not found, insert a new one
234 # item not found, insert a new one
235 end = bs
235 end = bs
236 if w[1] == 1:
236 if w[1] == 1:
237 sys.stderr.write("failed to remove %s from manifest\n"
237 sys.stderr.write("failed to remove %s from manifest\n"
238 % f)
238 % f)
239 sys.exit(1)
239 sys.exit(1)
240 else:
240 else:
241 # item is found, replace/delete the existing line
241 # item is found, replace/delete the existing line
242 end = bs + 1
242 end = bs + 1
243 delta.append([start, end, offsets[start], offsets[end], l])
243 delta.append([start, end, offsets[start], offsets[end], l])
244
244
245 self.addlist = addlistdelta(addlist, delta)
245 self.addlist = addlistdelta(addlist, delta)
246 if self.mapcache[0] == self.tip():
246 if self.mapcache[0] == self.tip():
247 cachedelta = "".join(gendelta(delta))
247 cachedelta = "".join(gendelta(delta))
248 else:
248 else:
249 cachedelta = None
249 cachedelta = None
250
250
251 text = "".join(self.addlist)
251 text = "".join(self.addlist)
252 if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
252 if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
253 sys.stderr.write("manifest delta failure\n")
253 sys.stderr.write("manifest delta failure\n")
254 sys.exit(1)
254 sys.exit(1)
255 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
255 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
256 self.mapcache = (n, map, flags)
256 self.mapcache = (n, map, flags)
257 self.listcache = (text, self.addlist)
257 self.listcache = (text, self.addlist)
258 self.addlist = None
258 self.addlist = None
259
259
260 return n
260 return n
261
261
262 class changelog(revlog):
262 class changelog(revlog):
263 def __init__(self, opener):
263 def __init__(self, opener):
264 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
264 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
265
265
266 def extract(self, text):
266 def extract(self, text):
267 if not text:
267 if not text:
268 return (nullid, "", "0", [], "")
268 return (nullid, "", "0", [], "")
269 last = text.index("\n\n")
269 last = text.index("\n\n")
270 desc = text[last + 2:]
270 desc = text[last + 2:]
271 l = text[:last].splitlines()
271 l = text[:last].splitlines()
272 manifest = bin(l[0])
272 manifest = bin(l[0])
273 user = l[1]
273 user = l[1]
274 date = l[2]
274 date = l[2]
275 files = l[3:]
275 files = l[3:]
276 return (manifest, user, date, files, desc)
276 return (manifest, user, date, files, desc)
277
277
278 def read(self, node):
278 def read(self, node):
279 return self.extract(self.revision(node))
279 return self.extract(self.revision(node))
280
280
281 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
281 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
282 user=None, date=None):
282 user=None, date=None):
283 date = date or "%d %d" % (time.time(), time.timezone)
283 date = date or "%d %d" % (time.time(), time.timezone)
284 list.sort()
284 list.sort()
285 l = [hex(manifest), user, date] + list + ["", desc]
285 l = [hex(manifest), user, date] + list + ["", desc]
286 text = "\n".join(l)
286 text = "\n".join(l)
287 return self.addrevision(text, transaction, self.count(), p1, p2)
287 return self.addrevision(text, transaction, self.count(), p1, p2)
288
288
289 class dirstate:
289 class dirstate:
290 def __init__(self, opener, ui, root):
290 def __init__(self, opener, ui, root):
291 self.opener = opener
291 self.opener = opener
292 self.root = root
292 self.root = root
293 self.dirty = 0
293 self.dirty = 0
294 self.ui = ui
294 self.ui = ui
295 self.map = None
295 self.map = None
296 self.pl = None
296 self.pl = None
297 self.copies = {}
297 self.copies = {}
298 self.ignorefunc = None
298 self.ignorefunc = None
299
299
300 def wjoin(self, f):
300 def wjoin(self, f):
301 return os.path.join(self.root, f)
301 return os.path.join(self.root, f)
302
302
303 def ignore(self, f):
303 def ignore(self, f):
304 if not self.ignorefunc:
304 if not self.ignorefunc:
305 bigpat = []
305 bigpat = []
306 try:
306 try:
307 l = file(self.wjoin(".hgignore"))
307 l = file(self.wjoin(".hgignore"))
308 for pat in l:
308 for pat in l:
309 if pat != "\n":
309 if pat != "\n":
310 p = util.pconvert(pat[:-1])
310 p = util.pconvert(pat[:-1])
311 try:
311 try:
312 r = re.compile(p)
312 r = re.compile(p)
313 except:
313 except:
314 self.ui.warn("ignoring invalid ignore"
314 self.ui.warn("ignoring invalid ignore"
315 + " regular expression '%s'\n" % p)
315 + " regular expression '%s'\n" % p)
316 else:
316 else:
317 bigpat.append(util.pconvert(pat[:-1]))
317 bigpat.append(util.pconvert(pat[:-1]))
318 except IOError: pass
318 except IOError: pass
319
319
320 if bigpat:
320 if bigpat:
321 s = "(?:%s)" % (")|(?:".join(bigpat))
321 s = "(?:%s)" % (")|(?:".join(bigpat))
322 r = re.compile(s)
322 r = re.compile(s)
323 self.ignorefunc = r.search
323 self.ignorefunc = r.search
324 else:
324 else:
325 self.ignorefunc = util.never
325 self.ignorefunc = util.never
326
326
327 return self.ignorefunc(f)
327 return self.ignorefunc(f)
328
328
329 def __del__(self):
329 def __del__(self):
330 if self.dirty:
330 if self.dirty:
331 self.write()
331 self.write()
332
332
333 def __getitem__(self, key):
333 def __getitem__(self, key):
334 try:
334 try:
335 return self.map[key]
335 return self.map[key]
336 except TypeError:
336 except TypeError:
337 self.read()
337 self.read()
338 return self[key]
338 return self[key]
339
339
340 def __contains__(self, key):
340 def __contains__(self, key):
341 if not self.map: self.read()
341 if not self.map: self.read()
342 return key in self.map
342 return key in self.map
343
343
344 def parents(self):
344 def parents(self):
345 if not self.pl:
345 if not self.pl:
346 self.read()
346 self.read()
347 return self.pl
347 return self.pl
348
348
349 def markdirty(self):
349 def markdirty(self):
350 if not self.dirty:
350 if not self.dirty:
351 self.dirty = 1
351 self.dirty = 1
352
352
353 def setparents(self, p1, p2 = nullid):
353 def setparents(self, p1, p2 = nullid):
354 self.markdirty()
354 self.markdirty()
355 self.pl = p1, p2
355 self.pl = p1, p2
356
356
357 def state(self, key):
357 def state(self, key):
358 try:
358 try:
359 return self[key][0]
359 return self[key][0]
360 except KeyError:
360 except KeyError:
361 return "?"
361 return "?"
362
362
363 def read(self):
363 def read(self):
364 if self.map is not None: return self.map
364 if self.map is not None: return self.map
365
365
366 self.map = {}
366 self.map = {}
367 self.pl = [nullid, nullid]
367 self.pl = [nullid, nullid]
368 try:
368 try:
369 st = self.opener("dirstate").read()
369 st = self.opener("dirstate").read()
370 if not st: return
370 if not st: return
371 except: return
371 except: return
372
372
373 self.pl = [st[:20], st[20: 40]]
373 self.pl = [st[:20], st[20: 40]]
374
374
375 pos = 40
375 pos = 40
376 while pos < len(st):
376 while pos < len(st):
377 e = struct.unpack(">cllll", st[pos:pos+17])
377 e = struct.unpack(">cllll", st[pos:pos+17])
378 l = e[4]
378 l = e[4]
379 pos += 17
379 pos += 17
380 f = st[pos:pos + l]
380 f = st[pos:pos + l]
381 if '\0' in f:
381 if '\0' in f:
382 f, c = f.split('\0')
382 f, c = f.split('\0')
383 self.copies[f] = c
383 self.copies[f] = c
384 self.map[f] = e[:4]
384 self.map[f] = e[:4]
385 pos += l
385 pos += l
386
386
387 def copy(self, source, dest):
387 def copy(self, source, dest):
388 self.read()
388 self.read()
389 self.markdirty()
389 self.markdirty()
390 self.copies[dest] = source
390 self.copies[dest] = source
391
391
392 def copied(self, file):
392 def copied(self, file):
393 return self.copies.get(file, None)
393 return self.copies.get(file, None)
394
394
395 def update(self, files, state):
395 def update(self, files, state):
396 ''' current states:
396 ''' current states:
397 n normal
397 n normal
398 m needs merging
398 m needs merging
399 r marked for removal
399 r marked for removal
400 a marked for addition'''
400 a marked for addition'''
401
401
402 if not files: return
402 if not files: return
403 self.read()
403 self.read()
404 self.markdirty()
404 self.markdirty()
405 for f in files:
405 for f in files:
406 if state == "r":
406 if state == "r":
407 self.map[f] = ('r', 0, 0, 0)
407 self.map[f] = ('r', 0, 0, 0)
408 else:
408 else:
409 s = os.stat(os.path.join(self.root, f))
409 s = os.stat(os.path.join(self.root, f))
410 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
410 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
411
411
412 def forget(self, files):
412 def forget(self, files):
413 if not files: return
413 if not files: return
414 self.read()
414 self.read()
415 self.markdirty()
415 self.markdirty()
416 for f in files:
416 for f in files:
417 try:
417 try:
418 del self.map[f]
418 del self.map[f]
419 except KeyError:
419 except KeyError:
420 self.ui.warn("not in dirstate: %s!\n" % f)
420 self.ui.warn("not in dirstate: %s!\n" % f)
421 pass
421 pass
422
422
423 def clear(self):
423 def clear(self):
424 self.map = {}
424 self.map = {}
425 self.markdirty()
425 self.markdirty()
426
426
427 def write(self):
427 def write(self):
428 st = self.opener("dirstate", "w")
428 st = self.opener("dirstate", "w")
429 st.write("".join(self.pl))
429 st.write("".join(self.pl))
430 for f, e in self.map.items():
430 for f, e in self.map.items():
431 c = self.copied(f)
431 c = self.copied(f)
432 if c:
432 if c:
433 f = f + "\0" + c
433 f = f + "\0" + c
434 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
434 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
435 st.write(e + f)
435 st.write(e + f)
436 self.dirty = 0
436 self.dirty = 0
437
437
438 def walk(self, files = None, match = util.always):
438 def walk(self, files = None, match = util.always):
439 self.read()
439 self.read()
440 dc = self.map.copy()
440 dc = self.map.copy()
441 # walk all files by default
441 # walk all files by default
442 if not files: files = [self.root]
442 if not files: files = [self.root]
443 def traverse():
443 def traverse():
444 for f in util.unique(files):
444 for f in util.unique(files):
445 f = os.path.join(self.root, f)
445 f = os.path.join(self.root, f)
446 if os.path.isdir(f):
446 if os.path.isdir(f):
447 for dir, subdirs, fl in os.walk(f):
447 for dir, subdirs, fl in os.walk(f):
448 d = dir[len(self.root) + 1:]
448 d = dir[len(self.root) + 1:]
449 if d == '.hg':
449 nd = os.path.normpath(d)
450 if nd == '.hg':
450 subdirs[:] = []
451 subdirs[:] = []
451 continue
452 continue
452 for sd in subdirs:
453 for sd in subdirs:
453 ds = os.path.join(d, sd +'/')
454 ds = os.path.join(nd, sd +'/')
454 if self.ignore(ds) or not match(ds):
455 if self.ignore(ds) or not match(ds):
455 subdirs.remove(sd)
456 subdirs.remove(sd)
456 for fn in fl:
457 for fn in fl:
457 fn = util.pconvert(os.path.join(d, fn))
458 fn = util.pconvert(os.path.join(d, fn))
458 yield 'f', fn
459 yield 'f', fn
459 else:
460 else:
460 yield 'f', f[len(self.root) + 1:]
461 yield 'f', f[len(self.root) + 1:]
461
462
462 for k in dc.keys():
463 for k in dc.keys():
463 yield 'm', k
464 yield 'm', k
464
465
465 # yield only files that match: all in dirstate, others only if
466 # yield only files that match: all in dirstate, others only if
466 # not in .hgignore
467 # not in .hgignore
467
468
468 for src, fn in util.unique(traverse()):
469 for src, fn in util.unique(traverse()):
470 fn = os.path.normpath(fn)
469 if fn in dc:
471 if fn in dc:
470 del dc[fn]
472 del dc[fn]
471 elif self.ignore(fn):
473 elif self.ignore(fn):
472 continue
474 continue
473 if match(fn):
475 if match(fn):
474 yield src, fn
476 yield src, fn
475
477
476 def changes(self, files = None, match = util.always):
478 def changes(self, files = None, match = util.always):
477 self.read()
479 self.read()
478 dc = self.map.copy()
480 dc = self.map.copy()
479 lookup, changed, added, unknown = [], [], [], []
481 lookup, changed, added, unknown = [], [], [], []
480
482
481 for src, fn in self.walk(files, match):
483 for src, fn in self.walk(files, match):
482 try: s = os.stat(os.path.join(self.root, fn))
484 try: s = os.stat(os.path.join(self.root, fn))
483 except: continue
485 except: continue
484
486
485 if fn in dc:
487 if fn in dc:
486 c = dc[fn]
488 c = dc[fn]
487 del dc[fn]
489 del dc[fn]
488
490
489 if c[0] == 'm':
491 if c[0] == 'm':
490 changed.append(fn)
492 changed.append(fn)
491 elif c[0] == 'a':
493 elif c[0] == 'a':
492 added.append(fn)
494 added.append(fn)
493 elif c[0] == 'r':
495 elif c[0] == 'r':
494 unknown.append(fn)
496 unknown.append(fn)
495 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
497 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
496 changed.append(fn)
498 changed.append(fn)
497 elif c[1] != s.st_mode or c[3] != s.st_mtime:
499 elif c[1] != s.st_mode or c[3] != s.st_mtime:
498 lookup.append(fn)
500 lookup.append(fn)
499 else:
501 else:
500 if match(fn): unknown.append(fn)
502 if match(fn): unknown.append(fn)
501
503
502 return (lookup, changed, added, filter(match, dc.keys()), unknown)
504 return (lookup, changed, added, filter(match, dc.keys()), unknown)
503
505
504 # used to avoid circular references so destructors work
506 # used to avoid circular references so destructors work
505 def opener(base):
507 def opener(base):
506 p = base
508 p = base
507 def o(path, mode="r"):
509 def o(path, mode="r"):
508 if p.startswith("http://"):
510 if p.startswith("http://"):
509 f = os.path.join(p, urllib.quote(path))
511 f = os.path.join(p, urllib.quote(path))
510 return httprangereader.httprangereader(f)
512 return httprangereader.httprangereader(f)
511
513
512 f = os.path.join(p, path)
514 f = os.path.join(p, path)
513
515
514 mode += "b" # for that other OS
516 mode += "b" # for that other OS
515
517
516 if mode[0] != "r":
518 if mode[0] != "r":
517 try:
519 try:
518 s = os.stat(f)
520 s = os.stat(f)
519 except OSError:
521 except OSError:
520 d = os.path.dirname(f)
522 d = os.path.dirname(f)
521 if not os.path.isdir(d):
523 if not os.path.isdir(d):
522 os.makedirs(d)
524 os.makedirs(d)
523 else:
525 else:
524 if s.st_nlink > 1:
526 if s.st_nlink > 1:
525 file(f + ".tmp", "wb").write(file(f, "rb").read())
527 file(f + ".tmp", "wb").write(file(f, "rb").read())
526 util.rename(f+".tmp", f)
528 util.rename(f+".tmp", f)
527
529
528 return file(f, mode)
530 return file(f, mode)
529
531
530 return o
532 return o
531
533
532 class RepoError(Exception): pass
534 class RepoError(Exception): pass
533
535
534 class localrepository:
536 class localrepository:
535 def __init__(self, ui, path=None, create=0):
537 def __init__(self, ui, path=None, create=0):
536 self.remote = 0
538 self.remote = 0
537 if path and path.startswith("http://"):
539 if path and path.startswith("http://"):
538 self.remote = 1
540 self.remote = 1
539 self.path = path
541 self.path = path
540 else:
542 else:
541 if not path:
543 if not path:
542 p = os.getcwd()
544 p = os.getcwd()
543 while not os.path.isdir(os.path.join(p, ".hg")):
545 while not os.path.isdir(os.path.join(p, ".hg")):
544 oldp = p
546 oldp = p
545 p = os.path.dirname(p)
547 p = os.path.dirname(p)
546 if p == oldp: raise RepoError("no repo found")
548 if p == oldp: raise RepoError("no repo found")
547 path = p
549 path = p
548 self.path = os.path.join(path, ".hg")
550 self.path = os.path.join(path, ".hg")
549
551
550 if not create and not os.path.isdir(self.path):
552 if not create and not os.path.isdir(self.path):
551 raise RepoError("repository %s not found" % self.path)
553 raise RepoError("repository %s not found" % self.path)
552
554
553 self.root = path
555 self.root = path
554 self.ui = ui
556 self.ui = ui
555
557
556 if create:
558 if create:
557 os.mkdir(self.path)
559 os.mkdir(self.path)
558 os.mkdir(self.join("data"))
560 os.mkdir(self.join("data"))
559
561
560 self.opener = opener(self.path)
562 self.opener = opener(self.path)
561 self.wopener = opener(self.root)
563 self.wopener = opener(self.root)
562 self.manifest = manifest(self.opener)
564 self.manifest = manifest(self.opener)
563 self.changelog = changelog(self.opener)
565 self.changelog = changelog(self.opener)
564 self.tagscache = None
566 self.tagscache = None
565 self.nodetagscache = None
567 self.nodetagscache = None
566
568
567 if not self.remote:
569 if not self.remote:
568 self.dirstate = dirstate(self.opener, ui, self.root)
570 self.dirstate = dirstate(self.opener, ui, self.root)
569 try:
571 try:
570 self.ui.readconfig(self.opener("hgrc"))
572 self.ui.readconfig(self.opener("hgrc"))
571 except IOError: pass
573 except IOError: pass
572
574
573 def hook(self, name, **args):
575 def hook(self, name, **args):
574 s = self.ui.config("hooks", name)
576 s = self.ui.config("hooks", name)
575 if s:
577 if s:
576 self.ui.note("running hook %s: %s\n" % (name, s))
578 self.ui.note("running hook %s: %s\n" % (name, s))
577 old = {}
579 old = {}
578 for k, v in args.items():
580 for k, v in args.items():
579 k = k.upper()
581 k = k.upper()
580 old[k] = os.environ.get(k, None)
582 old[k] = os.environ.get(k, None)
581 os.environ[k] = v
583 os.environ[k] = v
582
584
583 r = os.system(s)
585 r = os.system(s)
584
586
585 for k, v in old.items():
587 for k, v in old.items():
586 if v != None:
588 if v != None:
587 os.environ[k] = v
589 os.environ[k] = v
588 else:
590 else:
589 del os.environ[k]
591 del os.environ[k]
590
592
591 if r:
593 if r:
592 self.ui.warn("abort: %s hook failed with status %d!\n" %
594 self.ui.warn("abort: %s hook failed with status %d!\n" %
593 (name, r))
595 (name, r))
594 return False
596 return False
595 return True
597 return True
596
598
597 def tags(self):
599 def tags(self):
598 '''return a mapping of tag to node'''
600 '''return a mapping of tag to node'''
599 if not self.tagscache:
601 if not self.tagscache:
600 self.tagscache = {}
602 self.tagscache = {}
601 def addtag(self, k, n):
603 def addtag(self, k, n):
602 try:
604 try:
603 bin_n = bin(n)
605 bin_n = bin(n)
604 except TypeError:
606 except TypeError:
605 bin_n = ''
607 bin_n = ''
606 self.tagscache[k.strip()] = bin_n
608 self.tagscache[k.strip()] = bin_n
607
609
608 try:
610 try:
609 # read each head of the tags file, ending with the tip
611 # read each head of the tags file, ending with the tip
610 # and add each tag found to the map, with "newer" ones
612 # and add each tag found to the map, with "newer" ones
611 # taking precedence
613 # taking precedence
612 fl = self.file(".hgtags")
614 fl = self.file(".hgtags")
613 h = fl.heads()
615 h = fl.heads()
614 h.reverse()
616 h.reverse()
615 for r in h:
617 for r in h:
616 for l in fl.revision(r).splitlines():
618 for l in fl.revision(r).splitlines():
617 if l:
619 if l:
618 n, k = l.split(" ", 1)
620 n, k = l.split(" ", 1)
619 addtag(self, k, n)
621 addtag(self, k, n)
620 except KeyError:
622 except KeyError:
621 pass
623 pass
622
624
623 try:
625 try:
624 f = self.opener("localtags")
626 f = self.opener("localtags")
625 for l in f:
627 for l in f:
626 n, k = l.split(" ", 1)
628 n, k = l.split(" ", 1)
627 addtag(self, k, n)
629 addtag(self, k, n)
628 except IOError:
630 except IOError:
629 pass
631 pass
630
632
631 self.tagscache['tip'] = self.changelog.tip()
633 self.tagscache['tip'] = self.changelog.tip()
632
634
633 return self.tagscache
635 return self.tagscache
634
636
635 def tagslist(self):
637 def tagslist(self):
636 '''return a list of tags ordered by revision'''
638 '''return a list of tags ordered by revision'''
637 l = []
639 l = []
638 for t, n in self.tags().items():
640 for t, n in self.tags().items():
639 try:
641 try:
640 r = self.changelog.rev(n)
642 r = self.changelog.rev(n)
641 except:
643 except:
642 r = -2 # sort to the beginning of the list if unknown
644 r = -2 # sort to the beginning of the list if unknown
643 l.append((r,t,n))
645 l.append((r,t,n))
644 l.sort()
646 l.sort()
645 return [(t,n) for r,t,n in l]
647 return [(t,n) for r,t,n in l]
646
648
647 def nodetags(self, node):
649 def nodetags(self, node):
648 '''return the tags associated with a node'''
650 '''return the tags associated with a node'''
649 if not self.nodetagscache:
651 if not self.nodetagscache:
650 self.nodetagscache = {}
652 self.nodetagscache = {}
651 for t,n in self.tags().items():
653 for t,n in self.tags().items():
652 self.nodetagscache.setdefault(n,[]).append(t)
654 self.nodetagscache.setdefault(n,[]).append(t)
653 return self.nodetagscache.get(node, [])
655 return self.nodetagscache.get(node, [])
654
656
655 def lookup(self, key):
657 def lookup(self, key):
656 try:
658 try:
657 return self.tags()[key]
659 return self.tags()[key]
658 except KeyError:
660 except KeyError:
659 try:
661 try:
660 return self.changelog.lookup(key)
662 return self.changelog.lookup(key)
661 except:
663 except:
662 raise RepoError("unknown revision '%s'" % key)
664 raise RepoError("unknown revision '%s'" % key)
663
665
664 def dev(self):
666 def dev(self):
665 if self.remote: return -1
667 if self.remote: return -1
666 return os.stat(self.path).st_dev
668 return os.stat(self.path).st_dev
667
669
668 def join(self, f):
670 def join(self, f):
669 return os.path.join(self.path, f)
671 return os.path.join(self.path, f)
670
672
671 def wjoin(self, f):
673 def wjoin(self, f):
672 return os.path.join(self.root, f)
674 return os.path.join(self.root, f)
673
675
674 def file(self, f):
676 def file(self, f):
675 if f[0] == '/': f = f[1:]
677 if f[0] == '/': f = f[1:]
676 return filelog(self.opener, f)
678 return filelog(self.opener, f)
677
679
678 def getcwd(self):
680 def getcwd(self):
679 cwd = os.getcwd()
681 cwd = os.getcwd()
680 if cwd == self.root: return ''
682 if cwd == self.root: return ''
681 return cwd[len(self.root) + 1:]
683 return cwd[len(self.root) + 1:]
682
684
683 def wfile(self, f, mode='r'):
685 def wfile(self, f, mode='r'):
684 return self.wopener(f, mode)
686 return self.wopener(f, mode)
685
687
686 def transaction(self):
688 def transaction(self):
687 # save dirstate for undo
689 # save dirstate for undo
688 try:
690 try:
689 ds = self.opener("dirstate").read()
691 ds = self.opener("dirstate").read()
690 except IOError:
692 except IOError:
691 ds = ""
693 ds = ""
692 self.opener("journal.dirstate", "w").write(ds)
694 self.opener("journal.dirstate", "w").write(ds)
693
695
694 def after():
696 def after():
695 util.rename(self.join("journal"), self.join("undo"))
697 util.rename(self.join("journal"), self.join("undo"))
696 util.rename(self.join("journal.dirstate"),
698 util.rename(self.join("journal.dirstate"),
697 self.join("undo.dirstate"))
699 self.join("undo.dirstate"))
698
700
699 return transaction.transaction(self.ui.warn, self.opener,
701 return transaction.transaction(self.ui.warn, self.opener,
700 self.join("journal"), after)
702 self.join("journal"), after)
701
703
702 def recover(self):
704 def recover(self):
703 lock = self.lock()
705 lock = self.lock()
704 if os.path.exists(self.join("journal")):
706 if os.path.exists(self.join("journal")):
705 self.ui.status("rolling back interrupted transaction\n")
707 self.ui.status("rolling back interrupted transaction\n")
706 return transaction.rollback(self.opener, self.join("journal"))
708 return transaction.rollback(self.opener, self.join("journal"))
707 else:
709 else:
708 self.ui.warn("no interrupted transaction available\n")
710 self.ui.warn("no interrupted transaction available\n")
709
711
710 def undo(self):
712 def undo(self):
711 lock = self.lock()
713 lock = self.lock()
712 if os.path.exists(self.join("undo")):
714 if os.path.exists(self.join("undo")):
713 self.ui.status("rolling back last transaction\n")
715 self.ui.status("rolling back last transaction\n")
714 transaction.rollback(self.opener, self.join("undo"))
716 transaction.rollback(self.opener, self.join("undo"))
715 self.dirstate = None
717 self.dirstate = None
716 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
718 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
717 self.dirstate = dirstate(self.opener, self.ui, self.root)
719 self.dirstate = dirstate(self.opener, self.ui, self.root)
718 else:
720 else:
719 self.ui.warn("no undo information available\n")
721 self.ui.warn("no undo information available\n")
720
722
721 def lock(self, wait = 1):
723 def lock(self, wait = 1):
722 try:
724 try:
723 return lock.lock(self.join("lock"), 0)
725 return lock.lock(self.join("lock"), 0)
724 except lock.LockHeld, inst:
726 except lock.LockHeld, inst:
725 if wait:
727 if wait:
726 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
728 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
727 return lock.lock(self.join("lock"), wait)
729 return lock.lock(self.join("lock"), wait)
728 raise inst
730 raise inst
729
731
730 def rawcommit(self, files, text, user, date, p1=None, p2=None):
732 def rawcommit(self, files, text, user, date, p1=None, p2=None):
731 orig_parent = self.dirstate.parents()[0] or nullid
733 orig_parent = self.dirstate.parents()[0] or nullid
732 p1 = p1 or self.dirstate.parents()[0] or nullid
734 p1 = p1 or self.dirstate.parents()[0] or nullid
733 p2 = p2 or self.dirstate.parents()[1] or nullid
735 p2 = p2 or self.dirstate.parents()[1] or nullid
734 c1 = self.changelog.read(p1)
736 c1 = self.changelog.read(p1)
735 c2 = self.changelog.read(p2)
737 c2 = self.changelog.read(p2)
736 m1 = self.manifest.read(c1[0])
738 m1 = self.manifest.read(c1[0])
737 mf1 = self.manifest.readflags(c1[0])
739 mf1 = self.manifest.readflags(c1[0])
738 m2 = self.manifest.read(c2[0])
740 m2 = self.manifest.read(c2[0])
739
741
740 if orig_parent == p1:
742 if orig_parent == p1:
741 update_dirstate = 1
743 update_dirstate = 1
742 else:
744 else:
743 update_dirstate = 0
745 update_dirstate = 0
744
746
745 tr = self.transaction()
747 tr = self.transaction()
746 mm = m1.copy()
748 mm = m1.copy()
747 mfm = mf1.copy()
749 mfm = mf1.copy()
748 linkrev = self.changelog.count()
750 linkrev = self.changelog.count()
749 for f in files:
751 for f in files:
750 try:
752 try:
751 t = self.wfile(f).read()
753 t = self.wfile(f).read()
752 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
754 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
753 r = self.file(f)
755 r = self.file(f)
754 mfm[f] = tm
756 mfm[f] = tm
755 mm[f] = r.add(t, {}, tr, linkrev,
757 mm[f] = r.add(t, {}, tr, linkrev,
756 m1.get(f, nullid), m2.get(f, nullid))
758 m1.get(f, nullid), m2.get(f, nullid))
757 if update_dirstate:
759 if update_dirstate:
758 self.dirstate.update([f], "n")
760 self.dirstate.update([f], "n")
759 except IOError:
761 except IOError:
760 try:
762 try:
761 del mm[f]
763 del mm[f]
762 del mfm[f]
764 del mfm[f]
763 if update_dirstate:
765 if update_dirstate:
764 self.dirstate.forget([f])
766 self.dirstate.forget([f])
765 except:
767 except:
766 # deleted from p2?
768 # deleted from p2?
767 pass
769 pass
768
770
769 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
771 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
770 user = user or self.ui.username()
772 user = user or self.ui.username()
771 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
773 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
772 tr.close()
774 tr.close()
773 if update_dirstate:
775 if update_dirstate:
774 self.dirstate.setparents(n, nullid)
776 self.dirstate.setparents(n, nullid)
775
777
776 def commit(self, files = None, text = "", user = None, date = None,
778 def commit(self, files = None, text = "", user = None, date = None,
777 match = util.always):
779 match = util.always):
778 commit = []
780 commit = []
779 remove = []
781 remove = []
780 if files:
782 if files:
781 for f in files:
783 for f in files:
782 s = self.dirstate.state(f)
784 s = self.dirstate.state(f)
783 if s in 'nmai':
785 if s in 'nmai':
784 commit.append(f)
786 commit.append(f)
785 elif s == 'r':
787 elif s == 'r':
786 remove.append(f)
788 remove.append(f)
787 else:
789 else:
788 self.ui.warn("%s not tracked!\n" % f)
790 self.ui.warn("%s not tracked!\n" % f)
789 else:
791 else:
790 (c, a, d, u) = self.changes(match = match)
792 (c, a, d, u) = self.changes(match = match)
791 commit = c + a
793 commit = c + a
792 remove = d
794 remove = d
793
795
794 if not commit and not remove:
796 if not commit and not remove:
795 self.ui.status("nothing changed\n")
797 self.ui.status("nothing changed\n")
796 return
798 return
797
799
798 if not self.hook("precommit"):
800 if not self.hook("precommit"):
799 return 1
801 return 1
800
802
801 p1, p2 = self.dirstate.parents()
803 p1, p2 = self.dirstate.parents()
802 c1 = self.changelog.read(p1)
804 c1 = self.changelog.read(p1)
803 c2 = self.changelog.read(p2)
805 c2 = self.changelog.read(p2)
804 m1 = self.manifest.read(c1[0])
806 m1 = self.manifest.read(c1[0])
805 mf1 = self.manifest.readflags(c1[0])
807 mf1 = self.manifest.readflags(c1[0])
806 m2 = self.manifest.read(c2[0])
808 m2 = self.manifest.read(c2[0])
807 lock = self.lock()
809 lock = self.lock()
808 tr = self.transaction()
810 tr = self.transaction()
809
811
810 # check in files
812 # check in files
811 new = {}
813 new = {}
812 linkrev = self.changelog.count()
814 linkrev = self.changelog.count()
813 commit.sort()
815 commit.sort()
814 for f in commit:
816 for f in commit:
815 self.ui.note(f + "\n")
817 self.ui.note(f + "\n")
816 try:
818 try:
817 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
819 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
818 t = self.wfile(f).read()
820 t = self.wfile(f).read()
819 except IOError:
821 except IOError:
820 self.ui.warn("trouble committing %s!\n" % f)
822 self.ui.warn("trouble committing %s!\n" % f)
821 raise
823 raise
822
824
823 meta = {}
825 meta = {}
824 cp = self.dirstate.copied(f)
826 cp = self.dirstate.copied(f)
825 if cp:
827 if cp:
826 meta["copy"] = cp
828 meta["copy"] = cp
827 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
829 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
828 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
830 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
829
831
830 r = self.file(f)
832 r = self.file(f)
831 fp1 = m1.get(f, nullid)
833 fp1 = m1.get(f, nullid)
832 fp2 = m2.get(f, nullid)
834 fp2 = m2.get(f, nullid)
833 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
835 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
834
836
835 # update manifest
837 # update manifest
836 m1.update(new)
838 m1.update(new)
837 for f in remove:
839 for f in remove:
838 if f in m1:
840 if f in m1:
839 del m1[f]
841 del m1[f]
840 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
842 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
841 (new, remove))
843 (new, remove))
842
844
843 # add changeset
845 # add changeset
844 new = new.keys()
846 new = new.keys()
845 new.sort()
847 new.sort()
846
848
847 if not text:
849 if not text:
848 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
850 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
849 edittext += "".join(["HG: changed %s\n" % f for f in new])
851 edittext += "".join(["HG: changed %s\n" % f for f in new])
850 edittext += "".join(["HG: removed %s\n" % f for f in remove])
852 edittext += "".join(["HG: removed %s\n" % f for f in remove])
851 edittext = self.ui.edit(edittext)
853 edittext = self.ui.edit(edittext)
852 if not edittext.rstrip():
854 if not edittext.rstrip():
853 return 1
855 return 1
854 text = edittext
856 text = edittext
855
857
856 user = user or self.ui.username()
858 user = user or self.ui.username()
857 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
859 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
858
860
859 tr.close()
861 tr.close()
860
862
861 self.dirstate.setparents(n)
863 self.dirstate.setparents(n)
862 self.dirstate.update(new, "n")
864 self.dirstate.update(new, "n")
863 self.dirstate.forget(remove)
865 self.dirstate.forget(remove)
864
866
865 if not self.hook("commit", node=hex(n)):
867 if not self.hook("commit", node=hex(n)):
866 return 1
868 return 1
867
869
868 def walk(self, node = None, files = [], match = util.always):
870 def walk(self, node = None, files = [], match = util.always):
869 if node:
871 if node:
870 for fn in self.manifest.read(self.changelog.read(node)[0]):
872 for fn in self.manifest.read(self.changelog.read(node)[0]):
871 yield 'm', fn
873 if match(fn): yield 'm', fn
872 else:
874 else:
873 for src, fn in self.dirstate.walk(files, match):
875 for src, fn in self.dirstate.walk(files, match):
874 yield src, fn
876 yield src, fn
875
877
876 def changes(self, node1 = None, node2 = None, files = [],
878 def changes(self, node1 = None, node2 = None, files = [],
877 match = util.always):
879 match = util.always):
878 mf2, u = None, []
880 mf2, u = None, []
879
881
880 def fcmp(fn, mf):
882 def fcmp(fn, mf):
881 t1 = self.wfile(fn).read()
883 t1 = self.wfile(fn).read()
882 t2 = self.file(fn).revision(mf[fn])
884 t2 = self.file(fn).revision(mf[fn])
883 return cmp(t1, t2)
885 return cmp(t1, t2)
884
886
885 def mfmatches(node):
887 def mfmatches(node):
886 mf = dict(self.manifest.read(node))
888 mf = dict(self.manifest.read(node))
887 for fn in mf.keys():
889 for fn in mf.keys():
888 if not match(fn):
890 if not match(fn):
889 del mf[fn]
891 del mf[fn]
890 return mf
892 return mf
891
893
892 # are we comparing the working directory?
894 # are we comparing the working directory?
893 if not node2:
895 if not node2:
894 l, c, a, d, u = self.dirstate.changes(files, match)
896 l, c, a, d, u = self.dirstate.changes(files, match)
895
897
896 # are we comparing working dir against its parent?
898 # are we comparing working dir against its parent?
897 if not node1:
899 if not node1:
898 if l:
900 if l:
899 # do a full compare of any files that might have changed
901 # do a full compare of any files that might have changed
900 change = self.changelog.read(self.dirstate.parents()[0])
902 change = self.changelog.read(self.dirstate.parents()[0])
901 mf2 = mfmatches(change[0])
903 mf2 = mfmatches(change[0])
902 for f in l:
904 for f in l:
903 if fcmp(f, mf2):
905 if fcmp(f, mf2):
904 c.append(f)
906 c.append(f)
905
907
906 for l in c, a, d, u:
908 for l in c, a, d, u:
907 l.sort()
909 l.sort()
908
910
909 return (c, a, d, u)
911 return (c, a, d, u)
910
912
911 # are we comparing working dir against non-tip?
913 # are we comparing working dir against non-tip?
912 # generate a pseudo-manifest for the working dir
914 # generate a pseudo-manifest for the working dir
913 if not node2:
915 if not node2:
914 if not mf2:
916 if not mf2:
915 change = self.changelog.read(self.dirstate.parents()[0])
917 change = self.changelog.read(self.dirstate.parents()[0])
916 mf2 = mfmatches(change[0])
918 mf2 = mfmatches(change[0])
917 for f in a + c + l:
919 for f in a + c + l:
918 mf2[f] = ""
920 mf2[f] = ""
919 for f in d:
921 for f in d:
920 if f in mf2: del mf2[f]
922 if f in mf2: del mf2[f]
921 else:
923 else:
922 change = self.changelog.read(node2)
924 change = self.changelog.read(node2)
923 mf2 = mfmatches(change[0])
925 mf2 = mfmatches(change[0])
924
926
925 # flush lists from dirstate before comparing manifests
927 # flush lists from dirstate before comparing manifests
926 c, a = [], []
928 c, a = [], []
927
929
928 change = self.changelog.read(node1)
930 change = self.changelog.read(node1)
929 mf1 = mfmatches(change[0])
931 mf1 = mfmatches(change[0])
930
932
931 for fn in mf2:
933 for fn in mf2:
932 if mf1.has_key(fn):
934 if mf1.has_key(fn):
933 if mf1[fn] != mf2[fn]:
935 if mf1[fn] != mf2[fn]:
934 if mf2[fn] != "" or fcmp(fn, mf1):
936 if mf2[fn] != "" or fcmp(fn, mf1):
935 c.append(fn)
937 c.append(fn)
936 del mf1[fn]
938 del mf1[fn]
937 else:
939 else:
938 a.append(fn)
940 a.append(fn)
939
941
940 d = mf1.keys()
942 d = mf1.keys()
941
943
942 for l in c, a, d, u:
944 for l in c, a, d, u:
943 l.sort()
945 l.sort()
944
946
945 return (c, a, d, u)
947 return (c, a, d, u)
946
948
947 def add(self, list):
949 def add(self, list):
948 for f in list:
950 for f in list:
949 p = self.wjoin(f)
951 p = self.wjoin(f)
950 if not os.path.exists(p):
952 if not os.path.exists(p):
951 self.ui.warn("%s does not exist!\n" % f)
953 self.ui.warn("%s does not exist!\n" % f)
952 elif not os.path.isfile(p):
954 elif not os.path.isfile(p):
953 self.ui.warn("%s not added: only files supported currently\n" % f)
955 self.ui.warn("%s not added: only files supported currently\n" % f)
954 elif self.dirstate.state(f) in 'an':
956 elif self.dirstate.state(f) in 'an':
955 self.ui.warn("%s already tracked!\n" % f)
957 self.ui.warn("%s already tracked!\n" % f)
956 else:
958 else:
957 self.dirstate.update([f], "a")
959 self.dirstate.update([f], "a")
958
960
959 def forget(self, list):
961 def forget(self, list):
960 for f in list:
962 for f in list:
961 if self.dirstate.state(f) not in 'ai':
963 if self.dirstate.state(f) not in 'ai':
962 self.ui.warn("%s not added!\n" % f)
964 self.ui.warn("%s not added!\n" % f)
963 else:
965 else:
964 self.dirstate.forget([f])
966 self.dirstate.forget([f])
965
967
966 def remove(self, list):
968 def remove(self, list):
967 for f in list:
969 for f in list:
968 p = self.wjoin(f)
970 p = self.wjoin(f)
969 if os.path.exists(p):
971 if os.path.exists(p):
970 self.ui.warn("%s still exists!\n" % f)
972 self.ui.warn("%s still exists!\n" % f)
971 elif self.dirstate.state(f) == 'a':
973 elif self.dirstate.state(f) == 'a':
972 self.ui.warn("%s never committed!\n" % f)
974 self.ui.warn("%s never committed!\n" % f)
973 self.dirstate.forget([f])
975 self.dirstate.forget([f])
974 elif f not in self.dirstate:
976 elif f not in self.dirstate:
975 self.ui.warn("%s not tracked!\n" % f)
977 self.ui.warn("%s not tracked!\n" % f)
976 else:
978 else:
977 self.dirstate.update([f], "r")
979 self.dirstate.update([f], "r")
978
980
979 def copy(self, source, dest):
981 def copy(self, source, dest):
980 p = self.wjoin(dest)
982 p = self.wjoin(dest)
981 if not os.path.exists(p):
983 if not os.path.exists(p):
982 self.ui.warn("%s does not exist!\n" % dest)
984 self.ui.warn("%s does not exist!\n" % dest)
983 elif not os.path.isfile(p):
985 elif not os.path.isfile(p):
984 self.ui.warn("copy failed: %s is not a file\n" % dest)
986 self.ui.warn("copy failed: %s is not a file\n" % dest)
985 else:
987 else:
986 if self.dirstate.state(dest) == '?':
988 if self.dirstate.state(dest) == '?':
987 self.dirstate.update([dest], "a")
989 self.dirstate.update([dest], "a")
988 self.dirstate.copy(source, dest)
990 self.dirstate.copy(source, dest)
989
991
990 def heads(self):
992 def heads(self):
991 return self.changelog.heads()
993 return self.changelog.heads()
992
994
993 def branches(self, nodes):
995 def branches(self, nodes):
994 if not nodes: nodes = [self.changelog.tip()]
996 if not nodes: nodes = [self.changelog.tip()]
995 b = []
997 b = []
996 for n in nodes:
998 for n in nodes:
997 t = n
999 t = n
998 while n:
1000 while n:
999 p = self.changelog.parents(n)
1001 p = self.changelog.parents(n)
1000 if p[1] != nullid or p[0] == nullid:
1002 if p[1] != nullid or p[0] == nullid:
1001 b.append((t, n, p[0], p[1]))
1003 b.append((t, n, p[0], p[1]))
1002 break
1004 break
1003 n = p[0]
1005 n = p[0]
1004 return b
1006 return b
1005
1007
1006 def between(self, pairs):
1008 def between(self, pairs):
1007 r = []
1009 r = []
1008
1010
1009 for top, bottom in pairs:
1011 for top, bottom in pairs:
1010 n, l, i = top, [], 0
1012 n, l, i = top, [], 0
1011 f = 1
1013 f = 1
1012
1014
1013 while n != bottom:
1015 while n != bottom:
1014 p = self.changelog.parents(n)[0]
1016 p = self.changelog.parents(n)[0]
1015 if i == f:
1017 if i == f:
1016 l.append(n)
1018 l.append(n)
1017 f = f * 2
1019 f = f * 2
1018 n = p
1020 n = p
1019 i += 1
1021 i += 1
1020
1022
1021 r.append(l)
1023 r.append(l)
1022
1024
1023 return r
1025 return r
1024
1026
1025 def newer(self, nodes):
1027 def newer(self, nodes):
1026 m = {}
1028 m = {}
1027 nl = []
1029 nl = []
1028 pm = {}
1030 pm = {}
1029 cl = self.changelog
1031 cl = self.changelog
1030 t = l = cl.count()
1032 t = l = cl.count()
1031
1033
1032 # find the lowest numbered node
1034 # find the lowest numbered node
1033 for n in nodes:
1035 for n in nodes:
1034 l = min(l, cl.rev(n))
1036 l = min(l, cl.rev(n))
1035 m[n] = 1
1037 m[n] = 1
1036
1038
1037 for i in xrange(l, t):
1039 for i in xrange(l, t):
1038 n = cl.node(i)
1040 n = cl.node(i)
1039 if n in m: # explicitly listed
1041 if n in m: # explicitly listed
1040 pm[n] = 1
1042 pm[n] = 1
1041 nl.append(n)
1043 nl.append(n)
1042 continue
1044 continue
1043 for p in cl.parents(n):
1045 for p in cl.parents(n):
1044 if p in pm: # parent listed
1046 if p in pm: # parent listed
1045 pm[n] = 1
1047 pm[n] = 1
1046 nl.append(n)
1048 nl.append(n)
1047 break
1049 break
1048
1050
1049 return nl
1051 return nl
1050
1052
1051 def findincoming(self, remote, base={}):
1053 def findincoming(self, remote, base={}):
1052 m = self.changelog.nodemap
1054 m = self.changelog.nodemap
1053 search = []
1055 search = []
1054 fetch = []
1056 fetch = []
1055 seen = {}
1057 seen = {}
1056 seenbranch = {}
1058 seenbranch = {}
1057
1059
1058 # assume we're closer to the tip than the root
1060 # assume we're closer to the tip than the root
1059 # and start by examining the heads
1061 # and start by examining the heads
1060 self.ui.status("searching for changes\n")
1062 self.ui.status("searching for changes\n")
1061 heads = remote.heads()
1063 heads = remote.heads()
1062 unknown = []
1064 unknown = []
1063 for h in heads:
1065 for h in heads:
1064 if h not in m:
1066 if h not in m:
1065 unknown.append(h)
1067 unknown.append(h)
1066 else:
1068 else:
1067 base[h] = 1
1069 base[h] = 1
1068
1070
1069 if not unknown:
1071 if not unknown:
1070 return None
1072 return None
1071
1073
1072 rep = {}
1074 rep = {}
1073 reqcnt = 0
1075 reqcnt = 0
1074
1076
1075 # search through remote branches
1077 # search through remote branches
1076 # a 'branch' here is a linear segment of history, with four parts:
1078 # a 'branch' here is a linear segment of history, with four parts:
1077 # head, root, first parent, second parent
1079 # head, root, first parent, second parent
1078 # (a branch always has two parents (or none) by definition)
1080 # (a branch always has two parents (or none) by definition)
1079 unknown = remote.branches(unknown)
1081 unknown = remote.branches(unknown)
1080 while unknown:
1082 while unknown:
1081 r = []
1083 r = []
1082 while unknown:
1084 while unknown:
1083 n = unknown.pop(0)
1085 n = unknown.pop(0)
1084 if n[0] in seen:
1086 if n[0] in seen:
1085 continue
1087 continue
1086
1088
1087 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
1089 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
1088 if n[0] == nullid:
1090 if n[0] == nullid:
1089 break
1091 break
1090 if n in seenbranch:
1092 if n in seenbranch:
1091 self.ui.debug("branch already found\n")
1093 self.ui.debug("branch already found\n")
1092 continue
1094 continue
1093 if n[1] and n[1] in m: # do we know the base?
1095 if n[1] and n[1] in m: # do we know the base?
1094 self.ui.debug("found incomplete branch %s:%s\n"
1096 self.ui.debug("found incomplete branch %s:%s\n"
1095 % (short(n[0]), short(n[1])))
1097 % (short(n[0]), short(n[1])))
1096 search.append(n) # schedule branch range for scanning
1098 search.append(n) # schedule branch range for scanning
1097 seenbranch[n] = 1
1099 seenbranch[n] = 1
1098 else:
1100 else:
1099 if n[1] not in seen and n[1] not in fetch:
1101 if n[1] not in seen and n[1] not in fetch:
1100 if n[2] in m and n[3] in m:
1102 if n[2] in m and n[3] in m:
1101 self.ui.debug("found new changeset %s\n" %
1103 self.ui.debug("found new changeset %s\n" %
1102 short(n[1]))
1104 short(n[1]))
1103 fetch.append(n[1]) # earliest unknown
1105 fetch.append(n[1]) # earliest unknown
1104 base[n[2]] = 1 # latest known
1106 base[n[2]] = 1 # latest known
1105 continue
1107 continue
1106
1108
1107 for a in n[2:4]:
1109 for a in n[2:4]:
1108 if a not in rep:
1110 if a not in rep:
1109 r.append(a)
1111 r.append(a)
1110 rep[a] = 1
1112 rep[a] = 1
1111
1113
1112 seen[n[0]] = 1
1114 seen[n[0]] = 1
1113
1115
1114 if r:
1116 if r:
1115 reqcnt += 1
1117 reqcnt += 1
1116 self.ui.debug("request %d: %s\n" %
1118 self.ui.debug("request %d: %s\n" %
1117 (reqcnt, " ".join(map(short, r))))
1119 (reqcnt, " ".join(map(short, r))))
1118 for p in range(0, len(r), 10):
1120 for p in range(0, len(r), 10):
1119 for b in remote.branches(r[p:p+10]):
1121 for b in remote.branches(r[p:p+10]):
1120 self.ui.debug("received %s:%s\n" %
1122 self.ui.debug("received %s:%s\n" %
1121 (short(b[0]), short(b[1])))
1123 (short(b[0]), short(b[1])))
1122 if b[0] not in m and b[0] not in seen:
1124 if b[0] not in m and b[0] not in seen:
1123 unknown.append(b)
1125 unknown.append(b)
1124
1126
1125 # do binary search on the branches we found
1127 # do binary search on the branches we found
1126 while search:
1128 while search:
1127 n = search.pop(0)
1129 n = search.pop(0)
1128 reqcnt += 1
1130 reqcnt += 1
1129 l = remote.between([(n[0], n[1])])[0]
1131 l = remote.between([(n[0], n[1])])[0]
1130 l.append(n[1])
1132 l.append(n[1])
1131 p = n[0]
1133 p = n[0]
1132 f = 1
1134 f = 1
1133 for i in l:
1135 for i in l:
1134 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
1136 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
1135 if i in m:
1137 if i in m:
1136 if f <= 2:
1138 if f <= 2:
1137 self.ui.debug("found new branch changeset %s\n" %
1139 self.ui.debug("found new branch changeset %s\n" %
1138 short(p))
1140 short(p))
1139 fetch.append(p)
1141 fetch.append(p)
1140 base[i] = 1
1142 base[i] = 1
1141 else:
1143 else:
1142 self.ui.debug("narrowed branch search to %s:%s\n"
1144 self.ui.debug("narrowed branch search to %s:%s\n"
1143 % (short(p), short(i)))
1145 % (short(p), short(i)))
1144 search.append((p, i))
1146 search.append((p, i))
1145 break
1147 break
1146 p, f = i, f * 2
1148 p, f = i, f * 2
1147
1149
1148 # sanity check our fetch list
1150 # sanity check our fetch list
1149 for f in fetch:
1151 for f in fetch:
1150 if f in m:
1152 if f in m:
1151 raise RepoError("already have changeset " + short(f[:4]))
1153 raise RepoError("already have changeset " + short(f[:4]))
1152
1154
1153 if base.keys() == [nullid]:
1155 if base.keys() == [nullid]:
1154 self.ui.warn("warning: pulling from an unrelated repository!\n")
1156 self.ui.warn("warning: pulling from an unrelated repository!\n")
1155
1157
1156 self.ui.note("adding new changesets starting at " +
1158 self.ui.note("adding new changesets starting at " +
1157 " ".join([short(f) for f in fetch]) + "\n")
1159 " ".join([short(f) for f in fetch]) + "\n")
1158
1160
1159 self.ui.debug("%d total queries\n" % reqcnt)
1161 self.ui.debug("%d total queries\n" % reqcnt)
1160
1162
1161 return fetch
1163 return fetch
1162
1164
1163 def findoutgoing(self, remote):
1165 def findoutgoing(self, remote):
1164 base = {}
1166 base = {}
1165 self.findincoming(remote, base)
1167 self.findincoming(remote, base)
1166 remain = dict.fromkeys(self.changelog.nodemap)
1168 remain = dict.fromkeys(self.changelog.nodemap)
1167
1169
1168 # prune everything remote has from the tree
1170 # prune everything remote has from the tree
1169 del remain[nullid]
1171 del remain[nullid]
1170 remove = base.keys()
1172 remove = base.keys()
1171 while remove:
1173 while remove:
1172 n = remove.pop(0)
1174 n = remove.pop(0)
1173 if n in remain:
1175 if n in remain:
1174 del remain[n]
1176 del remain[n]
1175 for p in self.changelog.parents(n):
1177 for p in self.changelog.parents(n):
1176 remove.append(p)
1178 remove.append(p)
1177
1179
1178 # find every node whose parents have been pruned
1180 # find every node whose parents have been pruned
1179 subset = []
1181 subset = []
1180 for n in remain:
1182 for n in remain:
1181 p1, p2 = self.changelog.parents(n)
1183 p1, p2 = self.changelog.parents(n)
1182 if p1 not in remain and p2 not in remain:
1184 if p1 not in remain and p2 not in remain:
1183 subset.append(n)
1185 subset.append(n)
1184
1186
1185 # this is the set of all roots we have to push
1187 # this is the set of all roots we have to push
1186 return subset
1188 return subset
1187
1189
1188 def pull(self, remote):
1190 def pull(self, remote):
1189 lock = self.lock()
1191 lock = self.lock()
1190
1192
1191 # if we have an empty repo, fetch everything
1193 # if we have an empty repo, fetch everything
1192 if self.changelog.tip() == nullid:
1194 if self.changelog.tip() == nullid:
1193 self.ui.status("requesting all changes\n")
1195 self.ui.status("requesting all changes\n")
1194 fetch = [nullid]
1196 fetch = [nullid]
1195 else:
1197 else:
1196 fetch = self.findincoming(remote)
1198 fetch = self.findincoming(remote)
1197
1199
1198 if not fetch:
1200 if not fetch:
1199 self.ui.status("no changes found\n")
1201 self.ui.status("no changes found\n")
1200 return 1
1202 return 1
1201
1203
1202 cg = remote.changegroup(fetch)
1204 cg = remote.changegroup(fetch)
1203 return self.addchangegroup(cg)
1205 return self.addchangegroup(cg)
1204
1206
1205 def push(self, remote):
1207 def push(self, remote):
1206 lock = remote.lock()
1208 lock = remote.lock()
1207 update = self.findoutgoing(remote)
1209 update = self.findoutgoing(remote)
1208 if not update:
1210 if not update:
1209 self.ui.status("no changes found\n")
1211 self.ui.status("no changes found\n")
1210 return 1
1212 return 1
1211
1213
1212 cg = self.changegroup(update)
1214 cg = self.changegroup(update)
1213 return remote.addchangegroup(cg)
1215 return remote.addchangegroup(cg)
1214
1216
1215 def changegroup(self, basenodes):
1217 def changegroup(self, basenodes):
1216 class genread:
1218 class genread:
1217 def __init__(self, generator):
1219 def __init__(self, generator):
1218 self.g = generator
1220 self.g = generator
1219 self.buf = ""
1221 self.buf = ""
1220 def read(self, l):
1222 def read(self, l):
1221 while l > len(self.buf):
1223 while l > len(self.buf):
1222 try:
1224 try:
1223 self.buf += self.g.next()
1225 self.buf += self.g.next()
1224 except StopIteration:
1226 except StopIteration:
1225 break
1227 break
1226 d, self.buf = self.buf[:l], self.buf[l:]
1228 d, self.buf = self.buf[:l], self.buf[l:]
1227 return d
1229 return d
1228
1230
1229 def gengroup():
1231 def gengroup():
1230 nodes = self.newer(basenodes)
1232 nodes = self.newer(basenodes)
1231
1233
1232 # construct the link map
1234 # construct the link map
1233 linkmap = {}
1235 linkmap = {}
1234 for n in nodes:
1236 for n in nodes:
1235 linkmap[self.changelog.rev(n)] = n
1237 linkmap[self.changelog.rev(n)] = n
1236
1238
1237 # construct a list of all changed files
1239 # construct a list of all changed files
1238 changed = {}
1240 changed = {}
1239 for n in nodes:
1241 for n in nodes:
1240 c = self.changelog.read(n)
1242 c = self.changelog.read(n)
1241 for f in c[3]:
1243 for f in c[3]:
1242 changed[f] = 1
1244 changed[f] = 1
1243 changed = changed.keys()
1245 changed = changed.keys()
1244 changed.sort()
1246 changed.sort()
1245
1247
1246 # the changegroup is changesets + manifests + all file revs
1248 # the changegroup is changesets + manifests + all file revs
1247 revs = [ self.changelog.rev(n) for n in nodes ]
1249 revs = [ self.changelog.rev(n) for n in nodes ]
1248
1250
1249 for y in self.changelog.group(linkmap): yield y
1251 for y in self.changelog.group(linkmap): yield y
1250 for y in self.manifest.group(linkmap): yield y
1252 for y in self.manifest.group(linkmap): yield y
1251 for f in changed:
1253 for f in changed:
1252 yield struct.pack(">l", len(f) + 4) + f
1254 yield struct.pack(">l", len(f) + 4) + f
1253 g = self.file(f).group(linkmap)
1255 g = self.file(f).group(linkmap)
1254 for y in g:
1256 for y in g:
1255 yield y
1257 yield y
1256
1258
1257 yield struct.pack(">l", 0)
1259 yield struct.pack(">l", 0)
1258
1260
1259 return genread(gengroup())
1261 return genread(gengroup())
1260
1262
1261 def addchangegroup(self, source):
1263 def addchangegroup(self, source):
1262
1264
1263 def getchunk():
1265 def getchunk():
1264 d = source.read(4)
1266 d = source.read(4)
1265 if not d: return ""
1267 if not d: return ""
1266 l = struct.unpack(">l", d)[0]
1268 l = struct.unpack(">l", d)[0]
1267 if l <= 4: return ""
1269 if l <= 4: return ""
1268 return source.read(l - 4)
1270 return source.read(l - 4)
1269
1271
1270 def getgroup():
1272 def getgroup():
1271 while 1:
1273 while 1:
1272 c = getchunk()
1274 c = getchunk()
1273 if not c: break
1275 if not c: break
1274 yield c
1276 yield c
1275
1277
1276 def csmap(x):
1278 def csmap(x):
1277 self.ui.debug("add changeset %s\n" % short(x))
1279 self.ui.debug("add changeset %s\n" % short(x))
1278 return self.changelog.count()
1280 return self.changelog.count()
1279
1281
1280 def revmap(x):
1282 def revmap(x):
1281 return self.changelog.rev(x)
1283 return self.changelog.rev(x)
1282
1284
1283 if not source: return
1285 if not source: return
1284 changesets = files = revisions = 0
1286 changesets = files = revisions = 0
1285
1287
1286 tr = self.transaction()
1288 tr = self.transaction()
1287
1289
1288 # pull off the changeset group
1290 # pull off the changeset group
1289 self.ui.status("adding changesets\n")
1291 self.ui.status("adding changesets\n")
1290 co = self.changelog.tip()
1292 co = self.changelog.tip()
1291 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1293 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1292 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1294 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1293
1295
1294 # pull off the manifest group
1296 # pull off the manifest group
1295 self.ui.status("adding manifests\n")
1297 self.ui.status("adding manifests\n")
1296 mm = self.manifest.tip()
1298 mm = self.manifest.tip()
1297 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1299 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1298
1300
1299 # process the files
1301 # process the files
1300 self.ui.status("adding file changes\n")
1302 self.ui.status("adding file changes\n")
1301 while 1:
1303 while 1:
1302 f = getchunk()
1304 f = getchunk()
1303 if not f: break
1305 if not f: break
1304 self.ui.debug("adding %s revisions\n" % f)
1306 self.ui.debug("adding %s revisions\n" % f)
1305 fl = self.file(f)
1307 fl = self.file(f)
1306 o = fl.count()
1308 o = fl.count()
1307 n = fl.addgroup(getgroup(), revmap, tr)
1309 n = fl.addgroup(getgroup(), revmap, tr)
1308 revisions += fl.count() - o
1310 revisions += fl.count() - o
1309 files += 1
1311 files += 1
1310
1312
1311 self.ui.status(("added %d changesets" +
1313 self.ui.status(("added %d changesets" +
1312 " with %d changes to %d files\n")
1314 " with %d changes to %d files\n")
1313 % (changesets, revisions, files))
1315 % (changesets, revisions, files))
1314
1316
1315 tr.close()
1317 tr.close()
1316
1318
1317 if not self.hook("changegroup"):
1319 if not self.hook("changegroup"):
1318 return 1
1320 return 1
1319
1321
1320 return
1322 return
1321
1323
1322 def update(self, node, allow=False, force=False, choose=None,
1324 def update(self, node, allow=False, force=False, choose=None,
1323 moddirstate=True):
1325 moddirstate=True):
1324 pl = self.dirstate.parents()
1326 pl = self.dirstate.parents()
1325 if not force and pl[1] != nullid:
1327 if not force and pl[1] != nullid:
1326 self.ui.warn("aborting: outstanding uncommitted merges\n")
1328 self.ui.warn("aborting: outstanding uncommitted merges\n")
1327 return 1
1329 return 1
1328
1330
1329 p1, p2 = pl[0], node
1331 p1, p2 = pl[0], node
1330 pa = self.changelog.ancestor(p1, p2)
1332 pa = self.changelog.ancestor(p1, p2)
1331 m1n = self.changelog.read(p1)[0]
1333 m1n = self.changelog.read(p1)[0]
1332 m2n = self.changelog.read(p2)[0]
1334 m2n = self.changelog.read(p2)[0]
1333 man = self.manifest.ancestor(m1n, m2n)
1335 man = self.manifest.ancestor(m1n, m2n)
1334 m1 = self.manifest.read(m1n)
1336 m1 = self.manifest.read(m1n)
1335 mf1 = self.manifest.readflags(m1n)
1337 mf1 = self.manifest.readflags(m1n)
1336 m2 = self.manifest.read(m2n)
1338 m2 = self.manifest.read(m2n)
1337 mf2 = self.manifest.readflags(m2n)
1339 mf2 = self.manifest.readflags(m2n)
1338 ma = self.manifest.read(man)
1340 ma = self.manifest.read(man)
1339 mfa = self.manifest.readflags(man)
1341 mfa = self.manifest.readflags(man)
1340
1342
1341 (c, a, d, u) = self.changes()
1343 (c, a, d, u) = self.changes()
1342
1344
1343 # is this a jump, or a merge? i.e. is there a linear path
1345 # is this a jump, or a merge? i.e. is there a linear path
1344 # from p1 to p2?
1346 # from p1 to p2?
1345 linear_path = (pa == p1 or pa == p2)
1347 linear_path = (pa == p1 or pa == p2)
1346
1348
1347 # resolve the manifest to determine which files
1349 # resolve the manifest to determine which files
1348 # we care about merging
1350 # we care about merging
1349 self.ui.note("resolving manifests\n")
1351 self.ui.note("resolving manifests\n")
1350 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1352 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1351 (force, allow, moddirstate, linear_path))
1353 (force, allow, moddirstate, linear_path))
1352 self.ui.debug(" ancestor %s local %s remote %s\n" %
1354 self.ui.debug(" ancestor %s local %s remote %s\n" %
1353 (short(man), short(m1n), short(m2n)))
1355 (short(man), short(m1n), short(m2n)))
1354
1356
1355 merge = {}
1357 merge = {}
1356 get = {}
1358 get = {}
1357 remove = []
1359 remove = []
1358 mark = {}
1360 mark = {}
1359
1361
1360 # construct a working dir manifest
1362 # construct a working dir manifest
1361 mw = m1.copy()
1363 mw = m1.copy()
1362 mfw = mf1.copy()
1364 mfw = mf1.copy()
1363 umap = dict.fromkeys(u)
1365 umap = dict.fromkeys(u)
1364
1366
1365 for f in a + c + u:
1367 for f in a + c + u:
1366 mw[f] = ""
1368 mw[f] = ""
1367 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1369 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1368
1370
1369 for f in d:
1371 for f in d:
1370 if f in mw: del mw[f]
1372 if f in mw: del mw[f]
1371
1373
1372 # If we're jumping between revisions (as opposed to merging),
1374 # If we're jumping between revisions (as opposed to merging),
1373 # and if neither the working directory nor the target rev has
1375 # and if neither the working directory nor the target rev has
1374 # the file, then we need to remove it from the dirstate, to
1376 # the file, then we need to remove it from the dirstate, to
1375 # prevent the dirstate from listing the file when it is no
1377 # prevent the dirstate from listing the file when it is no
1376 # longer in the manifest.
1378 # longer in the manifest.
1377 if moddirstate and linear_path and f not in m2:
1379 if moddirstate and linear_path and f not in m2:
1378 self.dirstate.forget((f,))
1380 self.dirstate.forget((f,))
1379
1381
1380 # Compare manifests
1382 # Compare manifests
1381 for f, n in mw.iteritems():
1383 for f, n in mw.iteritems():
1382 if choose and not choose(f): continue
1384 if choose and not choose(f): continue
1383 if f in m2:
1385 if f in m2:
1384 s = 0
1386 s = 0
1385
1387
1386 # is the wfile new since m1, and match m2?
1388 # is the wfile new since m1, and match m2?
1387 if f not in m1:
1389 if f not in m1:
1388 t1 = self.wfile(f).read()
1390 t1 = self.wfile(f).read()
1389 t2 = self.file(f).revision(m2[f])
1391 t2 = self.file(f).revision(m2[f])
1390 if cmp(t1, t2) == 0:
1392 if cmp(t1, t2) == 0:
1391 mark[f] = 1
1393 mark[f] = 1
1392 n = m2[f]
1394 n = m2[f]
1393 del t1, t2
1395 del t1, t2
1394
1396
1395 # are files different?
1397 # are files different?
1396 if n != m2[f]:
1398 if n != m2[f]:
1397 a = ma.get(f, nullid)
1399 a = ma.get(f, nullid)
1398 # are both different from the ancestor?
1400 # are both different from the ancestor?
1399 if n != a and m2[f] != a:
1401 if n != a and m2[f] != a:
1400 self.ui.debug(" %s versions differ, resolve\n" % f)
1402 self.ui.debug(" %s versions differ, resolve\n" % f)
1401 # merge executable bits
1403 # merge executable bits
1402 # "if we changed or they changed, change in merge"
1404 # "if we changed or they changed, change in merge"
1403 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1405 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1404 mode = ((a^b) | (a^c)) ^ a
1406 mode = ((a^b) | (a^c)) ^ a
1405 merge[f] = (m1.get(f, nullid), m2[f], mode)
1407 merge[f] = (m1.get(f, nullid), m2[f], mode)
1406 s = 1
1408 s = 1
1407 # are we clobbering?
1409 # are we clobbering?
1408 # is remote's version newer?
1410 # is remote's version newer?
1409 # or are we going back in time?
1411 # or are we going back in time?
1410 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1412 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1411 self.ui.debug(" remote %s is newer, get\n" % f)
1413 self.ui.debug(" remote %s is newer, get\n" % f)
1412 get[f] = m2[f]
1414 get[f] = m2[f]
1413 s = 1
1415 s = 1
1414 else:
1416 else:
1415 mark[f] = 1
1417 mark[f] = 1
1416 elif f in umap:
1418 elif f in umap:
1417 # this unknown file is the same as the checkout
1419 # this unknown file is the same as the checkout
1418 get[f] = m2[f]
1420 get[f] = m2[f]
1419
1421
1420 if not s and mfw[f] != mf2[f]:
1422 if not s and mfw[f] != mf2[f]:
1421 if force:
1423 if force:
1422 self.ui.debug(" updating permissions for %s\n" % f)
1424 self.ui.debug(" updating permissions for %s\n" % f)
1423 util.set_exec(self.wjoin(f), mf2[f])
1425 util.set_exec(self.wjoin(f), mf2[f])
1424 else:
1426 else:
1425 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1427 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1426 mode = ((a^b) | (a^c)) ^ a
1428 mode = ((a^b) | (a^c)) ^ a
1427 if mode != b:
1429 if mode != b:
1428 self.ui.debug(" updating permissions for %s\n" % f)
1430 self.ui.debug(" updating permissions for %s\n" % f)
1429 util.set_exec(self.wjoin(f), mode)
1431 util.set_exec(self.wjoin(f), mode)
1430 mark[f] = 1
1432 mark[f] = 1
1431 del m2[f]
1433 del m2[f]
1432 elif f in ma:
1434 elif f in ma:
1433 if n != ma[f]:
1435 if n != ma[f]:
1434 r = "d"
1436 r = "d"
1435 if not force and (linear_path or allow):
1437 if not force and (linear_path or allow):
1436 r = self.ui.prompt(
1438 r = self.ui.prompt(
1437 (" local changed %s which remote deleted\n" % f) +
1439 (" local changed %s which remote deleted\n" % f) +
1438 "(k)eep or (d)elete?", "[kd]", "k")
1440 "(k)eep or (d)elete?", "[kd]", "k")
1439 if r == "d":
1441 if r == "d":
1440 remove.append(f)
1442 remove.append(f)
1441 else:
1443 else:
1442 self.ui.debug("other deleted %s\n" % f)
1444 self.ui.debug("other deleted %s\n" % f)
1443 remove.append(f) # other deleted it
1445 remove.append(f) # other deleted it
1444 else:
1446 else:
1445 if n == m1.get(f, nullid): # same as parent
1447 if n == m1.get(f, nullid): # same as parent
1446 if p2 == pa: # going backwards?
1448 if p2 == pa: # going backwards?
1447 self.ui.debug("remote deleted %s\n" % f)
1449 self.ui.debug("remote deleted %s\n" % f)
1448 remove.append(f)
1450 remove.append(f)
1449 else:
1451 else:
1450 self.ui.debug("local created %s, keeping\n" % f)
1452 self.ui.debug("local created %s, keeping\n" % f)
1451 else:
1453 else:
1452 self.ui.debug("working dir created %s, keeping\n" % f)
1454 self.ui.debug("working dir created %s, keeping\n" % f)
1453
1455
1454 for f, n in m2.iteritems():
1456 for f, n in m2.iteritems():
1455 if choose and not choose(f): continue
1457 if choose and not choose(f): continue
1456 if f[0] == "/": continue
1458 if f[0] == "/": continue
1457 if f in ma and n != ma[f]:
1459 if f in ma and n != ma[f]:
1458 r = "k"
1460 r = "k"
1459 if not force and (linear_path or allow):
1461 if not force and (linear_path or allow):
1460 r = self.ui.prompt(
1462 r = self.ui.prompt(
1461 ("remote changed %s which local deleted\n" % f) +
1463 ("remote changed %s which local deleted\n" % f) +
1462 "(k)eep or (d)elete?", "[kd]", "k")
1464 "(k)eep or (d)elete?", "[kd]", "k")
1463 if r == "k": get[f] = n
1465 if r == "k": get[f] = n
1464 elif f not in ma:
1466 elif f not in ma:
1465 self.ui.debug("remote created %s\n" % f)
1467 self.ui.debug("remote created %s\n" % f)
1466 get[f] = n
1468 get[f] = n
1467 else:
1469 else:
1468 if force or p2 == pa: # going backwards?
1470 if force or p2 == pa: # going backwards?
1469 self.ui.debug("local deleted %s, recreating\n" % f)
1471 self.ui.debug("local deleted %s, recreating\n" % f)
1470 get[f] = n
1472 get[f] = n
1471 else:
1473 else:
1472 self.ui.debug("local deleted %s\n" % f)
1474 self.ui.debug("local deleted %s\n" % f)
1473
1475
1474 del mw, m1, m2, ma
1476 del mw, m1, m2, ma
1475
1477
1476 if force:
1478 if force:
1477 for f in merge:
1479 for f in merge:
1478 get[f] = merge[f][1]
1480 get[f] = merge[f][1]
1479 merge = {}
1481 merge = {}
1480
1482
1481 if linear_path or force:
1483 if linear_path or force:
1482 # we don't need to do any magic, just jump to the new rev
1484 # we don't need to do any magic, just jump to the new rev
1483 mode = 'n'
1485 mode = 'n'
1484 p1, p2 = p2, nullid
1486 p1, p2 = p2, nullid
1485 else:
1487 else:
1486 if not allow:
1488 if not allow:
1487 self.ui.status("this update spans a branch" +
1489 self.ui.status("this update spans a branch" +
1488 " affecting the following files:\n")
1490 " affecting the following files:\n")
1489 fl = merge.keys() + get.keys()
1491 fl = merge.keys() + get.keys()
1490 fl.sort()
1492 fl.sort()
1491 for f in fl:
1493 for f in fl:
1492 cf = ""
1494 cf = ""
1493 if f in merge: cf = " (resolve)"
1495 if f in merge: cf = " (resolve)"
1494 self.ui.status(" %s%s\n" % (f, cf))
1496 self.ui.status(" %s%s\n" % (f, cf))
1495 self.ui.warn("aborting update spanning branches!\n")
1497 self.ui.warn("aborting update spanning branches!\n")
1496 self.ui.status("(use update -m to merge across branches" +
1498 self.ui.status("(use update -m to merge across branches" +
1497 " or -C to lose changes)\n")
1499 " or -C to lose changes)\n")
1498 return 1
1500 return 1
1499 # we have to remember what files we needed to get/change
1501 # we have to remember what files we needed to get/change
1500 # because any file that's different from either one of its
1502 # because any file that's different from either one of its
1501 # parents must be in the changeset
1503 # parents must be in the changeset
1502 mode = 'm'
1504 mode = 'm'
1503 if moddirstate:
1505 if moddirstate:
1504 self.dirstate.update(mark.keys(), "m")
1506 self.dirstate.update(mark.keys(), "m")
1505
1507
1506 if moddirstate:
1508 if moddirstate:
1507 self.dirstate.setparents(p1, p2)
1509 self.dirstate.setparents(p1, p2)
1508
1510
1509 # get the files we don't need to change
1511 # get the files we don't need to change
1510 files = get.keys()
1512 files = get.keys()
1511 files.sort()
1513 files.sort()
1512 for f in files:
1514 for f in files:
1513 if f[0] == "/": continue
1515 if f[0] == "/": continue
1514 self.ui.note("getting %s\n" % f)
1516 self.ui.note("getting %s\n" % f)
1515 t = self.file(f).read(get[f])
1517 t = self.file(f).read(get[f])
1516 try:
1518 try:
1517 self.wfile(f, "w").write(t)
1519 self.wfile(f, "w").write(t)
1518 except IOError:
1520 except IOError:
1519 os.makedirs(os.path.dirname(self.wjoin(f)))
1521 os.makedirs(os.path.dirname(self.wjoin(f)))
1520 self.wfile(f, "w").write(t)
1522 self.wfile(f, "w").write(t)
1521 util.set_exec(self.wjoin(f), mf2[f])
1523 util.set_exec(self.wjoin(f), mf2[f])
1522 if moddirstate:
1524 if moddirstate:
1523 self.dirstate.update([f], mode)
1525 self.dirstate.update([f], mode)
1524
1526
1525 # merge the tricky bits
1527 # merge the tricky bits
1526 files = merge.keys()
1528 files = merge.keys()
1527 files.sort()
1529 files.sort()
1528 for f in files:
1530 for f in files:
1529 self.ui.status("merging %s\n" % f)
1531 self.ui.status("merging %s\n" % f)
1530 m, o, flag = merge[f]
1532 m, o, flag = merge[f]
1531 self.merge3(f, m, o)
1533 self.merge3(f, m, o)
1532 util.set_exec(self.wjoin(f), flag)
1534 util.set_exec(self.wjoin(f), flag)
1533 if moddirstate and mode == 'm':
1535 if moddirstate and mode == 'm':
1534 # only update dirstate on branch merge, otherwise we
1536 # only update dirstate on branch merge, otherwise we
1535 # could mark files with changes as unchanged
1537 # could mark files with changes as unchanged
1536 self.dirstate.update([f], mode)
1538 self.dirstate.update([f], mode)
1537
1539
1538 remove.sort()
1540 remove.sort()
1539 for f in remove:
1541 for f in remove:
1540 self.ui.note("removing %s\n" % f)
1542 self.ui.note("removing %s\n" % f)
1541 try:
1543 try:
1542 os.unlink(f)
1544 os.unlink(f)
1543 except OSError, inst:
1545 except OSError, inst:
1544 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1546 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1545 # try removing directories that might now be empty
1547 # try removing directories that might now be empty
1546 try: os.removedirs(os.path.dirname(f))
1548 try: os.removedirs(os.path.dirname(f))
1547 except: pass
1549 except: pass
1548 if moddirstate:
1550 if moddirstate:
1549 if mode == 'n':
1551 if mode == 'n':
1550 self.dirstate.forget(remove)
1552 self.dirstate.forget(remove)
1551 else:
1553 else:
1552 self.dirstate.update(remove, 'r')
1554 self.dirstate.update(remove, 'r')
1553
1555
1554 def merge3(self, fn, my, other):
1556 def merge3(self, fn, my, other):
1555 """perform a 3-way merge in the working directory"""
1557 """perform a 3-way merge in the working directory"""
1556
1558
1557 def temp(prefix, node):
1559 def temp(prefix, node):
1558 pre = "%s~%s." % (os.path.basename(fn), prefix)
1560 pre = "%s~%s." % (os.path.basename(fn), prefix)
1559 (fd, name) = tempfile.mkstemp("", pre)
1561 (fd, name) = tempfile.mkstemp("", pre)
1560 f = os.fdopen(fd, "wb")
1562 f = os.fdopen(fd, "wb")
1561 f.write(fl.revision(node))
1563 f.write(fl.revision(node))
1562 f.close()
1564 f.close()
1563 return name
1565 return name
1564
1566
1565 fl = self.file(fn)
1567 fl = self.file(fn)
1566 base = fl.ancestor(my, other)
1568 base = fl.ancestor(my, other)
1567 a = self.wjoin(fn)
1569 a = self.wjoin(fn)
1568 b = temp("base", base)
1570 b = temp("base", base)
1569 c = temp("other", other)
1571 c = temp("other", other)
1570
1572
1571 self.ui.note("resolving %s\n" % fn)
1573 self.ui.note("resolving %s\n" % fn)
1572 self.ui.debug("file %s: other %s ancestor %s\n" %
1574 self.ui.debug("file %s: other %s ancestor %s\n" %
1573 (fn, short(other), short(base)))
1575 (fn, short(other), short(base)))
1574
1576
1575 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1577 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1576 or "hgmerge")
1578 or "hgmerge")
1577 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1579 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1578 if r:
1580 if r:
1579 self.ui.warn("merging %s failed!\n" % fn)
1581 self.ui.warn("merging %s failed!\n" % fn)
1580
1582
1581 os.unlink(b)
1583 os.unlink(b)
1582 os.unlink(c)
1584 os.unlink(c)
1583
1585
1584 def verify(self):
1586 def verify(self):
1585 filelinkrevs = {}
1587 filelinkrevs = {}
1586 filenodes = {}
1588 filenodes = {}
1587 changesets = revisions = files = 0
1589 changesets = revisions = files = 0
1588 errors = 0
1590 errors = 0
1589
1591
1590 seen = {}
1592 seen = {}
1591 self.ui.status("checking changesets\n")
1593 self.ui.status("checking changesets\n")
1592 for i in range(self.changelog.count()):
1594 for i in range(self.changelog.count()):
1593 changesets += 1
1595 changesets += 1
1594 n = self.changelog.node(i)
1596 n = self.changelog.node(i)
1595 if n in seen:
1597 if n in seen:
1596 self.ui.warn("duplicate changeset at revision %d\n" % i)
1598 self.ui.warn("duplicate changeset at revision %d\n" % i)
1597 errors += 1
1599 errors += 1
1598 seen[n] = 1
1600 seen[n] = 1
1599
1601
1600 for p in self.changelog.parents(n):
1602 for p in self.changelog.parents(n):
1601 if p not in self.changelog.nodemap:
1603 if p not in self.changelog.nodemap:
1602 self.ui.warn("changeset %s has unknown parent %s\n" %
1604 self.ui.warn("changeset %s has unknown parent %s\n" %
1603 (short(n), short(p)))
1605 (short(n), short(p)))
1604 errors += 1
1606 errors += 1
1605 try:
1607 try:
1606 changes = self.changelog.read(n)
1608 changes = self.changelog.read(n)
1607 except Exception, inst:
1609 except Exception, inst:
1608 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1610 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1609 errors += 1
1611 errors += 1
1610
1612
1611 for f in changes[3]:
1613 for f in changes[3]:
1612 filelinkrevs.setdefault(f, []).append(i)
1614 filelinkrevs.setdefault(f, []).append(i)
1613
1615
1614 seen = {}
1616 seen = {}
1615 self.ui.status("checking manifests\n")
1617 self.ui.status("checking manifests\n")
1616 for i in range(self.manifest.count()):
1618 for i in range(self.manifest.count()):
1617 n = self.manifest.node(i)
1619 n = self.manifest.node(i)
1618 if n in seen:
1620 if n in seen:
1619 self.ui.warn("duplicate manifest at revision %d\n" % i)
1621 self.ui.warn("duplicate manifest at revision %d\n" % i)
1620 errors += 1
1622 errors += 1
1621 seen[n] = 1
1623 seen[n] = 1
1622
1624
1623 for p in self.manifest.parents(n):
1625 for p in self.manifest.parents(n):
1624 if p not in self.manifest.nodemap:
1626 if p not in self.manifest.nodemap:
1625 self.ui.warn("manifest %s has unknown parent %s\n" %
1627 self.ui.warn("manifest %s has unknown parent %s\n" %
1626 (short(n), short(p)))
1628 (short(n), short(p)))
1627 errors += 1
1629 errors += 1
1628
1630
1629 try:
1631 try:
1630 delta = mdiff.patchtext(self.manifest.delta(n))
1632 delta = mdiff.patchtext(self.manifest.delta(n))
1631 except KeyboardInterrupt:
1633 except KeyboardInterrupt:
1632 self.ui.warn("aborted")
1634 self.ui.warn("aborted")
1633 sys.exit(0)
1635 sys.exit(0)
1634 except Exception, inst:
1636 except Exception, inst:
1635 self.ui.warn("unpacking manifest %s: %s\n"
1637 self.ui.warn("unpacking manifest %s: %s\n"
1636 % (short(n), inst))
1638 % (short(n), inst))
1637 errors += 1
1639 errors += 1
1638
1640
1639 ff = [ l.split('\0') for l in delta.splitlines() ]
1641 ff = [ l.split('\0') for l in delta.splitlines() ]
1640 for f, fn in ff:
1642 for f, fn in ff:
1641 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1643 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1642
1644
1643 self.ui.status("crosschecking files in changesets and manifests\n")
1645 self.ui.status("crosschecking files in changesets and manifests\n")
1644 for f in filenodes:
1646 for f in filenodes:
1645 if f not in filelinkrevs:
1647 if f not in filelinkrevs:
1646 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1648 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1647 errors += 1
1649 errors += 1
1648
1650
1649 for f in filelinkrevs:
1651 for f in filelinkrevs:
1650 if f not in filenodes:
1652 if f not in filenodes:
1651 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1653 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1652 errors += 1
1654 errors += 1
1653
1655
1654 self.ui.status("checking files\n")
1656 self.ui.status("checking files\n")
1655 ff = filenodes.keys()
1657 ff = filenodes.keys()
1656 ff.sort()
1658 ff.sort()
1657 for f in ff:
1659 for f in ff:
1658 if f == "/dev/null": continue
1660 if f == "/dev/null": continue
1659 files += 1
1661 files += 1
1660 fl = self.file(f)
1662 fl = self.file(f)
1661 nodes = { nullid: 1 }
1663 nodes = { nullid: 1 }
1662 seen = {}
1664 seen = {}
1663 for i in range(fl.count()):
1665 for i in range(fl.count()):
1664 revisions += 1
1666 revisions += 1
1665 n = fl.node(i)
1667 n = fl.node(i)
1666
1668
1667 if n in seen:
1669 if n in seen:
1668 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1670 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1669 errors += 1
1671 errors += 1
1670
1672
1671 if n not in filenodes[f]:
1673 if n not in filenodes[f]:
1672 self.ui.warn("%s: %d:%s not in manifests\n"
1674 self.ui.warn("%s: %d:%s not in manifests\n"
1673 % (f, i, short(n)))
1675 % (f, i, short(n)))
1674 errors += 1
1676 errors += 1
1675 else:
1677 else:
1676 del filenodes[f][n]
1678 del filenodes[f][n]
1677
1679
1678 flr = fl.linkrev(n)
1680 flr = fl.linkrev(n)
1679 if flr not in filelinkrevs[f]:
1681 if flr not in filelinkrevs[f]:
1680 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1682 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1681 % (f, short(n), fl.linkrev(n)))
1683 % (f, short(n), fl.linkrev(n)))
1682 errors += 1
1684 errors += 1
1683 else:
1685 else:
1684 filelinkrevs[f].remove(flr)
1686 filelinkrevs[f].remove(flr)
1685
1687
1686 # verify contents
1688 # verify contents
1687 try:
1689 try:
1688 t = fl.read(n)
1690 t = fl.read(n)
1689 except Exception, inst:
1691 except Exception, inst:
1690 self.ui.warn("unpacking file %s %s: %s\n"
1692 self.ui.warn("unpacking file %s %s: %s\n"
1691 % (f, short(n), inst))
1693 % (f, short(n), inst))
1692 errors += 1
1694 errors += 1
1693
1695
1694 # verify parents
1696 # verify parents
1695 (p1, p2) = fl.parents(n)
1697 (p1, p2) = fl.parents(n)
1696 if p1 not in nodes:
1698 if p1 not in nodes:
1697 self.ui.warn("file %s:%s unknown parent 1 %s" %
1699 self.ui.warn("file %s:%s unknown parent 1 %s" %
1698 (f, short(n), short(p1)))
1700 (f, short(n), short(p1)))
1699 errors += 1
1701 errors += 1
1700 if p2 not in nodes:
1702 if p2 not in nodes:
1701 self.ui.warn("file %s:%s unknown parent 2 %s" %
1703 self.ui.warn("file %s:%s unknown parent 2 %s" %
1702 (f, short(n), short(p1)))
1704 (f, short(n), short(p1)))
1703 errors += 1
1705 errors += 1
1704 nodes[n] = 1
1706 nodes[n] = 1
1705
1707
1706 # cross-check
1708 # cross-check
1707 for node in filenodes[f]:
1709 for node in filenodes[f]:
1708 self.ui.warn("node %s in manifests not in %s\n"
1710 self.ui.warn("node %s in manifests not in %s\n"
1709 % (hex(node), f))
1711 % (hex(node), f))
1710 errors += 1
1712 errors += 1
1711
1713
1712 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1714 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1713 (files, changesets, revisions))
1715 (files, changesets, revisions))
1714
1716
1715 if errors:
1717 if errors:
1716 self.ui.warn("%d integrity errors encountered!\n" % errors)
1718 self.ui.warn("%d integrity errors encountered!\n" % errors)
1717 return 1
1719 return 1
1718
1720
1719 class httprepository:
1721 class httprepository:
1720 def __init__(self, ui, path):
1722 def __init__(self, ui, path):
1721 # fix missing / after hostname
1723 # fix missing / after hostname
1722 s = urlparse.urlsplit(path)
1724 s = urlparse.urlsplit(path)
1723 partial = s[2]
1725 partial = s[2]
1724 if not partial: partial = "/"
1726 if not partial: partial = "/"
1725 self.url = urlparse.urlunsplit((s[0], s[1], partial, '', ''))
1727 self.url = urlparse.urlunsplit((s[0], s[1], partial, '', ''))
1726 self.ui = ui
1728 self.ui = ui
1727 no_list = [ "localhost", "127.0.0.1" ]
1729 no_list = [ "localhost", "127.0.0.1" ]
1728 host = ui.config("http_proxy", "host")
1730 host = ui.config("http_proxy", "host")
1729 if host is None:
1731 if host is None:
1730 host = os.environ.get("http_proxy")
1732 host = os.environ.get("http_proxy")
1731 if host and host.startswith('http://'):
1733 if host and host.startswith('http://'):
1732 host = host[7:]
1734 host = host[7:]
1733 user = ui.config("http_proxy", "user")
1735 user = ui.config("http_proxy", "user")
1734 passwd = ui.config("http_proxy", "passwd")
1736 passwd = ui.config("http_proxy", "passwd")
1735 no = ui.config("http_proxy", "no")
1737 no = ui.config("http_proxy", "no")
1736 if no is None:
1738 if no is None:
1737 no = os.environ.get("no_proxy")
1739 no = os.environ.get("no_proxy")
1738 if no:
1740 if no:
1739 no_list = no_list + no.split(",")
1741 no_list = no_list + no.split(",")
1740
1742
1741 no_proxy = 0
1743 no_proxy = 0
1742 for h in no_list:
1744 for h in no_list:
1743 if (path.startswith("http://" + h + "/") or
1745 if (path.startswith("http://" + h + "/") or
1744 path.startswith("http://" + h + ":") or
1746 path.startswith("http://" + h + ":") or
1745 path == "http://" + h):
1747 path == "http://" + h):
1746 no_proxy = 1
1748 no_proxy = 1
1747
1749
1748 # Note: urllib2 takes proxy values from the environment and those will
1750 # Note: urllib2 takes proxy values from the environment and those will
1749 # take precedence
1751 # take precedence
1750 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1752 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1751 if os.environ.has_key(env):
1753 if os.environ.has_key(env):
1752 del os.environ[env]
1754 del os.environ[env]
1753
1755
1754 proxy_handler = urllib2.BaseHandler()
1756 proxy_handler = urllib2.BaseHandler()
1755 if host and not no_proxy:
1757 if host and not no_proxy:
1756 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1758 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1757
1759
1758 authinfo = None
1760 authinfo = None
1759 if user and passwd:
1761 if user and passwd:
1760 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1762 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1761 passmgr.add_password(None, host, user, passwd)
1763 passmgr.add_password(None, host, user, passwd)
1762 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1764 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1763
1765
1764 opener = urllib2.build_opener(proxy_handler, authinfo)
1766 opener = urllib2.build_opener(proxy_handler, authinfo)
1765 urllib2.install_opener(opener)
1767 urllib2.install_opener(opener)
1766
1768
1767 def dev(self):
1769 def dev(self):
1768 return -1
1770 return -1
1769
1771
1770 def do_cmd(self, cmd, **args):
1772 def do_cmd(self, cmd, **args):
1771 self.ui.debug("sending %s command\n" % cmd)
1773 self.ui.debug("sending %s command\n" % cmd)
1772 q = {"cmd": cmd}
1774 q = {"cmd": cmd}
1773 q.update(args)
1775 q.update(args)
1774 qs = urllib.urlencode(q)
1776 qs = urllib.urlencode(q)
1775 cu = "%s?%s" % (self.url, qs)
1777 cu = "%s?%s" % (self.url, qs)
1776 resp = urllib2.urlopen(cu)
1778 resp = urllib2.urlopen(cu)
1777 proto = resp.headers['content-type']
1779 proto = resp.headers['content-type']
1778
1780
1779 # accept old "text/plain" and "application/hg-changegroup" for now
1781 # accept old "text/plain" and "application/hg-changegroup" for now
1780 if not proto.startswith('application/mercurial') and \
1782 if not proto.startswith('application/mercurial') and \
1781 not proto.startswith('text/plain') and \
1783 not proto.startswith('text/plain') and \
1782 not proto.startswith('application/hg-changegroup'):
1784 not proto.startswith('application/hg-changegroup'):
1783 raise RepoError("'%s' does not appear to be an hg repository"
1785 raise RepoError("'%s' does not appear to be an hg repository"
1784 % self.url)
1786 % self.url)
1785
1787
1786 if proto.startswith('application/mercurial'):
1788 if proto.startswith('application/mercurial'):
1787 version = proto[22:]
1789 version = proto[22:]
1788 if float(version) > 0.1:
1790 if float(version) > 0.1:
1789 raise RepoError("'%s' uses newer protocol %s" %
1791 raise RepoError("'%s' uses newer protocol %s" %
1790 (self.url, version))
1792 (self.url, version))
1791
1793
1792 return resp
1794 return resp
1793
1795
1794 def heads(self):
1796 def heads(self):
1795 d = self.do_cmd("heads").read()
1797 d = self.do_cmd("heads").read()
1796 try:
1798 try:
1797 return map(bin, d[:-1].split(" "))
1799 return map(bin, d[:-1].split(" "))
1798 except:
1800 except:
1799 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1801 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1800 raise
1802 raise
1801
1803
1802 def branches(self, nodes):
1804 def branches(self, nodes):
1803 n = " ".join(map(hex, nodes))
1805 n = " ".join(map(hex, nodes))
1804 d = self.do_cmd("branches", nodes=n).read()
1806 d = self.do_cmd("branches", nodes=n).read()
1805 try:
1807 try:
1806 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1808 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1807 return br
1809 return br
1808 except:
1810 except:
1809 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1811 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1810 raise
1812 raise
1811
1813
1812 def between(self, pairs):
1814 def between(self, pairs):
1813 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1815 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1814 d = self.do_cmd("between", pairs=n).read()
1816 d = self.do_cmd("between", pairs=n).read()
1815 try:
1817 try:
1816 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1818 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1817 return p
1819 return p
1818 except:
1820 except:
1819 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1821 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1820 raise
1822 raise
1821
1823
1822 def changegroup(self, nodes):
1824 def changegroup(self, nodes):
1823 n = " ".join(map(hex, nodes))
1825 n = " ".join(map(hex, nodes))
1824 f = self.do_cmd("changegroup", roots=n)
1826 f = self.do_cmd("changegroup", roots=n)
1825 bytes = 0
1827 bytes = 0
1826
1828
1827 class zread:
1829 class zread:
1828 def __init__(self, f):
1830 def __init__(self, f):
1829 self.zd = zlib.decompressobj()
1831 self.zd = zlib.decompressobj()
1830 self.f = f
1832 self.f = f
1831 self.buf = ""
1833 self.buf = ""
1832 def read(self, l):
1834 def read(self, l):
1833 while l > len(self.buf):
1835 while l > len(self.buf):
1834 r = self.f.read(4096)
1836 r = self.f.read(4096)
1835 if r:
1837 if r:
1836 self.buf += self.zd.decompress(r)
1838 self.buf += self.zd.decompress(r)
1837 else:
1839 else:
1838 self.buf += self.zd.flush()
1840 self.buf += self.zd.flush()
1839 break
1841 break
1840 d, self.buf = self.buf[:l], self.buf[l:]
1842 d, self.buf = self.buf[:l], self.buf[l:]
1841 return d
1843 return d
1842
1844
1843 return zread(f)
1845 return zread(f)
1844
1846
1845 class remotelock:
1847 class remotelock:
1846 def __init__(self, repo):
1848 def __init__(self, repo):
1847 self.repo = repo
1849 self.repo = repo
1848 def release(self):
1850 def release(self):
1849 self.repo.unlock()
1851 self.repo.unlock()
1850 self.repo = None
1852 self.repo = None
1851 def __del__(self):
1853 def __del__(self):
1852 if self.repo:
1854 if self.repo:
1853 self.release()
1855 self.release()
1854
1856
1855 class sshrepository:
1857 class sshrepository:
1856 def __init__(self, ui, path):
1858 def __init__(self, ui, path):
1857 self.url = path
1859 self.url = path
1858 self.ui = ui
1860 self.ui = ui
1859
1861
1860 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
1862 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
1861 if not m:
1863 if not m:
1862 raise RepoError("couldn't parse destination %s\n" % path)
1864 raise RepoError("couldn't parse destination %s\n" % path)
1863
1865
1864 self.user = m.group(2)
1866 self.user = m.group(2)
1865 self.host = m.group(3)
1867 self.host = m.group(3)
1866 self.port = m.group(5)
1868 self.port = m.group(5)
1867 self.path = m.group(7)
1869 self.path = m.group(7)
1868
1870
1869 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
1871 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
1870 args = self.port and ("%s -p %s") % (args, self.port) or args
1872 args = self.port and ("%s -p %s") % (args, self.port) or args
1871 path = self.path or ""
1873 path = self.path or ""
1872
1874
1873 cmd = "ssh %s 'hg -R %s serve --stdio'"
1875 cmd = "ssh %s 'hg -R %s serve --stdio'"
1874 cmd = cmd % (args, path)
1876 cmd = cmd % (args, path)
1875
1877
1876 self.pipeo, self.pipei, self.pipee = os.popen3(cmd)
1878 self.pipeo, self.pipei, self.pipee = os.popen3(cmd)
1877
1879
1878 def readerr(self):
1880 def readerr(self):
1879 while 1:
1881 while 1:
1880 r,w,x = select.select([self.pipee], [], [], 0)
1882 r,w,x = select.select([self.pipee], [], [], 0)
1881 if not r: break
1883 if not r: break
1882 l = self.pipee.readline()
1884 l = self.pipee.readline()
1883 if not l: break
1885 if not l: break
1884 self.ui.status("remote: ", l)
1886 self.ui.status("remote: ", l)
1885
1887
1886 def __del__(self):
1888 def __del__(self):
1887 self.pipeo.close()
1889 self.pipeo.close()
1888 self.pipei.close()
1890 self.pipei.close()
1889 for l in self.pipee:
1891 for l in self.pipee:
1890 self.ui.status("remote: ", l)
1892 self.ui.status("remote: ", l)
1891 self.pipee.close()
1893 self.pipee.close()
1892
1894
1893 def dev(self):
1895 def dev(self):
1894 return -1
1896 return -1
1895
1897
1896 def do_cmd(self, cmd, **args):
1898 def do_cmd(self, cmd, **args):
1897 self.ui.debug("sending %s command\n" % cmd)
1899 self.ui.debug("sending %s command\n" % cmd)
1898 self.pipeo.write("%s\n" % cmd)
1900 self.pipeo.write("%s\n" % cmd)
1899 for k, v in args.items():
1901 for k, v in args.items():
1900 self.pipeo.write("%s %d\n" % (k, len(v)))
1902 self.pipeo.write("%s %d\n" % (k, len(v)))
1901 self.pipeo.write(v)
1903 self.pipeo.write(v)
1902 self.pipeo.flush()
1904 self.pipeo.flush()
1903
1905
1904 return self.pipei
1906 return self.pipei
1905
1907
1906 def call(self, cmd, **args):
1908 def call(self, cmd, **args):
1907 r = self.do_cmd(cmd, **args)
1909 r = self.do_cmd(cmd, **args)
1908 l = r.readline()
1910 l = r.readline()
1909 self.readerr()
1911 self.readerr()
1910 try:
1912 try:
1911 l = int(l)
1913 l = int(l)
1912 except:
1914 except:
1913 raise RepoError("unexpected response '%s'" % l)
1915 raise RepoError("unexpected response '%s'" % l)
1914 return r.read(l)
1916 return r.read(l)
1915
1917
1916 def lock(self):
1918 def lock(self):
1917 self.call("lock")
1919 self.call("lock")
1918 return remotelock(self)
1920 return remotelock(self)
1919
1921
1920 def unlock(self):
1922 def unlock(self):
1921 self.call("unlock")
1923 self.call("unlock")
1922
1924
1923 def heads(self):
1925 def heads(self):
1924 d = self.call("heads")
1926 d = self.call("heads")
1925 try:
1927 try:
1926 return map(bin, d[:-1].split(" "))
1928 return map(bin, d[:-1].split(" "))
1927 except:
1929 except:
1928 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1930 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1929
1931
1930 def branches(self, nodes):
1932 def branches(self, nodes):
1931 n = " ".join(map(hex, nodes))
1933 n = " ".join(map(hex, nodes))
1932 d = self.call("branches", nodes=n)
1934 d = self.call("branches", nodes=n)
1933 try:
1935 try:
1934 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1936 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1935 return br
1937 return br
1936 except:
1938 except:
1937 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1939 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1938
1940
1939 def between(self, pairs):
1941 def between(self, pairs):
1940 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1942 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1941 d = self.call("between", pairs=n)
1943 d = self.call("between", pairs=n)
1942 try:
1944 try:
1943 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1945 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1944 return p
1946 return p
1945 except:
1947 except:
1946 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1948 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1947
1949
1948 def changegroup(self, nodes):
1950 def changegroup(self, nodes):
1949 n = " ".join(map(hex, nodes))
1951 n = " ".join(map(hex, nodes))
1950 f = self.do_cmd("changegroup", roots=n)
1952 f = self.do_cmd("changegroup", roots=n)
1951 return self.pipei
1953 return self.pipei
1952
1954
1953 def addchangegroup(self, cg):
1955 def addchangegroup(self, cg):
1954 d = self.call("addchangegroup")
1956 d = self.call("addchangegroup")
1955 if d:
1957 if d:
1956 raise RepoError("push refused: %s", d)
1958 raise RepoError("push refused: %s", d)
1957
1959
1958 while 1:
1960 while 1:
1959 d = cg.read(4096)
1961 d = cg.read(4096)
1960 if not d: break
1962 if not d: break
1961 self.pipeo.write(d)
1963 self.pipeo.write(d)
1962 self.readerr()
1964 self.readerr()
1963
1965
1964 self.pipeo.flush()
1966 self.pipeo.flush()
1965
1967
1966 self.readerr()
1968 self.readerr()
1967 l = int(self.pipei.readline())
1969 l = int(self.pipei.readline())
1968 return self.pipei.read(l) != ""
1970 return self.pipei.read(l) != ""
1969
1971
1970 def repository(ui, path=None, create=0):
1972 def repository(ui, path=None, create=0):
1971 if path:
1973 if path:
1972 if path.startswith("http://"):
1974 if path.startswith("http://"):
1973 return httprepository(ui, path)
1975 return httprepository(ui, path)
1974 if path.startswith("hg://"):
1976 if path.startswith("hg://"):
1975 return httprepository(ui, path.replace("hg://", "http://"))
1977 return httprepository(ui, path.replace("hg://", "http://"))
1976 if path.startswith("old-http://"):
1978 if path.startswith("old-http://"):
1977 return localrepository(ui, path.replace("old-http://", "http://"))
1979 return localrepository(ui, path.replace("old-http://", "http://"))
1978 if path.startswith("ssh://"):
1980 if path.startswith("ssh://"):
1979 return sshrepository(ui, path)
1981 return sshrepository(ui, path)
1980
1982
1981 return localrepository(ui, path, create)
1983 return localrepository(ui, path, create)
@@ -1,229 +1,238 b''
1 # util.py - utility functions and platform specfic implementations
1 # util.py - utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, errno
8 import os, errno
9 from demandload import *
9 from demandload import *
10 demandload(globals(), "re")
10 demandload(globals(), "re")
11
11
12 def unique(g):
12 def unique(g):
13 seen = {}
13 seen = {}
14 for f in g:
14 for f in g:
15 if f not in seen:
15 if f not in seen:
16 seen[f] = 1
16 seen[f] = 1
17 yield f
17 yield f
18
18
19 class CommandError(Exception): pass
19 class CommandError(Exception): pass
20
20
21 def always(fn): return True
21 def always(fn): return True
22 def never(fn): return False
22 def never(fn): return False
23
23
24 def globre(pat, head = '^', tail = '$'):
24 def globre(pat, head = '^', tail = '$'):
25 "convert a glob pattern into a regexp"
25 "convert a glob pattern into a regexp"
26 i, n = 0, len(pat)
26 i, n = 0, len(pat)
27 res = ''
27 res = ''
28 group = False
28 group = False
29 def peek(): return i < n and pat[i]
29 def peek(): return i < n and pat[i]
30 while i < n:
30 while i < n:
31 c = pat[i]
31 c = pat[i]
32 i = i+1
32 i = i+1
33 if c == '*':
33 if c == '*':
34 if peek() == '*':
34 if peek() == '*':
35 i += 1
35 i += 1
36 res += '.*'
36 res += '.*'
37 else:
37 else:
38 res += '[^/]*'
38 res += '[^/]*'
39 elif c == '?':
39 elif c == '?':
40 res += '.'
40 res += '.'
41 elif c == '[':
41 elif c == '[':
42 j = i
42 j = i
43 if j < n and pat[j] in '!]':
43 if j < n and pat[j] in '!]':
44 j += 1
44 j += 1
45 while j < n and pat[j] != ']':
45 while j < n and pat[j] != ']':
46 j += 1
46 j += 1
47 if j >= n:
47 if j >= n:
48 res += '\\['
48 res += '\\['
49 else:
49 else:
50 stuff = pat[i:j].replace('\\','\\\\')
50 stuff = pat[i:j].replace('\\','\\\\')
51 i = j + 1
51 i = j + 1
52 if stuff[0] == '!':
52 if stuff[0] == '!':
53 stuff = '^' + stuff[1:]
53 stuff = '^' + stuff[1:]
54 elif stuff[0] == '^':
54 elif stuff[0] == '^':
55 stuff = '\\' + stuff
55 stuff = '\\' + stuff
56 res = '%s[%s]' % (res, stuff)
56 res = '%s[%s]' % (res, stuff)
57 elif c == '{':
57 elif c == '{':
58 group = True
58 group = True
59 res += '(?:'
59 res += '(?:'
60 elif c == '}' and group:
60 elif c == '}' and group:
61 res += ')'
61 res += ')'
62 group = False
62 group = False
63 elif c == ',' and group:
63 elif c == ',' and group:
64 res += '|'
64 res += '|'
65 else:
65 else:
66 res += re.escape(c)
66 res += re.escape(c)
67 return head + res + tail
67 return head + res + tail
68
68
69 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
69 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
70
70
71 def matcher(cwd, names, inc, exc, head = ''):
71 def matcher(cwd, names, inc, exc, head = ''):
72 def patlike(name):
72 def patkind(name):
73 for prefix in 're:', 'glob:', 'path:':
73 for prefix in 're:', 'glob:', 'path:':
74 if name.startswith(prefix): return True
74 if name.startswith(prefix): return name.split(':', 1)
75 for c in name:
75 for c in name:
76 if c in _globchars: return True
76 if c in _globchars: return 'glob', name
77 return 'relpath', name
78
79 cwdsep = cwd + os.sep
77
80
78 def regex(name, tail):
81 def regex(name, tail):
79 '''convert a pattern into a regular expression'''
82 '''convert a pattern into a regular expression'''
80 if name.startswith('re:'):
83 kind, name = patkind(name)
81 return name[3:]
84 if kind == 're':
82 elif name.startswith('path:'):
85 return name
83 return '^' + re.escape(name[5:]) + '$'
86 elif kind == 'path':
84 elif name.startswith('glob:'):
87 return '^' + re.escape(name) + '$'
85 return head + globre(name[5:], '', tail)
88 if cwd: name = os.path.join(cwdsep, name)
89 name = os.path.normpath(name)
90 if name == '.': name = '**'
86 return head + globre(name, '', tail)
91 return head + globre(name, '', tail)
87
92
88 cwdsep = cwd + os.sep
89
90 def under(fn):
93 def under(fn):
91 """check if fn is under our cwd"""
94 """check if fn is under our cwd"""
92 return not cwd or fn.startswith(cwdsep)
95 return not cwd or fn.startswith(cwdsep)
93
96
94 def matchfn(pats, tail):
97 def matchfn(pats, tail):
95 """build a matching function from a set of patterns"""
98 """build a matching function from a set of patterns"""
96 if pats:
99 if pats:
97 pat = '(?:%s)' % '|'.join([regex(p, tail) for p in pats])
100 pat = '(?:%s)' % '|'.join([regex(p, tail) for p in pats])
98 if cwd:
99 pat = re.escape(cwdsep) + pat
100 return re.compile(pat).match
101 return re.compile(pat).match
101
102
102 pats = filter(patlike, names)
103 def globprefix(pat):
103 files = [n for n in names if not patlike(n)]
104 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
104 if pats: plain = []
105 root = []
105 elif cwd: plain = [cwdsep + f for f in files]
106 for p in pat.split(os.sep):
106 else: plain = files
107 if patkind(p)[0] == 'glob': break
108 root.append(p)
109 return os.sep.join(root)
110
111 patkinds = map(patkind, names)
112 pats = [name for (kind, name) in patkinds if kind != 'relpath']
113 files = [name for (kind, name) in patkinds if kind == 'relpath']
114 roots = filter(None, map(globprefix, pats)) + files
115 if cwd: roots = [cwdsep + r for r in roots]
107
116
108 patmatch = matchfn(pats, '$')
117 patmatch = matchfn(pats, '$') or always
109 filematch = matchfn(files, '(?:/|$)')
118 filematch = matchfn(files, '(?:/|$)') or always
110 incmatch = matchfn(inc, '(?:/|$)') or under
119 incmatch = matchfn(inc, '(?:/|$)') or always
111 excmatch = matchfn(exc, '(?:/|$)') or (lambda fn: False)
120 excmatch = matchfn(exc, '(?:/|$)') or (lambda fn: False)
112
121
113 return plain, lambda fn: (incmatch(fn) and not excmatch(fn) and
122 return roots, lambda fn: (incmatch(fn) and not excmatch(fn) and
114 (fn.endswith('/') or
123 (fn.endswith('/') or
115 (not pats and not files) or
124 (not pats and not files) or
116 (pats and patmatch(fn)) or
125 (pats and patmatch(fn)) or
117 (files and filematch(fn))))
126 (files and filematch(fn))))
118
127
119 def system(cmd, errprefix=None):
128 def system(cmd, errprefix=None):
120 """execute a shell command that must succeed"""
129 """execute a shell command that must succeed"""
121 rc = os.system(cmd)
130 rc = os.system(cmd)
122 if rc:
131 if rc:
123 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
132 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
124 explain_exit(rc)[0])
133 explain_exit(rc)[0])
125 if errprefix:
134 if errprefix:
126 errmsg = "%s: %s" % (errprefix, errmsg)
135 errmsg = "%s: %s" % (errprefix, errmsg)
127 raise CommandError(errmsg)
136 raise CommandError(errmsg)
128
137
129 def rename(src, dst):
138 def rename(src, dst):
130 try:
139 try:
131 os.rename(src, dst)
140 os.rename(src, dst)
132 except:
141 except:
133 os.unlink(dst)
142 os.unlink(dst)
134 os.rename(src, dst)
143 os.rename(src, dst)
135
144
136 def copytree(src, dst, copyfile):
145 def copytree(src, dst, copyfile):
137 """Copy a directory tree, files are copied using 'copyfile'."""
146 """Copy a directory tree, files are copied using 'copyfile'."""
138 names = os.listdir(src)
147 names = os.listdir(src)
139 os.mkdir(dst)
148 os.mkdir(dst)
140
149
141 for name in names:
150 for name in names:
142 srcname = os.path.join(src, name)
151 srcname = os.path.join(src, name)
143 dstname = os.path.join(dst, name)
152 dstname = os.path.join(dst, name)
144 if os.path.isdir(srcname):
153 if os.path.isdir(srcname):
145 copytree(srcname, dstname, copyfile)
154 copytree(srcname, dstname, copyfile)
146 elif os.path.isfile(srcname):
155 elif os.path.isfile(srcname):
147 copyfile(srcname, dstname)
156 copyfile(srcname, dstname)
148 else:
157 else:
149 raise IOError("Not a regular file: %r" % srcname)
158 raise IOError("Not a regular file: %r" % srcname)
150
159
151 def _makelock_file(info, pathname):
160 def _makelock_file(info, pathname):
152 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
161 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
153 os.write(ld, info)
162 os.write(ld, info)
154 os.close(ld)
163 os.close(ld)
155
164
156 def _readlock_file(pathname):
165 def _readlock_file(pathname):
157 return file(pathname).read()
166 return file(pathname).read()
158
167
159 # Platfor specific varients
168 # Platfor specific varients
160 if os.name == 'nt':
169 if os.name == 'nt':
161 nulldev = 'NUL:'
170 nulldev = 'NUL:'
162
171
163 def is_exec(f, last):
172 def is_exec(f, last):
164 return last
173 return last
165
174
166 def set_exec(f, mode):
175 def set_exec(f, mode):
167 pass
176 pass
168
177
169 def pconvert(path):
178 def pconvert(path):
170 return path.replace("\\", "/")
179 return path.replace("\\", "/")
171
180
172 makelock = _makelock_file
181 makelock = _makelock_file
173 readlock = _readlock_file
182 readlock = _readlock_file
174
183
175 def explain_exit(code):
184 def explain_exit(code):
176 return "exited with status %d" % code, code
185 return "exited with status %d" % code, code
177
186
178 else:
187 else:
179 nulldev = '/dev/null'
188 nulldev = '/dev/null'
180
189
181 def is_exec(f, last):
190 def is_exec(f, last):
182 return (os.stat(f).st_mode & 0100 != 0)
191 return (os.stat(f).st_mode & 0100 != 0)
183
192
184 def set_exec(f, mode):
193 def set_exec(f, mode):
185 s = os.stat(f).st_mode
194 s = os.stat(f).st_mode
186 if (s & 0100 != 0) == mode:
195 if (s & 0100 != 0) == mode:
187 return
196 return
188 if mode:
197 if mode:
189 # Turn on +x for every +r bit when making a file executable
198 # Turn on +x for every +r bit when making a file executable
190 # and obey umask.
199 # and obey umask.
191 umask = os.umask(0)
200 umask = os.umask(0)
192 os.umask(umask)
201 os.umask(umask)
193 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
202 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
194 else:
203 else:
195 os.chmod(f, s & 0666)
204 os.chmod(f, s & 0666)
196
205
197 def pconvert(path):
206 def pconvert(path):
198 return path
207 return path
199
208
200 def makelock(info, pathname):
209 def makelock(info, pathname):
201 try:
210 try:
202 os.symlink(info, pathname)
211 os.symlink(info, pathname)
203 except OSError, why:
212 except OSError, why:
204 if why.errno == errno.EEXIST:
213 if why.errno == errno.EEXIST:
205 raise
214 raise
206 else:
215 else:
207 _makelock_file(info, pathname)
216 _makelock_file(info, pathname)
208
217
209 def readlock(pathname):
218 def readlock(pathname):
210 try:
219 try:
211 return os.readlink(pathname)
220 return os.readlink(pathname)
212 except OSError, why:
221 except OSError, why:
213 if why.errno == errno.EINVAL:
222 if why.errno == errno.EINVAL:
214 return _readlock_file(pathname)
223 return _readlock_file(pathname)
215 else:
224 else:
216 raise
225 raise
217
226
218 def explain_exit(code):
227 def explain_exit(code):
219 """return a 2-tuple (desc, code) describing a process's status"""
228 """return a 2-tuple (desc, code) describing a process's status"""
220 if os.WIFEXITED(code):
229 if os.WIFEXITED(code):
221 val = os.WEXITSTATUS(code)
230 val = os.WEXITSTATUS(code)
222 return "exited with status %d" % val, val
231 return "exited with status %d" % val, val
223 elif os.WIFSIGNALED(code):
232 elif os.WIFSIGNALED(code):
224 val = os.WTERMSIG(code)
233 val = os.WTERMSIG(code)
225 return "killed by signal %d" % val, val
234 return "killed by signal %d" % val, val
226 elif os.WIFSTOPPED(code):
235 elif os.WIFSTOPPED(code):
227 val = os.STOPSIG(code)
236 val = os.STOPSIG(code)
228 return "stopped by signal %d" % val, val
237 return "stopped by signal %d" % val, val
229 raise ValueError("invalid exit code")
238 raise ValueError("invalid exit code")
General Comments 0
You need to be logged in to leave comments. Login now