##// END OF EJS Templates
Refactor diffrevs/diffdir into changes...
mpm@selenic.com -
r536:c15b4bc0 default
parent child Browse files
Show More
@@ -0,0 +1,12 b''
1 #!/bin/sh
2
3 hg init
4 touch a
5 hg add a
6 hg ci -t "a" -u test -d "0 0"
7
8 echo 123 > b
9 hg add b
10 hg diff | sed "s/\(\(---\|+++\).*\)\t.*/\1/"
11
12 hg diff -r tip | sed "s/\(\(---\|+++\).*\)\t.*/\1/"
@@ -0,0 +1,10 b''
1 diff -r 3903775176ed b
2 --- /dev/null
3 +++ b/b
4 @@ -0,0 +1,1 @@
5 +123
6 diff -r 3903775176ed b
7 --- /dev/null
8 +++ b/b
9 @@ -0,0 +1,1 @@
10 +123
@@ -1,917 +1,917 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 import os, re, sys, signal
8 import os, re, sys, signal
9 import fancyopts, ui, hg, util
9 import fancyopts, ui, hg, util
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "mdiff time hgweb traceback random signal errno version")
11 demandload(globals(), "mdiff time hgweb traceback random signal errno version")
12
12
13 class UnknownCommand(Exception): pass
13 class UnknownCommand(Exception): pass
14
14
15 def filterfiles(filters, files):
15 def filterfiles(filters, files):
16 l = [ x for x in files if x in filters ]
16 l = [ x for x in files if x in filters ]
17
17
18 for t in filters:
18 for t in filters:
19 if t and t[-1] != "/": t += "/"
19 if t and t[-1] != "/": t += "/"
20 l += [ x for x in files if x.startswith(t) ]
20 l += [ x for x in files if x.startswith(t) ]
21 return l
21 return l
22
22
23 def relfilter(repo, files):
23 def relfilter(repo, files):
24 if os.getcwd() != repo.root:
24 if os.getcwd() != repo.root:
25 p = os.getcwd()[len(repo.root) + 1: ]
25 p = os.getcwd()[len(repo.root) + 1: ]
26 return filterfiles([util.pconvert(p)], files)
26 return filterfiles([util.pconvert(p)], files)
27 return files
27 return files
28
28
29 def relpath(repo, args):
29 def relpath(repo, args):
30 if os.getcwd() != repo.root:
30 if os.getcwd() != repo.root:
31 p = os.getcwd()[len(repo.root) + 1: ]
31 p = os.getcwd()[len(repo.root) + 1: ]
32 return [ util.pconvert(os.path.normpath(os.path.join(p, x))) for x in args ]
32 return [ util.pconvert(os.path.normpath(os.path.join(p, x))) for x in args ]
33 return args
33 return args
34
34
35 def dodiff(ui, repo, path, files = None, node1 = None, node2 = None):
35 def dodiff(ui, repo, path, files = None, node1 = None, node2 = None):
36 def date(c):
36 def date(c):
37 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
37 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
38
38
39 if node2:
39 if node2:
40 change = repo.changelog.read(node2)
40 change = repo.changelog.read(node2)
41 mmap2 = repo.manifest.read(change[0])
41 mmap2 = repo.manifest.read(change[0])
42 (c, a, d) = repo.diffrevs(node1, node2)
42 (c, a, d, u) = repo.changes(node1, node2)
43 def read(f): return repo.file(f).read(mmap2[f])
43 def read(f): return repo.file(f).read(mmap2[f])
44 date2 = date(change)
44 date2 = date(change)
45 else:
45 else:
46 date2 = time.asctime()
46 date2 = time.asctime()
47 (c, a, d, u) = repo.diffdir(path, node1)
47 (c, a, d, u) = repo.changes(None, node1, path)
48 if not node1:
48 if not node1:
49 node1 = repo.dirstate.parents()[0]
49 node1 = repo.dirstate.parents()[0]
50 def read(f): return repo.wfile(f).read()
50 def read(f): return repo.wfile(f).read()
51
51
52 if ui.quiet:
52 if ui.quiet:
53 r = None
53 r = None
54 else:
54 else:
55 hexfunc = ui.verbose and hg.hex or hg.short
55 hexfunc = ui.verbose and hg.hex or hg.short
56 r = [hexfunc(node) for node in [node1, node2] if node]
56 r = [hexfunc(node) for node in [node1, node2] if node]
57
57
58 change = repo.changelog.read(node1)
58 change = repo.changelog.read(node1)
59 mmap = repo.manifest.read(change[0])
59 mmap = repo.manifest.read(change[0])
60 date1 = date(change)
60 date1 = date(change)
61
61
62 if files:
62 if files:
63 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
63 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
64
64
65 for f in c:
65 for f in c:
66 to = None
66 to = None
67 if f in mmap:
67 if f in mmap:
68 to = repo.file(f).read(mmap[f])
68 to = repo.file(f).read(mmap[f])
69 tn = read(f)
69 tn = read(f)
70 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
70 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
71 for f in a:
71 for f in a:
72 to = None
72 to = None
73 tn = read(f)
73 tn = read(f)
74 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
74 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
75 for f in d:
75 for f in d:
76 to = repo.file(f).read(mmap[f])
76 to = repo.file(f).read(mmap[f])
77 tn = None
77 tn = None
78 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
78 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
79
79
80 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
80 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
81 """show a single changeset or file revision"""
81 """show a single changeset or file revision"""
82 changelog = repo.changelog
82 changelog = repo.changelog
83 if filelog:
83 if filelog:
84 log = filelog
84 log = filelog
85 filerev = rev
85 filerev = rev
86 node = filenode = filelog.node(filerev)
86 node = filenode = filelog.node(filerev)
87 changerev = filelog.linkrev(filenode)
87 changerev = filelog.linkrev(filenode)
88 changenode = changenode or changelog.node(changerev)
88 changenode = changenode or changelog.node(changerev)
89 else:
89 else:
90 log = changelog
90 log = changelog
91 changerev = rev
91 changerev = rev
92 if changenode is None:
92 if changenode is None:
93 changenode = changelog.node(changerev)
93 changenode = changelog.node(changerev)
94 elif not changerev:
94 elif not changerev:
95 rev = changerev = changelog.rev(changenode)
95 rev = changerev = changelog.rev(changenode)
96 node = changenode
96 node = changenode
97
97
98 if ui.quiet:
98 if ui.quiet:
99 ui.write("%d:%s\n" % (rev, hg.hex(node)))
99 ui.write("%d:%s\n" % (rev, hg.hex(node)))
100 return
100 return
101
101
102 changes = changelog.read(changenode)
102 changes = changelog.read(changenode)
103
103
104 parents = [(log.rev(parent), hg.hex(parent))
104 parents = [(log.rev(parent), hg.hex(parent))
105 for parent in log.parents(node)
105 for parent in log.parents(node)
106 if ui.debugflag or parent != hg.nullid]
106 if ui.debugflag or parent != hg.nullid]
107 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
107 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
108 parents = []
108 parents = []
109
109
110 if filelog:
110 if filelog:
111 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
111 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
112 for parent in parents:
112 for parent in parents:
113 ui.write("parent: %d:%s\n" % parent)
113 ui.write("parent: %d:%s\n" % parent)
114 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
114 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
115 else:
115 else:
116 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
116 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
117 for tag in repo.nodetags(changenode):
117 for tag in repo.nodetags(changenode):
118 ui.status("tag: %s\n" % tag)
118 ui.status("tag: %s\n" % tag)
119 for parent in parents:
119 for parent in parents:
120 ui.write("parent: %d:%s\n" % parent)
120 ui.write("parent: %d:%s\n" % parent)
121 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
121 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
122 hg.hex(changes[0])))
122 hg.hex(changes[0])))
123 ui.status("user: %s\n" % changes[1])
123 ui.status("user: %s\n" % changes[1])
124 ui.status("date: %s\n" % time.asctime(
124 ui.status("date: %s\n" % time.asctime(
125 time.localtime(float(changes[2].split(' ')[0]))))
125 time.localtime(float(changes[2].split(' ')[0]))))
126 if ui.debugflag:
126 if ui.debugflag:
127 files = repo.diffrevs(changelog.parents(changenode)[0], changenode)
127 files = repo.changes(changelog.parents(changenode)[0], changenode)
128 for key, value in zip(["files:", "files+:", "files-:"], files):
128 for key, value in zip(["files:", "files+:", "files-:"], files):
129 if value:
129 if value:
130 ui.note("%-12s %s\n" % (key, " ".join(value)))
130 ui.note("%-12s %s\n" % (key, " ".join(value)))
131 else:
131 else:
132 ui.note("files: %s\n" % " ".join(changes[3]))
132 ui.note("files: %s\n" % " ".join(changes[3]))
133 description = changes[4].strip()
133 description = changes[4].strip()
134 if description:
134 if description:
135 if ui.verbose:
135 if ui.verbose:
136 ui.status("description:\n")
136 ui.status("description:\n")
137 ui.status(description)
137 ui.status(description)
138 ui.status("\n")
138 ui.status("\n")
139 else:
139 else:
140 ui.status("summary: %s\n" % description.splitlines()[0])
140 ui.status("summary: %s\n" % description.splitlines()[0])
141 ui.status("\n")
141 ui.status("\n")
142
142
143 def show_version(ui):
143 def show_version(ui):
144 """output version and copyright information"""
144 """output version and copyright information"""
145 ui.write("Mercurial version %s\n" % version.get_version())
145 ui.write("Mercurial version %s\n" % version.get_version())
146 ui.status(
146 ui.status(
147 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
147 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
148 "This is free software; see the source for copying conditions. "
148 "This is free software; see the source for copying conditions. "
149 "There is NO\nwarranty; "
149 "There is NO\nwarranty; "
150 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
150 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
151 )
151 )
152
152
153 def help(ui, cmd=None):
153 def help(ui, cmd=None):
154 '''show help for a given command or all commands'''
154 '''show help for a given command or all commands'''
155 if cmd:
155 if cmd:
156 try:
156 try:
157 i = find(cmd)
157 i = find(cmd)
158 ui.write("%s\n\n" % i[2])
158 ui.write("%s\n\n" % i[2])
159
159
160 if i[1]:
160 if i[1]:
161 for s, l, d, c in i[1]:
161 for s, l, d, c in i[1]:
162 opt=' '
162 opt=' '
163 if s: opt = opt + '-' + s + ' '
163 if s: opt = opt + '-' + s + ' '
164 if l: opt = opt + '--' + l + ' '
164 if l: opt = opt + '--' + l + ' '
165 if d: opt = opt + '(' + str(d) + ')'
165 if d: opt = opt + '(' + str(d) + ')'
166 ui.write(opt, "\n")
166 ui.write(opt, "\n")
167 if c: ui.write(' %s\n' % c)
167 if c: ui.write(' %s\n' % c)
168 ui.write("\n")
168 ui.write("\n")
169
169
170 ui.write(i[0].__doc__, "\n")
170 ui.write(i[0].__doc__, "\n")
171 except UnknownCommand:
171 except UnknownCommand:
172 ui.warn("hg: unknown command %s\n" % cmd)
172 ui.warn("hg: unknown command %s\n" % cmd)
173 sys.exit(0)
173 sys.exit(0)
174 else:
174 else:
175 if not ui.quiet:
175 if not ui.quiet:
176 show_version(ui)
176 show_version(ui)
177 ui.write('\n')
177 ui.write('\n')
178 ui.write('hg commands:\n\n')
178 ui.write('hg commands:\n\n')
179
179
180 h = {}
180 h = {}
181 for c, e in table.items():
181 for c, e in table.items():
182 f = c.split("|")[0]
182 f = c.split("|")[0]
183 if f.startswith("debug"):
183 if f.startswith("debug"):
184 continue
184 continue
185 d = ""
185 d = ""
186 if e[0].__doc__:
186 if e[0].__doc__:
187 d = e[0].__doc__.splitlines(0)[0].rstrip()
187 d = e[0].__doc__.splitlines(0)[0].rstrip()
188 h[f] = d
188 h[f] = d
189
189
190 fns = h.keys()
190 fns = h.keys()
191 fns.sort()
191 fns.sort()
192 m = max(map(len, fns))
192 m = max(map(len, fns))
193 for f in fns:
193 for f in fns:
194 ui.write(' %-*s %s\n' % (m, f, h[f]))
194 ui.write(' %-*s %s\n' % (m, f, h[f]))
195
195
196 # Commands start here, listed alphabetically
196 # Commands start here, listed alphabetically
197
197
198 def add(ui, repo, file, *files):
198 def add(ui, repo, file, *files):
199 '''add the specified files on the next commit'''
199 '''add the specified files on the next commit'''
200 repo.add(relpath(repo, (file,) + files))
200 repo.add(relpath(repo, (file,) + files))
201
201
202 def addremove(ui, repo, *files):
202 def addremove(ui, repo, *files):
203 """add all new files, delete all missing files"""
203 """add all new files, delete all missing files"""
204 if files:
204 if files:
205 files = relpath(repo, files)
205 files = relpath(repo, files)
206 d = []
206 d = []
207 u = []
207 u = []
208 for f in files:
208 for f in files:
209 p = repo.wjoin(f)
209 p = repo.wjoin(f)
210 s = repo.dirstate.state(f)
210 s = repo.dirstate.state(f)
211 isfile = os.path.isfile(p)
211 isfile = os.path.isfile(p)
212 if s != 'r' and not isfile:
212 if s != 'r' and not isfile:
213 d.append(f)
213 d.append(f)
214 elif s not in 'nmai' and isfile:
214 elif s not in 'nmai' and isfile:
215 u.append(f)
215 u.append(f)
216 else:
216 else:
217 (c, a, d, u) = repo.diffdir(repo.root)
217 (c, a, d, u) = repo.changes(None, None)
218 repo.add(u)
218 repo.add(u)
219 repo.remove(d)
219 repo.remove(d)
220
220
221 def annotate(u, repo, file, *files, **ops):
221 def annotate(u, repo, file, *files, **ops):
222 """show changeset information per file line"""
222 """show changeset information per file line"""
223 def getnode(rev):
223 def getnode(rev):
224 return hg.short(repo.changelog.node(rev))
224 return hg.short(repo.changelog.node(rev))
225
225
226 def getname(rev):
226 def getname(rev):
227 try:
227 try:
228 return bcache[rev]
228 return bcache[rev]
229 except KeyError:
229 except KeyError:
230 cl = repo.changelog.read(repo.changelog.node(rev))
230 cl = repo.changelog.read(repo.changelog.node(rev))
231 name = cl[1]
231 name = cl[1]
232 f = name.find('@')
232 f = name.find('@')
233 if f >= 0:
233 if f >= 0:
234 name = name[:f]
234 name = name[:f]
235 f = name.find('<')
235 f = name.find('<')
236 if f >= 0:
236 if f >= 0:
237 name = name[f+1:]
237 name = name[f+1:]
238 bcache[rev] = name
238 bcache[rev] = name
239 return name
239 return name
240
240
241 bcache = {}
241 bcache = {}
242 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
242 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
243 if not ops['user'] and not ops['changeset']:
243 if not ops['user'] and not ops['changeset']:
244 ops['number'] = 1
244 ops['number'] = 1
245
245
246 node = repo.dirstate.parents()[0]
246 node = repo.dirstate.parents()[0]
247 if ops['revision']:
247 if ops['revision']:
248 node = repo.changelog.lookup(ops['revision'])
248 node = repo.changelog.lookup(ops['revision'])
249 change = repo.changelog.read(node)
249 change = repo.changelog.read(node)
250 mmap = repo.manifest.read(change[0])
250 mmap = repo.manifest.read(change[0])
251 for f in relpath(repo, (file,) + files):
251 for f in relpath(repo, (file,) + files):
252 lines = repo.file(f).annotate(mmap[f])
252 lines = repo.file(f).annotate(mmap[f])
253 pieces = []
253 pieces = []
254
254
255 for o, f in opmap:
255 for o, f in opmap:
256 if ops[o]:
256 if ops[o]:
257 l = [ f(n) for n,t in lines ]
257 l = [ f(n) for n,t in lines ]
258 m = max(map(len, l))
258 m = max(map(len, l))
259 pieces.append([ "%*s" % (m, x) for x in l])
259 pieces.append([ "%*s" % (m, x) for x in l])
260
260
261 for p,l in zip(zip(*pieces), lines):
261 for p,l in zip(zip(*pieces), lines):
262 u.write(" ".join(p) + ": " + l[1])
262 u.write(" ".join(p) + ": " + l[1])
263
263
264 def cat(ui, repo, file, rev = []):
264 def cat(ui, repo, file, rev = []):
265 """output the latest or given revision of a file"""
265 """output the latest or given revision of a file"""
266 r = repo.file(relpath(repo, [file])[0])
266 r = repo.file(relpath(repo, [file])[0])
267 n = r.tip()
267 n = r.tip()
268 if rev: n = r.lookup(rev)
268 if rev: n = r.lookup(rev)
269 sys.stdout.write(r.read(n))
269 sys.stdout.write(r.read(n))
270
270
271 def clone(ui, source, dest = None, **opts):
271 def clone(ui, source, dest = None, **opts):
272 """make a copy of an existing repository"""
272 """make a copy of an existing repository"""
273 source = ui.expandpath(source)
273 source = ui.expandpath(source)
274
274
275 if dest is None:
275 if dest is None:
276 dest = os.path.basename(os.path.normpath(source))
276 dest = os.path.basename(os.path.normpath(source))
277
277
278 if os.path.exists(dest):
278 if os.path.exists(dest):
279 ui.warn("abort: destination '%s' already exists\n" % dest)
279 ui.warn("abort: destination '%s' already exists\n" % dest)
280 return 1
280 return 1
281
281
282 class dircleanup:
282 class dircleanup:
283 def __init__(self, dir):
283 def __init__(self, dir):
284 self.dir = dir
284 self.dir = dir
285 os.mkdir(dir)
285 os.mkdir(dir)
286 def close(self):
286 def close(self):
287 self.dir = None
287 self.dir = None
288 def __del__(self):
288 def __del__(self):
289 if self.dir:
289 if self.dir:
290 import shutil
290 import shutil
291 shutil.rmtree(self.dir, True)
291 shutil.rmtree(self.dir, True)
292
292
293 d = dircleanup(dest)
293 d = dircleanup(dest)
294
294
295 link = 0
295 link = 0
296 if not source.startswith("http://"):
296 if not source.startswith("http://"):
297 d1 = os.stat(dest).st_dev
297 d1 = os.stat(dest).st_dev
298 d2 = os.stat(source).st_dev
298 d2 = os.stat(source).st_dev
299 if d1 == d2: link = 1
299 if d1 == d2: link = 1
300
300
301 if link:
301 if link:
302 ui.note("copying by hardlink\n")
302 ui.note("copying by hardlink\n")
303 util.system("cp -al '%s'/.hg '%s'/.hg" % (source, dest))
303 util.system("cp -al '%s'/.hg '%s'/.hg" % (source, dest))
304 try:
304 try:
305 os.remove(os.path.join(dest, ".hg", "dirstate"))
305 os.remove(os.path.join(dest, ".hg", "dirstate"))
306 except: pass
306 except: pass
307
307
308 repo = hg.repository(ui, dest)
308 repo = hg.repository(ui, dest)
309
309
310 else:
310 else:
311 repo = hg.repository(ui, dest, create=1)
311 repo = hg.repository(ui, dest, create=1)
312 other = hg.repository(ui, source)
312 other = hg.repository(ui, source)
313 fetch = repo.findincoming(other)
313 fetch = repo.findincoming(other)
314 if fetch:
314 if fetch:
315 cg = other.changegroup(fetch)
315 cg = other.changegroup(fetch)
316 repo.addchangegroup(cg)
316 repo.addchangegroup(cg)
317
317
318 f = repo.opener("hgrc", "w")
318 f = repo.opener("hgrc", "w")
319 f.write("[paths]\n")
319 f.write("[paths]\n")
320 f.write("default = %s\n" % source)
320 f.write("default = %s\n" % source)
321
321
322 if not opts['noupdate']:
322 if not opts['noupdate']:
323 update(ui, repo)
323 update(ui, repo)
324
324
325 d.close()
325 d.close()
326
326
327 def commit(ui, repo, *files, **opts):
327 def commit(ui, repo, *files, **opts):
328 """commit the specified files or all outstanding changes"""
328 """commit the specified files or all outstanding changes"""
329 text = opts['text']
329 text = opts['text']
330 if not text and opts['logfile']:
330 if not text and opts['logfile']:
331 try: text = open(opts['logfile']).read()
331 try: text = open(opts['logfile']).read()
332 except IOError: pass
332 except IOError: pass
333
333
334 if opts['addremove']:
334 if opts['addremove']:
335 addremove(ui, repo, *files)
335 addremove(ui, repo, *files)
336 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
336 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
337
337
338 def copy(ui, repo, source, dest):
338 def copy(ui, repo, source, dest):
339 """mark a file as copied or renamed for the next commit"""
339 """mark a file as copied or renamed for the next commit"""
340 return repo.copy(*relpath(repo, (source, dest)))
340 return repo.copy(*relpath(repo, (source, dest)))
341
341
342 def debugcheckdirstate(ui, repo):
342 def debugcheckdirstate(ui, repo):
343 parent1, parent2 = repo.dirstate.parents()
343 parent1, parent2 = repo.dirstate.parents()
344 dc = repo.dirstate.dup()
344 dc = repo.dirstate.dup()
345 keys = dc.keys()
345 keys = dc.keys()
346 keys.sort()
346 keys.sort()
347 m1n = repo.changelog.read(parent1)[0]
347 m1n = repo.changelog.read(parent1)[0]
348 m2n = repo.changelog.read(parent2)[0]
348 m2n = repo.changelog.read(parent2)[0]
349 m1 = repo.manifest.read(m1n)
349 m1 = repo.manifest.read(m1n)
350 m2 = repo.manifest.read(m2n)
350 m2 = repo.manifest.read(m2n)
351 errors = 0
351 errors = 0
352 for f in dc:
352 for f in dc:
353 state = repo.dirstate.state(f)
353 state = repo.dirstate.state(f)
354 if state in "nr" and f not in m1:
354 if state in "nr" and f not in m1:
355 print "%s in state %s, but not listed in manifest1" % (f, state)
355 print "%s in state %s, but not listed in manifest1" % (f, state)
356 errors += 1
356 errors += 1
357 if state in "a" and f in m1:
357 if state in "a" and f in m1:
358 print "%s in state %s, but also listed in manifest1" % (f, state)
358 print "%s in state %s, but also listed in manifest1" % (f, state)
359 errors += 1
359 errors += 1
360 if state in "m" and f not in m1 and f not in m2:
360 if state in "m" and f not in m1 and f not in m2:
361 print "%s in state %s, but not listed in either manifest" % (f, state)
361 print "%s in state %s, but not listed in either manifest" % (f, state)
362 errors += 1
362 errors += 1
363 for f in m1:
363 for f in m1:
364 state = repo.dirstate.state(f)
364 state = repo.dirstate.state(f)
365 if state not in "nrm":
365 if state not in "nrm":
366 print "%s in manifest1, but listed as state %s" % (f, state)
366 print "%s in manifest1, but listed as state %s" % (f, state)
367 errors += 1
367 errors += 1
368 if errors:
368 if errors:
369 print ".hg/dirstate inconsistent with current parent's manifest, aborting"
369 print ".hg/dirstate inconsistent with current parent's manifest, aborting"
370 sys.exit(1)
370 sys.exit(1)
371
371
372 def debugdumpdirstate(ui, repo):
372 def debugdumpdirstate(ui, repo):
373 dc = repo.dirstate.dup()
373 dc = repo.dirstate.dup()
374 keys = dc.keys()
374 keys = dc.keys()
375 keys.sort()
375 keys.sort()
376 for file in keys:
376 for file in keys:
377 print "%s => %c" % (file, dc[file][0])
377 print "%s => %c" % (file, dc[file][0])
378
378
379 def debugindex(ui, file):
379 def debugindex(ui, file):
380 r = hg.revlog(hg.opener(""), file, "")
380 r = hg.revlog(hg.opener(""), file, "")
381 print " rev offset length base linkrev"+\
381 print " rev offset length base linkrev"+\
382 " p1 p2 nodeid"
382 " p1 p2 nodeid"
383 for i in range(r.count()):
383 for i in range(r.count()):
384 e = r.index[i]
384 e = r.index[i]
385 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
385 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
386 i, e[0], e[1], e[2], e[3],
386 i, e[0], e[1], e[2], e[3],
387 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
387 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
388
388
389 def debugindexdot(ui, file):
389 def debugindexdot(ui, file):
390 r = hg.revlog(hg.opener(""), file, "")
390 r = hg.revlog(hg.opener(""), file, "")
391 print "digraph G {"
391 print "digraph G {"
392 for i in range(r.count()):
392 for i in range(r.count()):
393 e = r.index[i]
393 e = r.index[i]
394 print "\t%d -> %d" % (r.rev(e[4]), i)
394 print "\t%d -> %d" % (r.rev(e[4]), i)
395 if e[5] != hg.nullid:
395 if e[5] != hg.nullid:
396 print "\t%d -> %d" % (r.rev(e[5]), i)
396 print "\t%d -> %d" % (r.rev(e[5]), i)
397 print "}"
397 print "}"
398
398
399 def diff(ui, repo, *files, **opts):
399 def diff(ui, repo, *files, **opts):
400 """diff working directory (or selected files)"""
400 """diff working directory (or selected files)"""
401 revs = []
401 revs = []
402 if opts['rev']:
402 if opts['rev']:
403 revs = map(lambda x: repo.lookup(x), opts['rev'])
403 revs = map(lambda x: repo.lookup(x), opts['rev'])
404
404
405 if len(revs) > 2:
405 if len(revs) > 2:
406 ui.warn("too many revisions to diff\n")
406 ui.warn("too many revisions to diff\n")
407 sys.exit(1)
407 sys.exit(1)
408
408
409 if files:
409 if files:
410 files = relpath(repo, files)
410 files = relpath(repo, files)
411 else:
411 else:
412 files = relpath(repo, [""])
412 files = relpath(repo, [""])
413
413
414 dodiff(ui, repo, os.getcwd(), files, *revs)
414 dodiff(ui, repo, os.getcwd(), files, *revs)
415
415
416 def export(ui, repo, changeset):
416 def export(ui, repo, changeset):
417 """dump the changeset header and diffs for a revision"""
417 """dump the changeset header and diffs for a revision"""
418 node = repo.lookup(changeset)
418 node = repo.lookup(changeset)
419 prev, other = repo.changelog.parents(node)
419 prev, other = repo.changelog.parents(node)
420 change = repo.changelog.read(node)
420 change = repo.changelog.read(node)
421 print "# HG changeset patch"
421 print "# HG changeset patch"
422 print "# User %s" % change[1]
422 print "# User %s" % change[1]
423 print "# Node ID %s" % hg.hex(node)
423 print "# Node ID %s" % hg.hex(node)
424 print "# Parent %s" % hg.hex(prev)
424 print "# Parent %s" % hg.hex(prev)
425 print
425 print
426 if other != hg.nullid:
426 if other != hg.nullid:
427 print "# Parent %s" % hg.hex(other)
427 print "# Parent %s" % hg.hex(other)
428 print change[4].rstrip()
428 print change[4].rstrip()
429 print
429 print
430
430
431 dodiff(ui, repo, "", None, prev, node)
431 dodiff(ui, repo, "", None, prev, node)
432
432
433 def forget(ui, repo, file, *files):
433 def forget(ui, repo, file, *files):
434 """don't add the specified files on the next commit"""
434 """don't add the specified files on the next commit"""
435 repo.forget(relpath(repo, (file,) + files))
435 repo.forget(relpath(repo, (file,) + files))
436
436
437 def heads(ui, repo):
437 def heads(ui, repo):
438 """show current repository heads"""
438 """show current repository heads"""
439 for n in repo.changelog.heads():
439 for n in repo.changelog.heads():
440 show_changeset(ui, repo, changenode=n)
440 show_changeset(ui, repo, changenode=n)
441
441
442 def identify(ui, repo):
442 def identify(ui, repo):
443 """print information about the working copy"""
443 """print information about the working copy"""
444 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
444 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
445 if not parents:
445 if not parents:
446 ui.write("unknown\n")
446 ui.write("unknown\n")
447 return
447 return
448
448
449 hexfunc = ui.verbose and hg.hex or hg.short
449 hexfunc = ui.verbose and hg.hex or hg.short
450 (c, a, d, u) = repo.diffdir(repo.root)
450 (c, a, d, u) = repo.changes(None, None)
451 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
451 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
452 (c or a or d) and "+" or "")]
452 (c or a or d) and "+" or "")]
453
453
454 if not ui.quiet:
454 if not ui.quiet:
455 # multiple tags for a single parent separated by '/'
455 # multiple tags for a single parent separated by '/'
456 parenttags = ['/'.join(tags)
456 parenttags = ['/'.join(tags)
457 for tags in map(repo.nodetags, parents) if tags]
457 for tags in map(repo.nodetags, parents) if tags]
458 # tags for multiple parents separated by ' + '
458 # tags for multiple parents separated by ' + '
459 output.append(' + '.join(parenttags))
459 output.append(' + '.join(parenttags))
460
460
461 ui.write("%s\n" % ' '.join(output))
461 ui.write("%s\n" % ' '.join(output))
462
462
463 def import_(ui, repo, patch1, *patches, **opts):
463 def import_(ui, repo, patch1, *patches, **opts):
464 """import an ordered set of patches"""
464 """import an ordered set of patches"""
465 try:
465 try:
466 import psyco
466 import psyco
467 psyco.full()
467 psyco.full()
468 except:
468 except:
469 pass
469 pass
470
470
471 patches = (patch1,) + patches
471 patches = (patch1,) + patches
472
472
473 d = opts["base"]
473 d = opts["base"]
474 strip = opts["strip"]
474 strip = opts["strip"]
475
475
476 for patch in patches:
476 for patch in patches:
477 ui.status("applying %s\n" % patch)
477 ui.status("applying %s\n" % patch)
478 pf = os.path.join(d, patch)
478 pf = os.path.join(d, patch)
479
479
480 text = ""
480 text = ""
481 for l in file(pf):
481 for l in file(pf):
482 if l[:4] == "--- ": break
482 if l[:4] == "--- ": break
483 text += l
483 text += l
484
484
485 # make sure text isn't empty
485 # make sure text isn't empty
486 if not text: text = "imported patch %s\n" % patch
486 if not text: text = "imported patch %s\n" % patch
487
487
488 f = os.popen("patch -p%d < %s" % (strip, pf))
488 f = os.popen("patch -p%d < %s" % (strip, pf))
489 files = []
489 files = []
490 for l in f.read().splitlines():
490 for l in f.read().splitlines():
491 l.rstrip('\r\n');
491 l.rstrip('\r\n');
492 ui.status("%s\n" % l)
492 ui.status("%s\n" % l)
493 if l[:14] == 'patching file ':
493 if l[:14] == 'patching file ':
494 pf = l[14:]
494 pf = l[14:]
495 if pf not in files:
495 if pf not in files:
496 files.append(pf)
496 files.append(pf)
497 patcherr = f.close()
497 patcherr = f.close()
498 if patcherr:
498 if patcherr:
499 sys.stderr.write("patch failed")
499 sys.stderr.write("patch failed")
500 sys.exit(1)
500 sys.exit(1)
501
501
502 if len(files) > 0:
502 if len(files) > 0:
503 addremove(ui, repo, *files)
503 addremove(ui, repo, *files)
504 repo.commit(files, text)
504 repo.commit(files, text)
505
505
506 def init(ui, source=None):
506 def init(ui, source=None):
507 """create a new repository in the current directory"""
507 """create a new repository in the current directory"""
508
508
509 if source:
509 if source:
510 ui.warn("no longer supported: use \"hg clone\" instead\n")
510 ui.warn("no longer supported: use \"hg clone\" instead\n")
511 sys.exit(1)
511 sys.exit(1)
512 repo = hg.repository(ui, ".", create=1)
512 repo = hg.repository(ui, ".", create=1)
513
513
514 def log(ui, repo, f = None):
514 def log(ui, repo, f = None):
515 """show the revision history of the repository or a single file"""
515 """show the revision history of the repository or a single file"""
516 if f:
516 if f:
517 f = relpath(repo, [f])[0]
517 f = relpath(repo, [f])[0]
518 r = repo.file(f)
518 r = repo.file(f)
519 for i in range(r.count() - 1, -1, -1):
519 for i in range(r.count() - 1, -1, -1):
520 show_changeset(ui, repo, filelog=r, rev=i)
520 show_changeset(ui, repo, filelog=r, rev=i)
521 else:
521 else:
522 for i in range(repo.changelog.count() - 1, -1, -1):
522 for i in range(repo.changelog.count() - 1, -1, -1):
523 show_changeset(ui, repo, rev=i)
523 show_changeset(ui, repo, rev=i)
524
524
525 def manifest(ui, repo, rev = []):
525 def manifest(ui, repo, rev = []):
526 """output the latest or given revision of the project manifest"""
526 """output the latest or given revision of the project manifest"""
527 n = repo.manifest.tip()
527 n = repo.manifest.tip()
528 if rev:
528 if rev:
529 n = repo.manifest.lookup(rev)
529 n = repo.manifest.lookup(rev)
530 m = repo.manifest.read(n)
530 m = repo.manifest.read(n)
531 mf = repo.manifest.readflags(n)
531 mf = repo.manifest.readflags(n)
532 files = m.keys()
532 files = m.keys()
533 files.sort()
533 files.sort()
534
534
535 for f in files:
535 for f in files:
536 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
536 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
537
537
538 def parents(ui, repo, node = None):
538 def parents(ui, repo, node = None):
539 '''show the parents of the current working dir'''
539 '''show the parents of the current working dir'''
540 if node:
540 if node:
541 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
541 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
542 else:
542 else:
543 p = repo.dirstate.parents()
543 p = repo.dirstate.parents()
544
544
545 for n in p:
545 for n in p:
546 if n != hg.nullid:
546 if n != hg.nullid:
547 show_changeset(ui, repo, changenode=n)
547 show_changeset(ui, repo, changenode=n)
548
548
549 def pull(ui, repo, source="default", **opts):
549 def pull(ui, repo, source="default", **opts):
550 """pull changes from the specified source"""
550 """pull changes from the specified source"""
551 source = ui.expandpath(source)
551 source = ui.expandpath(source)
552
552
553 ui.status('pulling from %s\n' % (source))
553 ui.status('pulling from %s\n' % (source))
554
554
555 other = hg.repository(ui, source)
555 other = hg.repository(ui, source)
556 fetch = repo.findincoming(other)
556 fetch = repo.findincoming(other)
557 if not fetch:
557 if not fetch:
558 ui.status("no changes found\n")
558 ui.status("no changes found\n")
559 return
559 return
560
560
561 cg = other.changegroup(fetch)
561 cg = other.changegroup(fetch)
562 r = repo.addchangegroup(cg)
562 r = repo.addchangegroup(cg)
563 if cg and not r:
563 if cg and not r:
564 if opts['update']:
564 if opts['update']:
565 return update(ui, repo)
565 return update(ui, repo)
566 else:
566 else:
567 ui.status("(run 'hg update' to get a working copy)\n")
567 ui.status("(run 'hg update' to get a working copy)\n")
568
568
569 return r
569 return r
570
570
571 def push(ui, repo, dest="default-push"):
571 def push(ui, repo, dest="default-push"):
572 """push changes to the specified destination"""
572 """push changes to the specified destination"""
573 dest = ui.expandpath(dest)
573 dest = ui.expandpath(dest)
574
574
575 if not dest.startswith("ssh://"):
575 if not dest.startswith("ssh://"):
576 ui.warn("abort: can only push to ssh:// destinations currently\n")
576 ui.warn("abort: can only push to ssh:// destinations currently\n")
577 return 1
577 return 1
578
578
579 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
579 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
580 if not m:
580 if not m:
581 ui.warn("abort: couldn't parse destination %s\n" % dest)
581 ui.warn("abort: couldn't parse destination %s\n" % dest)
582 return 1
582 return 1
583
583
584 user, host, port, path = map(m.group, (2, 3, 5, 7))
584 user, host, port, path = map(m.group, (2, 3, 5, 7))
585 uhost = user and ("%s@%s" % (user, host)) or host
585 uhost = user and ("%s@%s" % (user, host)) or host
586 port = port and (" -p %s") % port or ""
586 port = port and (" -p %s") % port or ""
587 path = path or ""
587 path = path or ""
588
588
589 sport = random.randrange(30000, 60000)
589 sport = random.randrange(30000, 60000)
590 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
590 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
591 cmd = cmd % (uhost, port, sport+1, sport, path, sport+1)
591 cmd = cmd % (uhost, port, sport+1, sport, path, sport+1)
592
592
593 child = os.fork()
593 child = os.fork()
594 if not child:
594 if not child:
595 sys.stdout = file("/dev/null", "w")
595 sys.stdout = file("/dev/null", "w")
596 sys.stderr = sys.stdout
596 sys.stderr = sys.stdout
597 hgweb.server(repo.root, "pull", "", "localhost", sport)
597 hgweb.server(repo.root, "pull", "", "localhost", sport)
598 else:
598 else:
599 ui.status("connecting to %s\n" % host)
599 ui.status("connecting to %s\n" % host)
600 r = os.system(cmd)
600 r = os.system(cmd)
601 os.kill(child, signal.SIGTERM)
601 os.kill(child, signal.SIGTERM)
602 return r
602 return r
603
603
604 def rawcommit(ui, repo, *flist, **rc):
604 def rawcommit(ui, repo, *flist, **rc):
605 "raw commit interface"
605 "raw commit interface"
606
606
607 text = rc['text']
607 text = rc['text']
608 if not text and rc['logfile']:
608 if not text and rc['logfile']:
609 try: text = open(rc['logfile']).read()
609 try: text = open(rc['logfile']).read()
610 except IOError: pass
610 except IOError: pass
611 if not text and not rc['logfile']:
611 if not text and not rc['logfile']:
612 print "missing commit text"
612 print "missing commit text"
613 return 1
613 return 1
614
614
615 files = relpath(repo, list(flist))
615 files = relpath(repo, list(flist))
616 if rc['files']:
616 if rc['files']:
617 files += open(rc['files']).read().splitlines()
617 files += open(rc['files']).read().splitlines()
618
618
619 rc['parent'] = map(repo.lookup, rc['parent'])
619 rc['parent'] = map(repo.lookup, rc['parent'])
620
620
621 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
621 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
622
622
623 def recover(ui, repo):
623 def recover(ui, repo):
624 """roll back an interrupted transaction"""
624 """roll back an interrupted transaction"""
625 repo.recover()
625 repo.recover()
626
626
627 def remove(ui, repo, file, *files):
627 def remove(ui, repo, file, *files):
628 """remove the specified files on the next commit"""
628 """remove the specified files on the next commit"""
629 repo.remove(relpath(repo, (file,) + files))
629 repo.remove(relpath(repo, (file,) + files))
630
630
631 def root(ui, repo):
631 def root(ui, repo):
632 """print the root (top) of the current working dir"""
632 """print the root (top) of the current working dir"""
633 ui.write(repo.root + "\n")
633 ui.write(repo.root + "\n")
634
634
635 def serve(ui, repo, **opts):
635 def serve(ui, repo, **opts):
636 """export the repository via HTTP"""
636 """export the repository via HTTP"""
637 hgweb.server(repo.root, opts["name"], opts["templates"],
637 hgweb.server(repo.root, opts["name"], opts["templates"],
638 opts["address"], opts["port"])
638 opts["address"], opts["port"])
639
639
640 def status(ui, repo):
640 def status(ui, repo):
641 '''show changed files in the working directory
641 '''show changed files in the working directory
642
642
643 C = changed
643 C = changed
644 A = added
644 A = added
645 R = removed
645 R = removed
646 ? = not tracked'''
646 ? = not tracked'''
647
647
648 (c, a, d, u) = repo.diffdir(os.getcwd())
648 (c, a, d, u) = repo.changes(None, None, os.getcwd())
649 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
649 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
650
650
651 for f in c: print "C", f
651 for f in c: print "C", f
652 for f in a: print "A", f
652 for f in a: print "A", f
653 for f in d: print "R", f
653 for f in d: print "R", f
654 for f in u: print "?", f
654 for f in u: print "?", f
655
655
656 def tag(ui, repo, name, rev = None, **opts):
656 def tag(ui, repo, name, rev = None, **opts):
657 """add a tag for the current tip or a given revision"""
657 """add a tag for the current tip or a given revision"""
658
658
659 if name == "tip":
659 if name == "tip":
660 ui.warn("abort: 'tip' is a reserved name!\n")
660 ui.warn("abort: 'tip' is a reserved name!\n")
661 return -1
661 return -1
662
662
663 (c, a, d, u) = repo.diffdir(repo.root)
663 (c, a, d, u) = repo.changes(None, None)
664 for x in (c, a, d, u):
664 for x in (c, a, d, u):
665 if ".hgtags" in x:
665 if ".hgtags" in x:
666 ui.warn("abort: working copy of .hgtags is changed!\n")
666 ui.warn("abort: working copy of .hgtags is changed!\n")
667 ui.status("(please commit .hgtags manually)\n")
667 ui.status("(please commit .hgtags manually)\n")
668 return -1
668 return -1
669
669
670 if rev:
670 if rev:
671 r = hg.hex(repo.lookup(rev))
671 r = hg.hex(repo.lookup(rev))
672 else:
672 else:
673 r = hg.hex(repo.changelog.tip())
673 r = hg.hex(repo.changelog.tip())
674
674
675 add = 0
675 add = 0
676 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
676 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
677 repo.wfile(".hgtags", "a").write("%s %s\n" % (r, name))
677 repo.wfile(".hgtags", "a").write("%s %s\n" % (r, name))
678 if add: repo.add([".hgtags"])
678 if add: repo.add([".hgtags"])
679
679
680 if not opts['text']:
680 if not opts['text']:
681 opts['text'] = "Added tag %s for changeset %s" % (name, r)
681 opts['text'] = "Added tag %s for changeset %s" % (name, r)
682
682
683 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
683 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
684
684
685 def tags(ui, repo):
685 def tags(ui, repo):
686 """list repository tags"""
686 """list repository tags"""
687
687
688 l = repo.tagslist()
688 l = repo.tagslist()
689 l.reverse()
689 l.reverse()
690 for t, n in l:
690 for t, n in l:
691 try:
691 try:
692 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
692 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
693 except KeyError:
693 except KeyError:
694 r = " ?:?"
694 r = " ?:?"
695 ui.write("%-30s %s\n" % (t, r))
695 ui.write("%-30s %s\n" % (t, r))
696
696
697 def tip(ui, repo):
697 def tip(ui, repo):
698 """show the tip revision"""
698 """show the tip revision"""
699 n = repo.changelog.tip()
699 n = repo.changelog.tip()
700 show_changeset(ui, repo, changenode=n)
700 show_changeset(ui, repo, changenode=n)
701
701
702 def undo(ui, repo):
702 def undo(ui, repo):
703 """undo the last transaction"""
703 """undo the last transaction"""
704 repo.undo()
704 repo.undo()
705
705
706 def update(ui, repo, node=None, merge=False, clean=False):
706 def update(ui, repo, node=None, merge=False, clean=False):
707 '''update or merge working directory
707 '''update or merge working directory
708
708
709 If there are no outstanding changes in the working directory and
709 If there are no outstanding changes in the working directory and
710 there is a linear relationship between the current version and the
710 there is a linear relationship between the current version and the
711 requested version, the result is the requested version.
711 requested version, the result is the requested version.
712
712
713 Otherwise the result is a merge between the contents of the
713 Otherwise the result is a merge between the contents of the
714 current working directory and the requested version. Files that
714 current working directory and the requested version. Files that
715 changed between either parent are marked as changed for the next
715 changed between either parent are marked as changed for the next
716 commit and a commit must be performed before any further updates
716 commit and a commit must be performed before any further updates
717 are allowed.
717 are allowed.
718 '''
718 '''
719 node = node and repo.lookup(node) or repo.changelog.tip()
719 node = node and repo.lookup(node) or repo.changelog.tip()
720 return repo.update(node, allow=merge, force=clean)
720 return repo.update(node, allow=merge, force=clean)
721
721
722 def verify(ui, repo):
722 def verify(ui, repo):
723 """verify the integrity of the repository"""
723 """verify the integrity of the repository"""
724 return repo.verify()
724 return repo.verify()
725
725
726 # Command options and aliases are listed here, alphabetically
726 # Command options and aliases are listed here, alphabetically
727
727
728 table = {
728 table = {
729 "add": (add, [], "hg add [files]"),
729 "add": (add, [], "hg add [files]"),
730 "addremove": (addremove, [], "hg addremove [files]"),
730 "addremove": (addremove, [], "hg addremove [files]"),
731 "annotate": (annotate,
731 "annotate": (annotate,
732 [('r', 'revision', '', 'revision'),
732 [('r', 'revision', '', 'revision'),
733 ('u', 'user', None, 'show user'),
733 ('u', 'user', None, 'show user'),
734 ('n', 'number', None, 'show revision number'),
734 ('n', 'number', None, 'show revision number'),
735 ('c', 'changeset', None, 'show changeset')],
735 ('c', 'changeset', None, 'show changeset')],
736 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
736 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
737 "cat": (cat, [], 'hg cat <file> [rev]'),
737 "cat": (cat, [], 'hg cat <file> [rev]'),
738 "clone": (clone, [('U', 'noupdate', None, 'skip update after cloning')],
738 "clone": (clone, [('U', 'noupdate', None, 'skip update after cloning')],
739 'hg clone [options] <source> [dest]'),
739 'hg clone [options] <source> [dest]'),
740 "commit|ci": (commit,
740 "commit|ci": (commit,
741 [('t', 'text', "", 'commit text'),
741 [('t', 'text', "", 'commit text'),
742 ('A', 'addremove', None, 'run add/remove during commit'),
742 ('A', 'addremove', None, 'run add/remove during commit'),
743 ('l', 'logfile', "", 'commit text file'),
743 ('l', 'logfile', "", 'commit text file'),
744 ('d', 'date', "", 'data'),
744 ('d', 'date', "", 'data'),
745 ('u', 'user', "", 'user')],
745 ('u', 'user', "", 'user')],
746 'hg commit [files]'),
746 'hg commit [files]'),
747 "copy": (copy, [], 'hg copy <source> <dest>'),
747 "copy": (copy, [], 'hg copy <source> <dest>'),
748 "debugcheckdirstate": (debugcheckdirstate, [], 'debugcheckdirstate'),
748 "debugcheckdirstate": (debugcheckdirstate, [], 'debugcheckdirstate'),
749 "debugdumpdirstate": (debugdumpdirstate, [], 'debugdumpdirstate'),
749 "debugdumpdirstate": (debugdumpdirstate, [], 'debugdumpdirstate'),
750 "debugindex": (debugindex, [], 'debugindex <file>'),
750 "debugindex": (debugindex, [], 'debugindex <file>'),
751 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
751 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
752 "diff": (diff, [('r', 'rev', [], 'revision')],
752 "diff": (diff, [('r', 'rev', [], 'revision')],
753 'hg diff [-r A] [-r B] [files]'),
753 'hg diff [-r A] [-r B] [files]'),
754 "export": (export, [], "hg export <changeset>"),
754 "export": (export, [], "hg export <changeset>"),
755 "forget": (forget, [], "hg forget [files]"),
755 "forget": (forget, [], "hg forget [files]"),
756 "heads": (heads, [], 'hg heads'),
756 "heads": (heads, [], 'hg heads'),
757 "help": (help, [], 'hg help [command]'),
757 "help": (help, [], 'hg help [command]'),
758 "identify|id": (identify, [], 'hg identify'),
758 "identify|id": (identify, [], 'hg identify'),
759 "import|patch": (import_,
759 "import|patch": (import_,
760 [('p', 'strip', 1, 'path strip'),
760 [('p', 'strip', 1, 'path strip'),
761 ('b', 'base', "", 'base path')],
761 ('b', 'base', "", 'base path')],
762 "hg import [options] <patches>"),
762 "hg import [options] <patches>"),
763 "init": (init, [], 'hg init'),
763 "init": (init, [], 'hg init'),
764 "log|history": (log, [], 'hg log [file]'),
764 "log|history": (log, [], 'hg log [file]'),
765 "manifest": (manifest, [], 'hg manifest [rev]'),
765 "manifest": (manifest, [], 'hg manifest [rev]'),
766 "parents": (parents, [], 'hg parents [node]'),
766 "parents": (parents, [], 'hg parents [node]'),
767 "pull": (pull,
767 "pull": (pull,
768 [('u', 'update', None, 'update working directory')],
768 [('u', 'update', None, 'update working directory')],
769 'hg pull [options] [source]'),
769 'hg pull [options] [source]'),
770 "push": (push, [], 'hg push <destination>'),
770 "push": (push, [], 'hg push <destination>'),
771 "rawcommit": (rawcommit,
771 "rawcommit": (rawcommit,
772 [('p', 'parent', [], 'parent'),
772 [('p', 'parent', [], 'parent'),
773 ('d', 'date', "", 'data'),
773 ('d', 'date', "", 'data'),
774 ('u', 'user', "", 'user'),
774 ('u', 'user', "", 'user'),
775 ('F', 'files', "", 'file list'),
775 ('F', 'files', "", 'file list'),
776 ('t', 'text', "", 'commit text'),
776 ('t', 'text', "", 'commit text'),
777 ('l', 'logfile', "", 'commit text file')],
777 ('l', 'logfile', "", 'commit text file')],
778 'hg rawcommit [options] [files]'),
778 'hg rawcommit [options] [files]'),
779 "recover": (recover, [], "hg recover"),
779 "recover": (recover, [], "hg recover"),
780 "remove|rm": (remove, [], "hg remove [files]"),
780 "remove|rm": (remove, [], "hg remove [files]"),
781 "root": (root, [], "hg root"),
781 "root": (root, [], "hg root"),
782 "serve": (serve, [('p', 'port', 8000, 'listen port'),
782 "serve": (serve, [('p', 'port', 8000, 'listen port'),
783 ('a', 'address', '', 'interface address'),
783 ('a', 'address', '', 'interface address'),
784 ('n', 'name', os.getcwd(), 'repository name'),
784 ('n', 'name', os.getcwd(), 'repository name'),
785 ('t', 'templates', "", 'template map')],
785 ('t', 'templates', "", 'template map')],
786 "hg serve [options]"),
786 "hg serve [options]"),
787 "status": (status, [], 'hg status'),
787 "status": (status, [], 'hg status'),
788 "tag": (tag, [('t', 'text', "", 'commit text'),
788 "tag": (tag, [('t', 'text', "", 'commit text'),
789 ('d', 'date', "", 'date'),
789 ('d', 'date', "", 'date'),
790 ('u', 'user', "", 'user')],
790 ('u', 'user', "", 'user')],
791 'hg tag [options] <name> [rev]'),
791 'hg tag [options] <name> [rev]'),
792 "tags": (tags, [], 'hg tags'),
792 "tags": (tags, [], 'hg tags'),
793 "tip": (tip, [], 'hg tip'),
793 "tip": (tip, [], 'hg tip'),
794 "undo": (undo, [], 'hg undo'),
794 "undo": (undo, [], 'hg undo'),
795 "update|up|checkout|co":
795 "update|up|checkout|co":
796 (update,
796 (update,
797 [('m', 'merge', None, 'allow merging of conflicts'),
797 [('m', 'merge', None, 'allow merging of conflicts'),
798 ('C', 'clean', None, 'overwrite locally modified files')],
798 ('C', 'clean', None, 'overwrite locally modified files')],
799 'hg update [options] [node]'),
799 'hg update [options] [node]'),
800 "verify": (verify, [], 'hg verify'),
800 "verify": (verify, [], 'hg verify'),
801 "version": (show_version, [], 'hg version'),
801 "version": (show_version, [], 'hg version'),
802 }
802 }
803
803
804 norepo = "clone init version help debugindex debugindexdot"
804 norepo = "clone init version help debugindex debugindexdot"
805
805
806 def find(cmd):
806 def find(cmd):
807 for e in table.keys():
807 for e in table.keys():
808 if re.match("(%s)$" % e, cmd):
808 if re.match("(%s)$" % e, cmd):
809 return table[e]
809 return table[e]
810
810
811 raise UnknownCommand(cmd)
811 raise UnknownCommand(cmd)
812
812
813 class SignalInterrupt(Exception): pass
813 class SignalInterrupt(Exception): pass
814
814
815 def catchterm(*args):
815 def catchterm(*args):
816 raise SignalInterrupt
816 raise SignalInterrupt
817
817
818 def run():
818 def run():
819 sys.exit(dispatch(sys.argv[1:]))
819 sys.exit(dispatch(sys.argv[1:]))
820
820
821 def dispatch(args):
821 def dispatch(args):
822 options = {}
822 options = {}
823 opts = [('v', 'verbose', None, 'verbose'),
823 opts = [('v', 'verbose', None, 'verbose'),
824 ('d', 'debug', None, 'debug'),
824 ('d', 'debug', None, 'debug'),
825 ('q', 'quiet', None, 'quiet'),
825 ('q', 'quiet', None, 'quiet'),
826 ('p', 'profile', None, 'profile'),
826 ('p', 'profile', None, 'profile'),
827 ('R', 'repository', "", 'repository root directory'),
827 ('R', 'repository', "", 'repository root directory'),
828 ('', 'traceback', None, 'print traceback on exception'),
828 ('', 'traceback', None, 'print traceback on exception'),
829 ('y', 'noninteractive', None, 'run non-interactively'),
829 ('y', 'noninteractive', None, 'run non-interactively'),
830 ('', 'version', None, 'output version information and exit'),
830 ('', 'version', None, 'output version information and exit'),
831 ]
831 ]
832
832
833 args = fancyopts.fancyopts(args, opts, options,
833 args = fancyopts.fancyopts(args, opts, options,
834 'hg [options] <command> [options] [files]')
834 'hg [options] <command> [options] [files]')
835
835
836 if not args:
836 if not args:
837 cmd = "help"
837 cmd = "help"
838 else:
838 else:
839 cmd, args = args[0], args[1:]
839 cmd, args = args[0], args[1:]
840
840
841 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
841 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
842 not options["noninteractive"])
842 not options["noninteractive"])
843
843
844 if options["version"]:
844 if options["version"]:
845 show_version(u)
845 show_version(u)
846 sys.exit(0)
846 sys.exit(0)
847
847
848 try:
848 try:
849 i = find(cmd)
849 i = find(cmd)
850 except UnknownCommand:
850 except UnknownCommand:
851 u.warn("hg: unknown command '%s'\n" % cmd)
851 u.warn("hg: unknown command '%s'\n" % cmd)
852 help(u)
852 help(u)
853 sys.exit(1)
853 sys.exit(1)
854
854
855 signal.signal(signal.SIGTERM, catchterm)
855 signal.signal(signal.SIGTERM, catchterm)
856
856
857 cmdoptions = {}
857 cmdoptions = {}
858 try:
858 try:
859 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
859 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
860 except fancyopts.getopt.GetoptError, inst:
860 except fancyopts.getopt.GetoptError, inst:
861 u.warn("hg %s: %s\n" % (cmd, inst))
861 u.warn("hg %s: %s\n" % (cmd, inst))
862 help(u, cmd)
862 help(u, cmd)
863 sys.exit(-1)
863 sys.exit(-1)
864
864
865 try:
865 try:
866 try:
866 try:
867 if cmd not in norepo.split():
867 if cmd not in norepo.split():
868 path = options["repository"] or ""
868 path = options["repository"] or ""
869 repo = hg.repository(ui=u, path=path)
869 repo = hg.repository(ui=u, path=path)
870 d = lambda: i[0](u, repo, *args, **cmdoptions)
870 d = lambda: i[0](u, repo, *args, **cmdoptions)
871 else:
871 else:
872 d = lambda: i[0](u, *args, **cmdoptions)
872 d = lambda: i[0](u, *args, **cmdoptions)
873
873
874 if options['profile']:
874 if options['profile']:
875 import hotshot, hotshot.stats
875 import hotshot, hotshot.stats
876 prof = hotshot.Profile("hg.prof")
876 prof = hotshot.Profile("hg.prof")
877 r = prof.runcall(d)
877 r = prof.runcall(d)
878 prof.close()
878 prof.close()
879 stats = hotshot.stats.load("hg.prof")
879 stats = hotshot.stats.load("hg.prof")
880 stats.strip_dirs()
880 stats.strip_dirs()
881 stats.sort_stats('time', 'calls')
881 stats.sort_stats('time', 'calls')
882 stats.print_stats(40)
882 stats.print_stats(40)
883 return r
883 return r
884 else:
884 else:
885 return d()
885 return d()
886 except:
886 except:
887 if options['traceback']:
887 if options['traceback']:
888 traceback.print_exc()
888 traceback.print_exc()
889 raise
889 raise
890 except util.CommandError, inst:
890 except util.CommandError, inst:
891 u.warn("abort: %s\n" % inst.args)
891 u.warn("abort: %s\n" % inst.args)
892 except hg.RepoError, inst:
892 except hg.RepoError, inst:
893 u.warn("abort: ", inst, "!\n")
893 u.warn("abort: ", inst, "!\n")
894 except SignalInterrupt:
894 except SignalInterrupt:
895 u.warn("killed!\n")
895 u.warn("killed!\n")
896 except KeyboardInterrupt:
896 except KeyboardInterrupt:
897 u.warn("interrupted!\n")
897 u.warn("interrupted!\n")
898 except IOError, inst:
898 except IOError, inst:
899 if hasattr(inst, "code"):
899 if hasattr(inst, "code"):
900 u.warn("abort: %s\n" % inst)
900 u.warn("abort: %s\n" % inst)
901 elif hasattr(inst, "reason"):
901 elif hasattr(inst, "reason"):
902 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
902 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
903 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
903 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
904 u.warn("broken pipe\n")
904 u.warn("broken pipe\n")
905 else:
905 else:
906 raise
906 raise
907 except TypeError, inst:
907 except TypeError, inst:
908 # was this an argument error?
908 # was this an argument error?
909 tb = traceback.extract_tb(sys.exc_info()[2])
909 tb = traceback.extract_tb(sys.exc_info()[2])
910 if len(tb) > 2: # no
910 if len(tb) > 2: # no
911 raise
911 raise
912 u.debug(inst, "\n")
912 u.debug(inst, "\n")
913 u.warn("%s: invalid arguments\n" % i[0].__name__)
913 u.warn("%s: invalid arguments\n" % i[0].__name__)
914 help(u, cmd)
914 help(u, cmd)
915
915
916 sys.exit(-1)
916 sys.exit(-1)
917
917
@@ -1,1496 +1,1522 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")
13 demandload(globals(), "tempfile httprangereader bdiff")
14
14
15 class filelog(revlog):
15 class filelog(revlog):
16 def __init__(self, opener, path):
16 def __init__(self, opener, path):
17 revlog.__init__(self, opener,
17 revlog.__init__(self, opener,
18 os.path.join("data", path + ".i"),
18 os.path.join("data", path + ".i"),
19 os.path.join("data", path + ".d"))
19 os.path.join("data", path + ".d"))
20
20
21 def read(self, node):
21 def read(self, node):
22 t = self.revision(node)
22 t = self.revision(node)
23 if t[:2] != '\1\n':
23 if t[:2] != '\1\n':
24 return t
24 return t
25 s = t.find('\1\n', 2)
25 s = t.find('\1\n', 2)
26 return t[s+2:]
26 return t[s+2:]
27
27
28 def readmeta(self, node):
28 def readmeta(self, node):
29 t = self.revision(node)
29 t = self.revision(node)
30 if t[:2] != '\1\n':
30 if t[:2] != '\1\n':
31 return t
31 return t
32 s = t.find('\1\n', 2)
32 s = t.find('\1\n', 2)
33 mt = t[2:s]
33 mt = t[2:s]
34 for l in mt.splitlines():
34 for l in mt.splitlines():
35 k, v = l.split(": ", 1)
35 k, v = l.split(": ", 1)
36 m[k] = v
36 m[k] = v
37 return m
37 return m
38
38
39 def add(self, text, meta, transaction, link, p1=None, p2=None):
39 def add(self, text, meta, transaction, link, p1=None, p2=None):
40 if meta or text[:2] == '\1\n':
40 if meta or text[:2] == '\1\n':
41 mt = ""
41 mt = ""
42 if meta:
42 if meta:
43 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
43 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
44 text = "\1\n" + "".join(mt) + "\1\n" + text
44 text = "\1\n" + "".join(mt) + "\1\n" + text
45 return self.addrevision(text, transaction, link, p1, p2)
45 return self.addrevision(text, transaction, link, p1, p2)
46
46
47 def annotate(self, node):
47 def annotate(self, node):
48
48
49 def decorate(text, rev):
49 def decorate(text, rev):
50 return ([rev] * len(text.splitlines()), text)
50 return ([rev] * len(text.splitlines()), text)
51
51
52 def pair(parent, child):
52 def pair(parent, child):
53 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
53 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
54 child[0][b1:b2] = parent[0][a1:a2]
54 child[0][b1:b2] = parent[0][a1:a2]
55 return child
55 return child
56
56
57 # find all ancestors
57 # find all ancestors
58 needed = {node:1}
58 needed = {node:1}
59 visit = [node]
59 visit = [node]
60 while visit:
60 while visit:
61 n = visit.pop(0)
61 n = visit.pop(0)
62 for p in self.parents(n):
62 for p in self.parents(n):
63 if p not in needed:
63 if p not in needed:
64 needed[p] = 1
64 needed[p] = 1
65 visit.append(p)
65 visit.append(p)
66 else:
66 else:
67 # count how many times we'll use this
67 # count how many times we'll use this
68 needed[p] += 1
68 needed[p] += 1
69
69
70 # sort by revision which is a topological order
70 # sort by revision which is a topological order
71 visit = [ (self.rev(n), n) for n in needed.keys() ]
71 visit = [ (self.rev(n), n) for n in needed.keys() ]
72 visit.sort()
72 visit.sort()
73 hist = {}
73 hist = {}
74
74
75 for r,n in visit:
75 for r,n in visit:
76 curr = decorate(self.read(n), self.linkrev(n))
76 curr = decorate(self.read(n), self.linkrev(n))
77 for p in self.parents(n):
77 for p in self.parents(n):
78 if p != nullid:
78 if p != nullid:
79 curr = pair(hist[p], curr)
79 curr = pair(hist[p], curr)
80 # trim the history of unneeded revs
80 # trim the history of unneeded revs
81 needed[p] -= 1
81 needed[p] -= 1
82 if not needed[p]:
82 if not needed[p]:
83 del hist[p]
83 del hist[p]
84 hist[n] = curr
84 hist[n] = curr
85
85
86 return zip(hist[n][0], hist[n][1].splitlines(1))
86 return zip(hist[n][0], hist[n][1].splitlines(1))
87
87
88 class manifest(revlog):
88 class manifest(revlog):
89 def __init__(self, opener):
89 def __init__(self, opener):
90 self.mapcache = None
90 self.mapcache = None
91 self.listcache = None
91 self.listcache = None
92 self.addlist = None
92 self.addlist = None
93 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
93 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
94
94
95 def read(self, node):
95 def read(self, node):
96 if node == nullid: return {} # don't upset local cache
96 if node == nullid: return {} # don't upset local cache
97 if self.mapcache and self.mapcache[0] == node:
97 if self.mapcache and self.mapcache[0] == node:
98 return self.mapcache[1].copy()
98 return self.mapcache[1].copy()
99 text = self.revision(node)
99 text = self.revision(node)
100 map = {}
100 map = {}
101 flag = {}
101 flag = {}
102 self.listcache = (text, text.splitlines(1))
102 self.listcache = (text, text.splitlines(1))
103 for l in self.listcache[1]:
103 for l in self.listcache[1]:
104 (f, n) = l.split('\0')
104 (f, n) = l.split('\0')
105 map[f] = bin(n[:40])
105 map[f] = bin(n[:40])
106 flag[f] = (n[40:-1] == "x")
106 flag[f] = (n[40:-1] == "x")
107 self.mapcache = (node, map, flag)
107 self.mapcache = (node, map, flag)
108 return map
108 return map
109
109
110 def readflags(self, node):
110 def readflags(self, node):
111 if node == nullid: return {} # don't upset local cache
111 if node == nullid: return {} # don't upset local cache
112 if not self.mapcache or self.mapcache[0] != node:
112 if not self.mapcache or self.mapcache[0] != node:
113 self.read(node)
113 self.read(node)
114 return self.mapcache[2]
114 return self.mapcache[2]
115
115
116 def diff(self, a, b):
116 def diff(self, a, b):
117 # this is sneaky, as we're not actually using a and b
117 # this is sneaky, as we're not actually using a and b
118 if self.listcache and self.addlist and self.listcache[0] == a:
118 if self.listcache and self.addlist and self.listcache[0] == a:
119 d = mdiff.diff(self.listcache[1], self.addlist, 1)
119 d = mdiff.diff(self.listcache[1], self.addlist, 1)
120 if mdiff.patch(a, d) != b:
120 if mdiff.patch(a, d) != b:
121 sys.stderr.write("*** sortdiff failed, falling back ***\n")
121 sys.stderr.write("*** sortdiff failed, falling back ***\n")
122 return mdiff.textdiff(a, b)
122 return mdiff.textdiff(a, b)
123 return d
123 return d
124 else:
124 else:
125 return mdiff.textdiff(a, b)
125 return mdiff.textdiff(a, b)
126
126
127 def add(self, map, flags, transaction, link, p1=None, p2=None):
127 def add(self, map, flags, transaction, link, p1=None, p2=None):
128 files = map.keys()
128 files = map.keys()
129 files.sort()
129 files.sort()
130
130
131 self.addlist = ["%s\000%s%s\n" %
131 self.addlist = ["%s\000%s%s\n" %
132 (f, hex(map[f]), flags[f] and "x" or '')
132 (f, hex(map[f]), flags[f] and "x" or '')
133 for f in files]
133 for f in files]
134 text = "".join(self.addlist)
134 text = "".join(self.addlist)
135
135
136 n = self.addrevision(text, transaction, link, p1, p2)
136 n = self.addrevision(text, transaction, link, p1, p2)
137 self.mapcache = (n, map, flags)
137 self.mapcache = (n, map, flags)
138 self.listcache = (text, self.addlist)
138 self.listcache = (text, self.addlist)
139 self.addlist = None
139 self.addlist = None
140
140
141 return n
141 return n
142
142
143 class changelog(revlog):
143 class changelog(revlog):
144 def __init__(self, opener):
144 def __init__(self, opener):
145 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
145 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
146
146
147 def extract(self, text):
147 def extract(self, text):
148 if not text:
148 if not text:
149 return (nullid, "", "0", [], "")
149 return (nullid, "", "0", [], "")
150 last = text.index("\n\n")
150 last = text.index("\n\n")
151 desc = text[last + 2:]
151 desc = text[last + 2:]
152 l = text[:last].splitlines()
152 l = text[:last].splitlines()
153 manifest = bin(l[0])
153 manifest = bin(l[0])
154 user = l[1]
154 user = l[1]
155 date = l[2]
155 date = l[2]
156 files = l[3:]
156 files = l[3:]
157 return (manifest, user, date, files, desc)
157 return (manifest, user, date, files, desc)
158
158
159 def read(self, node):
159 def read(self, node):
160 return self.extract(self.revision(node))
160 return self.extract(self.revision(node))
161
161
162 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
162 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
163 user=None, date=None):
163 user=None, date=None):
164 user = (user or
164 user = (user or
165 os.environ.get("HGUSER") or
165 os.environ.get("HGUSER") or
166 os.environ.get("EMAIL") or
166 os.environ.get("EMAIL") or
167 (os.environ.get("LOGNAME",
167 (os.environ.get("LOGNAME",
168 os.environ.get("USERNAME", "unknown"))
168 os.environ.get("USERNAME", "unknown"))
169 + '@' + socket.getfqdn()))
169 + '@' + socket.getfqdn()))
170 date = date or "%d %d" % (time.time(), time.timezone)
170 date = date or "%d %d" % (time.time(), time.timezone)
171 list.sort()
171 list.sort()
172 l = [hex(manifest), user, date] + list + ["", desc]
172 l = [hex(manifest), user, date] + list + ["", desc]
173 text = "\n".join(l)
173 text = "\n".join(l)
174 return self.addrevision(text, transaction, self.count(), p1, p2)
174 return self.addrevision(text, transaction, self.count(), p1, p2)
175
175
176 class dirstate:
176 class dirstate:
177 def __init__(self, opener, ui, root):
177 def __init__(self, opener, ui, root):
178 self.opener = opener
178 self.opener = opener
179 self.root = root
179 self.root = root
180 self.dirty = 0
180 self.dirty = 0
181 self.ui = ui
181 self.ui = ui
182 self.map = None
182 self.map = None
183 self.pl = None
183 self.pl = None
184 self.copies = {}
184 self.copies = {}
185
185
186 def __del__(self):
186 def __del__(self):
187 if self.dirty:
187 if self.dirty:
188 self.write()
188 self.write()
189
189
190 def __getitem__(self, key):
190 def __getitem__(self, key):
191 try:
191 try:
192 return self.map[key]
192 return self.map[key]
193 except TypeError:
193 except TypeError:
194 self.read()
194 self.read()
195 return self[key]
195 return self[key]
196
196
197 def __contains__(self, key):
197 def __contains__(self, key):
198 if not self.map: self.read()
198 if not self.map: self.read()
199 return key in self.map
199 return key in self.map
200
200
201 def parents(self):
201 def parents(self):
202 if not self.pl:
202 if not self.pl:
203 self.read()
203 self.read()
204 return self.pl
204 return self.pl
205
205
206 def setparents(self, p1, p2 = nullid):
206 def setparents(self, p1, p2 = nullid):
207 self.dirty = 1
207 self.dirty = 1
208 self.pl = p1, p2
208 self.pl = p1, p2
209
209
210 def state(self, key):
210 def state(self, key):
211 try:
211 try:
212 return self[key][0]
212 return self[key][0]
213 except KeyError:
213 except KeyError:
214 return "?"
214 return "?"
215
215
216 def read(self):
216 def read(self):
217 if self.map is not None: return self.map
217 if self.map is not None: return self.map
218
218
219 self.map = {}
219 self.map = {}
220 self.pl = [nullid, nullid]
220 self.pl = [nullid, nullid]
221 try:
221 try:
222 st = self.opener("dirstate").read()
222 st = self.opener("dirstate").read()
223 if not st: return
223 if not st: return
224 except: return
224 except: return
225
225
226 self.pl = [st[:20], st[20: 40]]
226 self.pl = [st[:20], st[20: 40]]
227
227
228 pos = 40
228 pos = 40
229 while pos < len(st):
229 while pos < len(st):
230 e = struct.unpack(">cllll", st[pos:pos+17])
230 e = struct.unpack(">cllll", st[pos:pos+17])
231 l = e[4]
231 l = e[4]
232 pos += 17
232 pos += 17
233 f = st[pos:pos + l]
233 f = st[pos:pos + l]
234 if '\0' in f:
234 if '\0' in f:
235 f, c = f.split('\0')
235 f, c = f.split('\0')
236 self.copies[f] = c
236 self.copies[f] = c
237 self.map[f] = e[:4]
237 self.map[f] = e[:4]
238 pos += l
238 pos += l
239
239
240 def copy(self, source, dest):
240 def copy(self, source, dest):
241 self.read()
241 self.read()
242 self.dirty = 1
242 self.dirty = 1
243 self.copies[dest] = source
243 self.copies[dest] = source
244
244
245 def copied(self, file):
245 def copied(self, file):
246 return self.copies.get(file, None)
246 return self.copies.get(file, None)
247
247
248 def update(self, files, state):
248 def update(self, files, state):
249 ''' current states:
249 ''' current states:
250 n normal
250 n normal
251 m needs merging
251 m needs merging
252 r marked for removal
252 r marked for removal
253 a marked for addition'''
253 a marked for addition'''
254
254
255 if not files: return
255 if not files: return
256 self.read()
256 self.read()
257 self.dirty = 1
257 self.dirty = 1
258 for f in files:
258 for f in files:
259 if state == "r":
259 if state == "r":
260 self.map[f] = ('r', 0, 0, 0)
260 self.map[f] = ('r', 0, 0, 0)
261 else:
261 else:
262 s = os.stat(os.path.join(self.root, f))
262 s = os.stat(os.path.join(self.root, f))
263 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
263 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
264
264
265 def forget(self, files):
265 def forget(self, files):
266 if not files: return
266 if not files: return
267 self.read()
267 self.read()
268 self.dirty = 1
268 self.dirty = 1
269 for f in files:
269 for f in files:
270 try:
270 try:
271 del self.map[f]
271 del self.map[f]
272 except KeyError:
272 except KeyError:
273 self.ui.warn("not in dirstate: %s!\n" % f)
273 self.ui.warn("not in dirstate: %s!\n" % f)
274 pass
274 pass
275
275
276 def clear(self):
276 def clear(self):
277 self.map = {}
277 self.map = {}
278 self.dirty = 1
278 self.dirty = 1
279
279
280 def write(self):
280 def write(self):
281 st = self.opener("dirstate", "w")
281 st = self.opener("dirstate", "w")
282 st.write("".join(self.pl))
282 st.write("".join(self.pl))
283 for f, e in self.map.items():
283 for f, e in self.map.items():
284 c = self.copied(f)
284 c = self.copied(f)
285 if c:
285 if c:
286 f = f + "\0" + c
286 f = f + "\0" + c
287 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
287 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
288 st.write(e + f)
288 st.write(e + f)
289 self.dirty = 0
289 self.dirty = 0
290
290
291 def dup(self):
291 def changes(self, files, ignore):
292 self.read()
292 self.read()
293 return self.map.copy()
293 dc = self.map.copy()
294 lookup, changed, added, unknown = [], [], [], []
295
296 # compare all files by default
297 if not files: files = [self.root]
298
299 def uniq(g):
300 seen = {}
301 for f in g:
302 if f not in seen:
303 seen[f] = 1
304 yield f
305
306 # recursive generator of all files listed
307 def walk(files):
308 for f in uniq(files):
309 if os.path.isdir(f):
310 for dir, subdirs, fl in os.walk(f):
311 d = dir[len(self.root) + 1:]
312 if ".hg" in subdirs: subdirs.remove(".hg")
313 for fn in fl:
314 fn = util.pconvert(os.path.join(d, fn))
315 yield fn
316 else:
317 yield f[len(self.root) + 1:]
318
319 for fn in uniq(walk(files)):
320 try: s = os.stat(os.path.join(self.root, fn))
321 except: continue
322
323 if fn in dc:
324 c = dc[fn]
325 del dc[fn]
326
327 if c[0] == 'm':
328 changed.append(fn)
329 elif c[0] == 'a':
330 added.append(fn)
331 elif c[0] == 'r':
332 unknown.append(fn)
333 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
334 changed.append(fn)
335 elif c[1] != s.st_mode or c[3] != s.st_mtime:
336 lookup.append(fn)
337 else:
338 if not ignore(fn): unknown.append(fn)
339
340 return (lookup, changed, added, dc.keys(), unknown)
294
341
295 # used to avoid circular references so destructors work
342 # used to avoid circular references so destructors work
296 def opener(base):
343 def opener(base):
297 p = base
344 p = base
298 def o(path, mode="r"):
345 def o(path, mode="r"):
299 if p[:7] == "http://":
346 if p[:7] == "http://":
300 f = os.path.join(p, urllib.quote(path))
347 f = os.path.join(p, urllib.quote(path))
301 return httprangereader.httprangereader(f)
348 return httprangereader.httprangereader(f)
302
349
303 f = os.path.join(p, path)
350 f = os.path.join(p, path)
304
351
305 mode += "b" # for that other OS
352 mode += "b" # for that other OS
306
353
307 if mode[0] != "r":
354 if mode[0] != "r":
308 try:
355 try:
309 s = os.stat(f)
356 s = os.stat(f)
310 except OSError:
357 except OSError:
311 d = os.path.dirname(f)
358 d = os.path.dirname(f)
312 if not os.path.isdir(d):
359 if not os.path.isdir(d):
313 os.makedirs(d)
360 os.makedirs(d)
314 else:
361 else:
315 if s.st_nlink > 1:
362 if s.st_nlink > 1:
316 file(f + ".tmp", "wb").write(file(f, "rb").read())
363 file(f + ".tmp", "wb").write(file(f, "rb").read())
317 util.rename(f+".tmp", f)
364 util.rename(f+".tmp", f)
318
365
319 return file(f, mode)
366 return file(f, mode)
320
367
321 return o
368 return o
322
369
323 class RepoError(Exception): pass
370 class RepoError(Exception): pass
324
371
325 class localrepository:
372 class localrepository:
326 def __init__(self, ui, path=None, create=0):
373 def __init__(self, ui, path=None, create=0):
327 self.remote = 0
374 self.remote = 0
328 if path and path[:7] == "http://":
375 if path and path[:7] == "http://":
329 self.remote = 1
376 self.remote = 1
330 self.path = path
377 self.path = path
331 else:
378 else:
332 if not path:
379 if not path:
333 p = os.getcwd()
380 p = os.getcwd()
334 while not os.path.isdir(os.path.join(p, ".hg")):
381 while not os.path.isdir(os.path.join(p, ".hg")):
335 oldp = p
382 oldp = p
336 p = os.path.dirname(p)
383 p = os.path.dirname(p)
337 if p == oldp: raise RepoError("no repo found")
384 if p == oldp: raise RepoError("no repo found")
338 path = p
385 path = p
339 self.path = os.path.join(path, ".hg")
386 self.path = os.path.join(path, ".hg")
340
387
341 if not create and not os.path.isdir(self.path):
388 if not create and not os.path.isdir(self.path):
342 raise RepoError("repository %s not found" % self.path)
389 raise RepoError("repository %s not found" % self.path)
343
390
344 self.root = path
391 self.root = path
345 self.ui = ui
392 self.ui = ui
346
393
347 if create:
394 if create:
348 os.mkdir(self.path)
395 os.mkdir(self.path)
349 os.mkdir(self.join("data"))
396 os.mkdir(self.join("data"))
350
397
351 self.opener = opener(self.path)
398 self.opener = opener(self.path)
352 self.wopener = opener(self.root)
399 self.wopener = opener(self.root)
353 self.manifest = manifest(self.opener)
400 self.manifest = manifest(self.opener)
354 self.changelog = changelog(self.opener)
401 self.changelog = changelog(self.opener)
355 self.ignorelist = None
402 self.ignorelist = None
356 self.tagscache = None
403 self.tagscache = None
357 self.nodetagscache = None
404 self.nodetagscache = None
358
405
359 if not self.remote:
406 if not self.remote:
360 self.dirstate = dirstate(self.opener, ui, self.root)
407 self.dirstate = dirstate(self.opener, ui, self.root)
361 try:
408 try:
362 self.ui.readconfig(self.opener("hgrc"))
409 self.ui.readconfig(self.opener("hgrc"))
363 except IOError: pass
410 except IOError: pass
364
411
365 def ignore(self, f):
412 def ignore(self, f):
366 if self.ignorelist is None:
413 if self.ignorelist is None:
367 self.ignorelist = []
414 self.ignorelist = []
368 try:
415 try:
369 l = file(self.wjoin(".hgignore"))
416 l = file(self.wjoin(".hgignore"))
370 for pat in l:
417 for pat in l:
371 if pat != "\n":
418 if pat != "\n":
372 self.ignorelist.append(re.compile(util.pconvert(pat[:-1])))
419 self.ignorelist.append(re.compile(util.pconvert(pat[:-1])))
373 except IOError: pass
420 except IOError: pass
374 for pat in self.ignorelist:
421 for pat in self.ignorelist:
375 if pat.search(f): return True
422 if pat.search(f): return True
376 return False
423 return False
377
424
378 def hook(self, name, **args):
425 def hook(self, name, **args):
379 s = self.ui.config("hooks", name)
426 s = self.ui.config("hooks", name)
380 if s:
427 if s:
381 self.ui.note("running hook %s: %s\n" % (name, s))
428 self.ui.note("running hook %s: %s\n" % (name, s))
382 old = {}
429 old = {}
383 for k, v in args.items():
430 for k, v in args.items():
384 k = k.upper()
431 k = k.upper()
385 old[k] = os.environ.get(k, None)
432 old[k] = os.environ.get(k, None)
386 os.environ[k] = v
433 os.environ[k] = v
387
434
388 r = os.system(s)
435 r = os.system(s)
389
436
390 for k, v in old.items():
437 for k, v in old.items():
391 if v != None:
438 if v != None:
392 os.environ[k] = v
439 os.environ[k] = v
393 else:
440 else:
394 del os.environ[k]
441 del os.environ[k]
395
442
396 if r:
443 if r:
397 self.ui.warn("abort: %s hook failed with status %d!\n" %
444 self.ui.warn("abort: %s hook failed with status %d!\n" %
398 (name, r))
445 (name, r))
399 return False
446 return False
400 return True
447 return True
401
448
402 def tags(self):
449 def tags(self):
403 '''return a mapping of tag to node'''
450 '''return a mapping of tag to node'''
404 if not self.tagscache:
451 if not self.tagscache:
405 self.tagscache = {}
452 self.tagscache = {}
406 try:
453 try:
407 # read each head of the tags file, ending with the tip
454 # read each head of the tags file, ending with the tip
408 # and add each tag found to the map, with "newer" ones
455 # and add each tag found to the map, with "newer" ones
409 # taking precedence
456 # taking precedence
410 fl = self.file(".hgtags")
457 fl = self.file(".hgtags")
411 h = fl.heads()
458 h = fl.heads()
412 h.reverse()
459 h.reverse()
413 for r in h:
460 for r in h:
414 for l in fl.revision(r).splitlines():
461 for l in fl.revision(r).splitlines():
415 if l:
462 if l:
416 n, k = l.split(" ", 1)
463 n, k = l.split(" ", 1)
417 try:
464 try:
418 bin_n = bin(n)
465 bin_n = bin(n)
419 except TypeError:
466 except TypeError:
420 bin_n = ''
467 bin_n = ''
421 self.tagscache[k.strip()] = bin_n
468 self.tagscache[k.strip()] = bin_n
422 except KeyError:
469 except KeyError:
423 pass
470 pass
424 for k, n in self.ui.configitems("tags"):
471 for k, n in self.ui.configitems("tags"):
425 try:
472 try:
426 bin_n = bin(n)
473 bin_n = bin(n)
427 except TypeError:
474 except TypeError:
428 bin_n = ''
475 bin_n = ''
429 self.tagscache[k] = bin_n
476 self.tagscache[k] = bin_n
430
477
431 self.tagscache['tip'] = self.changelog.tip()
478 self.tagscache['tip'] = self.changelog.tip()
432
479
433 return self.tagscache
480 return self.tagscache
434
481
435 def tagslist(self):
482 def tagslist(self):
436 '''return a list of tags ordered by revision'''
483 '''return a list of tags ordered by revision'''
437 l = []
484 l = []
438 for t, n in self.tags().items():
485 for t, n in self.tags().items():
439 try:
486 try:
440 r = self.changelog.rev(n)
487 r = self.changelog.rev(n)
441 except:
488 except:
442 r = -2 # sort to the beginning of the list if unknown
489 r = -2 # sort to the beginning of the list if unknown
443 l.append((r,t,n))
490 l.append((r,t,n))
444 l.sort()
491 l.sort()
445 return [(t,n) for r,t,n in l]
492 return [(t,n) for r,t,n in l]
446
493
447 def nodetags(self, node):
494 def nodetags(self, node):
448 '''return the tags associated with a node'''
495 '''return the tags associated with a node'''
449 if not self.nodetagscache:
496 if not self.nodetagscache:
450 self.nodetagscache = {}
497 self.nodetagscache = {}
451 for t,n in self.tags().items():
498 for t,n in self.tags().items():
452 self.nodetagscache.setdefault(n,[]).append(t)
499 self.nodetagscache.setdefault(n,[]).append(t)
453 return self.nodetagscache.get(node, [])
500 return self.nodetagscache.get(node, [])
454
501
455 def lookup(self, key):
502 def lookup(self, key):
456 try:
503 try:
457 return self.tags()[key]
504 return self.tags()[key]
458 except KeyError:
505 except KeyError:
459 return self.changelog.lookup(key)
506 return self.changelog.lookup(key)
460
507
461 def join(self, f):
508 def join(self, f):
462 return os.path.join(self.path, f)
509 return os.path.join(self.path, f)
463
510
464 def wjoin(self, f):
511 def wjoin(self, f):
465 return os.path.join(self.root, f)
512 return os.path.join(self.root, f)
466
513
467 def file(self, f):
514 def file(self, f):
468 if f[0] == '/': f = f[1:]
515 if f[0] == '/': f = f[1:]
469 return filelog(self.opener, f)
516 return filelog(self.opener, f)
470
517
471 def wfile(self, f, mode='r'):
518 def wfile(self, f, mode='r'):
472 return self.wopener(f, mode)
519 return self.wopener(f, mode)
473
520
474 def transaction(self):
521 def transaction(self):
475 # save dirstate for undo
522 # save dirstate for undo
476 try:
523 try:
477 ds = self.opener("dirstate").read()
524 ds = self.opener("dirstate").read()
478 except IOError:
525 except IOError:
479 ds = ""
526 ds = ""
480 self.opener("undo.dirstate", "w").write(ds)
527 self.opener("undo.dirstate", "w").write(ds)
481
528
482 return transaction.transaction(self.opener, self.join("journal"),
529 return transaction.transaction(self.opener, self.join("journal"),
483 self.join("undo"))
530 self.join("undo"))
484
531
485 def recover(self):
532 def recover(self):
486 lock = self.lock()
533 lock = self.lock()
487 if os.path.exists(self.join("recover")):
534 if os.path.exists(self.join("recover")):
488 self.ui.status("rolling back interrupted transaction\n")
535 self.ui.status("rolling back interrupted transaction\n")
489 return transaction.rollback(self.opener, self.join("recover"))
536 return transaction.rollback(self.opener, self.join("recover"))
490 else:
537 else:
491 self.ui.warn("no interrupted transaction available\n")
538 self.ui.warn("no interrupted transaction available\n")
492
539
493 def undo(self):
540 def undo(self):
494 lock = self.lock()
541 lock = self.lock()
495 if os.path.exists(self.join("undo")):
542 if os.path.exists(self.join("undo")):
496 self.ui.status("rolling back last transaction\n")
543 self.ui.status("rolling back last transaction\n")
497 transaction.rollback(self.opener, self.join("undo"))
544 transaction.rollback(self.opener, self.join("undo"))
498 self.dirstate = None
545 self.dirstate = None
499 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
546 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
500 self.dirstate = dirstate(self.opener, self.ui, self.root)
547 self.dirstate = dirstate(self.opener, self.ui, self.root)
501 else:
548 else:
502 self.ui.warn("no undo information available\n")
549 self.ui.warn("no undo information available\n")
503
550
504 def lock(self, wait = 1):
551 def lock(self, wait = 1):
505 try:
552 try:
506 return lock.lock(self.join("lock"), 0)
553 return lock.lock(self.join("lock"), 0)
507 except lock.LockHeld, inst:
554 except lock.LockHeld, inst:
508 if wait:
555 if wait:
509 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
556 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
510 return lock.lock(self.join("lock"), wait)
557 return lock.lock(self.join("lock"), wait)
511 raise inst
558 raise inst
512
559
513 def rawcommit(self, files, text, user, date, p1=None, p2=None):
560 def rawcommit(self, files, text, user, date, p1=None, p2=None):
514 orig_parent = self.dirstate.parents()[0] or nullid
561 orig_parent = self.dirstate.parents()[0] or nullid
515 p1 = p1 or self.dirstate.parents()[0] or nullid
562 p1 = p1 or self.dirstate.parents()[0] or nullid
516 p2 = p2 or self.dirstate.parents()[1] or nullid
563 p2 = p2 or self.dirstate.parents()[1] or nullid
517 c1 = self.changelog.read(p1)
564 c1 = self.changelog.read(p1)
518 c2 = self.changelog.read(p2)
565 c2 = self.changelog.read(p2)
519 m1 = self.manifest.read(c1[0])
566 m1 = self.manifest.read(c1[0])
520 mf1 = self.manifest.readflags(c1[0])
567 mf1 = self.manifest.readflags(c1[0])
521 m2 = self.manifest.read(c2[0])
568 m2 = self.manifest.read(c2[0])
522
569
523 if orig_parent == p1:
570 if orig_parent == p1:
524 update_dirstate = 1
571 update_dirstate = 1
525 else:
572 else:
526 update_dirstate = 0
573 update_dirstate = 0
527
574
528 tr = self.transaction()
575 tr = self.transaction()
529 mm = m1.copy()
576 mm = m1.copy()
530 mfm = mf1.copy()
577 mfm = mf1.copy()
531 linkrev = self.changelog.count()
578 linkrev = self.changelog.count()
532 for f in files:
579 for f in files:
533 try:
580 try:
534 t = self.wfile(f).read()
581 t = self.wfile(f).read()
535 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
582 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
536 r = self.file(f)
583 r = self.file(f)
537 mfm[f] = tm
584 mfm[f] = tm
538 mm[f] = r.add(t, {}, tr, linkrev,
585 mm[f] = r.add(t, {}, tr, linkrev,
539 m1.get(f, nullid), m2.get(f, nullid))
586 m1.get(f, nullid), m2.get(f, nullid))
540 if update_dirstate:
587 if update_dirstate:
541 self.dirstate.update([f], "n")
588 self.dirstate.update([f], "n")
542 except IOError:
589 except IOError:
543 try:
590 try:
544 del mm[f]
591 del mm[f]
545 del mfm[f]
592 del mfm[f]
546 if update_dirstate:
593 if update_dirstate:
547 self.dirstate.forget([f])
594 self.dirstate.forget([f])
548 except:
595 except:
549 # deleted from p2?
596 # deleted from p2?
550 pass
597 pass
551
598
552 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
599 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
553 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
600 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
554 tr.close()
601 tr.close()
555 if update_dirstate:
602 if update_dirstate:
556 self.dirstate.setparents(n, nullid)
603 self.dirstate.setparents(n, nullid)
557
604
558 def commit(self, files = None, text = "", user = None, date = None):
605 def commit(self, files = None, text = "", user = None, date = None):
559 commit = []
606 commit = []
560 remove = []
607 remove = []
561 if files:
608 if files:
562 for f in files:
609 for f in files:
563 s = self.dirstate.state(f)
610 s = self.dirstate.state(f)
564 if s in 'nmai':
611 if s in 'nmai':
565 commit.append(f)
612 commit.append(f)
566 elif s == 'r':
613 elif s == 'r':
567 remove.append(f)
614 remove.append(f)
568 else:
615 else:
569 self.ui.warn("%s not tracked!\n" % f)
616 self.ui.warn("%s not tracked!\n" % f)
570 else:
617 else:
571 (c, a, d, u) = self.diffdir(self.root)
618 (c, a, d, u) = self.changes(None, None)
572 commit = c + a
619 commit = c + a
573 remove = d
620 remove = d
574
621
575 if not commit and not remove:
622 if not commit and not remove:
576 self.ui.status("nothing changed\n")
623 self.ui.status("nothing changed\n")
577 return
624 return
578
625
579 if not self.hook("precommit"):
626 if not self.hook("precommit"):
580 return 1
627 return 1
581
628
582 p1, p2 = self.dirstate.parents()
629 p1, p2 = self.dirstate.parents()
583 c1 = self.changelog.read(p1)
630 c1 = self.changelog.read(p1)
584 c2 = self.changelog.read(p2)
631 c2 = self.changelog.read(p2)
585 m1 = self.manifest.read(c1[0])
632 m1 = self.manifest.read(c1[0])
586 mf1 = self.manifest.readflags(c1[0])
633 mf1 = self.manifest.readflags(c1[0])
587 m2 = self.manifest.read(c2[0])
634 m2 = self.manifest.read(c2[0])
588 lock = self.lock()
635 lock = self.lock()
589 tr = self.transaction()
636 tr = self.transaction()
590
637
591 # check in files
638 # check in files
592 new = {}
639 new = {}
593 linkrev = self.changelog.count()
640 linkrev = self.changelog.count()
594 commit.sort()
641 commit.sort()
595 for f in commit:
642 for f in commit:
596 self.ui.note(f + "\n")
643 self.ui.note(f + "\n")
597 try:
644 try:
598 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
645 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
599 t = self.wfile(f).read()
646 t = self.wfile(f).read()
600 except IOError:
647 except IOError:
601 self.warn("trouble committing %s!\n" % f)
648 self.warn("trouble committing %s!\n" % f)
602 raise
649 raise
603
650
604 meta = {}
651 meta = {}
605 cp = self.dirstate.copied(f)
652 cp = self.dirstate.copied(f)
606 if cp:
653 if cp:
607 meta["copy"] = cp
654 meta["copy"] = cp
608 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
655 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
609 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
656 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
610
657
611 r = self.file(f)
658 r = self.file(f)
612 fp1 = m1.get(f, nullid)
659 fp1 = m1.get(f, nullid)
613 fp2 = m2.get(f, nullid)
660 fp2 = m2.get(f, nullid)
614 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
661 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
615
662
616 # update manifest
663 # update manifest
617 m1.update(new)
664 m1.update(new)
618 for f in remove:
665 for f in remove:
619 if f in m1:
666 if f in m1:
620 del m1[f]
667 del m1[f]
621 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0])
668 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0])
622
669
623 # add changeset
670 # add changeset
624 new = new.keys()
671 new = new.keys()
625 new.sort()
672 new.sort()
626
673
627 if not text:
674 if not text:
628 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
675 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
629 edittext += "".join(["HG: changed %s\n" % f for f in new])
676 edittext += "".join(["HG: changed %s\n" % f for f in new])
630 edittext += "".join(["HG: removed %s\n" % f for f in remove])
677 edittext += "".join(["HG: removed %s\n" % f for f in remove])
631 edittext = self.ui.edit(edittext)
678 edittext = self.ui.edit(edittext)
632 if not edittext.rstrip():
679 if not edittext.rstrip():
633 return 1
680 return 1
634 text = edittext
681 text = edittext
635
682
636 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
683 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
637
684
638 if not self.hook("commit", node=hex(n)):
685 if not self.hook("commit", node=hex(n)):
639 return 1
686 return 1
640
687
641 tr.close()
688 tr.close()
642
689
643 self.dirstate.setparents(n)
690 self.dirstate.setparents(n)
644 self.dirstate.update(new, "n")
691 self.dirstate.update(new, "n")
645 self.dirstate.forget(remove)
692 self.dirstate.forget(remove)
646
693
647 def diffdir(self, path, changeset = None):
694 def changes(self, node1, node2, *files):
648 changed = []
695 # changed, added, deleted, unknown
649 added = []
696 c, a, d, u, mf1 = [], [], [], [], None
650 unknown = []
651 mf = {}
652
697
653 if changeset:
698 def fcmp(fn, mf):
654 change = self.changelog.read(changeset)
655 mf = self.manifest.read(change[0])
656 dc = dict.fromkeys(mf)
657 else:
658 changeset = self.dirstate.parents()[0]
659 change = self.changelog.read(changeset)
660 mf = self.manifest.read(change[0])
661 dc = self.dirstate.dup()
662
663 def fcmp(fn):
664 t1 = self.wfile(fn).read()
699 t1 = self.wfile(fn).read()
665 t2 = self.file(fn).revision(mf[fn])
700 t2 = self.file(fn).revision(mf[fn])
666 return cmp(t1, t2)
701 return cmp(t1, t2)
667
702
668 for dir, subdirs, files in os.walk(path):
703 # are we comparing the working directory?
669 d = dir[len(self.root)+1:]
704 if not node1:
670 if ".hg" in subdirs: subdirs.remove(".hg")
705 l, c, a, d, u = self.dirstate.changes(files, self.ignore)
706
707 # are we comparing working dir against its parent?
708 if not node2:
709 if l:
710 # do a full compare of any files that might have changed
711 change = self.changelog.read(self.dirstate.parents()[0])
712 mf1 = self.manifest.read(change[0])
713 for f in lookup:
714 if fcmp(f, mf):
715 c.append(f)
716 return (c, a, d, u)
671
717
672 for f in files:
718 # are we comparing working dir against non-tip?
673 fn = util.pconvert(os.path.join(d, f))
719 # generate a pseudo-manifest for the working dir
674 try: s = os.stat(os.path.join(self.root, fn))
720 if not node1:
675 except: continue
721 if not mf1:
676 if fn in dc:
722 change = self.changelog.read(self.dirstate.parents()[0])
677 c = dc[fn]
723 mf1 = self.manifest.read(change[0])
678 del dc[fn]
724 for f in a + c + l:
679 if not c:
725 mf1[f] = ""
680 if fcmp(fn):
726 for f in d:
681 changed.append(fn)
727 if f in mf1: del mf1[f]
682 elif c[0] == 'm':
728 else:
683 changed.append(fn)
729 change = self.changelog.read(node1)
684 elif c[0] == 'a':
730 mf1 = self.manifest.read(change[0])
685 added.append(fn)
686 elif c[0] == 'r':
687 unknown.append(fn)
688 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
689 changed.append(fn)
690 elif c[1] != s.st_mode or c[3] != s.st_mtime:
691 if fcmp(fn):
692 changed.append(fn)
693 else:
694 if self.ignore(fn): continue
695 unknown.append(fn)
696
731
697 deleted = dc.keys()
698 deleted.sort()
699
700 return (changed, added, deleted, unknown)
701
702 def diffrevs(self, node1, node2):
703 changed, added = [], []
704
705 change = self.changelog.read(node1)
706 mf1 = self.manifest.read(change[0])
707 change = self.changelog.read(node2)
732 change = self.changelog.read(node2)
708 mf2 = self.manifest.read(change[0])
733 mf2 = self.manifest.read(change[0])
709
734
710 for fn in mf2:
735 for fn in mf2:
711 if mf1.has_key(fn):
736 if mf1.has_key(fn):
712 if mf1[fn] != mf2[fn]:
737 if mf1[fn] != mf2[fn]:
713 changed.append(fn)
738 if mf1[fn] != "" or fcmp(fn, mf2):
739 c.append(fn)
714 del mf1[fn]
740 del mf1[fn]
715 else:
741 else:
716 added.append(fn)
742 a.append(fn)
717
743
718 deleted = mf1.keys()
744 d = mf1.keys()
719 deleted.sort()
745 d.sort()
720
746
721 return (changed, added, deleted)
747 return (c, a, d, u)
722
748
723 def add(self, list):
749 def add(self, list):
724 for f in list:
750 for f in list:
725 p = self.wjoin(f)
751 p = self.wjoin(f)
726 if not os.path.isfile(p):
752 if not os.path.isfile(p):
727 self.ui.warn("%s does not exist!\n" % f)
753 self.ui.warn("%s does not exist!\n" % f)
728 elif self.dirstate.state(f) == 'n':
754 elif self.dirstate.state(f) == 'n':
729 self.ui.warn("%s already tracked!\n" % f)
755 self.ui.warn("%s already tracked!\n" % f)
730 else:
756 else:
731 self.dirstate.update([f], "a")
757 self.dirstate.update([f], "a")
732
758
733 def forget(self, list):
759 def forget(self, list):
734 for f in list:
760 for f in list:
735 if self.dirstate.state(f) not in 'ai':
761 if self.dirstate.state(f) not in 'ai':
736 self.ui.warn("%s not added!\n" % f)
762 self.ui.warn("%s not added!\n" % f)
737 else:
763 else:
738 self.dirstate.forget([f])
764 self.dirstate.forget([f])
739
765
740 def remove(self, list):
766 def remove(self, list):
741 for f in list:
767 for f in list:
742 p = self.wjoin(f)
768 p = self.wjoin(f)
743 if os.path.isfile(p):
769 if os.path.isfile(p):
744 self.ui.warn("%s still exists!\n" % f)
770 self.ui.warn("%s still exists!\n" % f)
745 elif self.dirstate.state(f) == 'a':
771 elif self.dirstate.state(f) == 'a':
746 self.ui.warn("%s never committed!\n" % f)
772 self.ui.warn("%s never committed!\n" % f)
747 self.dirstate.forget(f)
773 self.dirstate.forget(f)
748 elif f not in self.dirstate:
774 elif f not in self.dirstate:
749 self.ui.warn("%s not tracked!\n" % f)
775 self.ui.warn("%s not tracked!\n" % f)
750 else:
776 else:
751 self.dirstate.update([f], "r")
777 self.dirstate.update([f], "r")
752
778
753 def copy(self, source, dest):
779 def copy(self, source, dest):
754 p = self.wjoin(dest)
780 p = self.wjoin(dest)
755 if not os.path.isfile(dest):
781 if not os.path.isfile(dest):
756 self.ui.warn("%s does not exist!\n" % dest)
782 self.ui.warn("%s does not exist!\n" % dest)
757 else:
783 else:
758 if self.dirstate.state(dest) == '?':
784 if self.dirstate.state(dest) == '?':
759 self.dirstate.update([dest], "a")
785 self.dirstate.update([dest], "a")
760 self.dirstate.copy(source, dest)
786 self.dirstate.copy(source, dest)
761
787
762 def heads(self):
788 def heads(self):
763 return self.changelog.heads()
789 return self.changelog.heads()
764
790
765 def branches(self, nodes):
791 def branches(self, nodes):
766 if not nodes: nodes = [self.changelog.tip()]
792 if not nodes: nodes = [self.changelog.tip()]
767 b = []
793 b = []
768 for n in nodes:
794 for n in nodes:
769 t = n
795 t = n
770 while n:
796 while n:
771 p = self.changelog.parents(n)
797 p = self.changelog.parents(n)
772 if p[1] != nullid or p[0] == nullid:
798 if p[1] != nullid or p[0] == nullid:
773 b.append((t, n, p[0], p[1]))
799 b.append((t, n, p[0], p[1]))
774 break
800 break
775 n = p[0]
801 n = p[0]
776 return b
802 return b
777
803
778 def between(self, pairs):
804 def between(self, pairs):
779 r = []
805 r = []
780
806
781 for top, bottom in pairs:
807 for top, bottom in pairs:
782 n, l, i = top, [], 0
808 n, l, i = top, [], 0
783 f = 1
809 f = 1
784
810
785 while n != bottom:
811 while n != bottom:
786 p = self.changelog.parents(n)[0]
812 p = self.changelog.parents(n)[0]
787 if i == f:
813 if i == f:
788 l.append(n)
814 l.append(n)
789 f = f * 2
815 f = f * 2
790 n = p
816 n = p
791 i += 1
817 i += 1
792
818
793 r.append(l)
819 r.append(l)
794
820
795 return r
821 return r
796
822
797 def newer(self, nodes):
823 def newer(self, nodes):
798 m = {}
824 m = {}
799 nl = []
825 nl = []
800 pm = {}
826 pm = {}
801 cl = self.changelog
827 cl = self.changelog
802 t = l = cl.count()
828 t = l = cl.count()
803
829
804 # find the lowest numbered node
830 # find the lowest numbered node
805 for n in nodes:
831 for n in nodes:
806 l = min(l, cl.rev(n))
832 l = min(l, cl.rev(n))
807 m[n] = 1
833 m[n] = 1
808
834
809 for i in xrange(l, t):
835 for i in xrange(l, t):
810 n = cl.node(i)
836 n = cl.node(i)
811 if n in m: # explicitly listed
837 if n in m: # explicitly listed
812 pm[n] = 1
838 pm[n] = 1
813 nl.append(n)
839 nl.append(n)
814 continue
840 continue
815 for p in cl.parents(n):
841 for p in cl.parents(n):
816 if p in pm: # parent listed
842 if p in pm: # parent listed
817 pm[n] = 1
843 pm[n] = 1
818 nl.append(n)
844 nl.append(n)
819 break
845 break
820
846
821 return nl
847 return nl
822
848
823 def findincoming(self, remote):
849 def findincoming(self, remote):
824 m = self.changelog.nodemap
850 m = self.changelog.nodemap
825 search = []
851 search = []
826 fetch = []
852 fetch = []
827 seen = {}
853 seen = {}
828 seenbranch = {}
854 seenbranch = {}
829
855
830 # if we have an empty repo, fetch everything
856 # if we have an empty repo, fetch everything
831 if self.changelog.tip() == nullid:
857 if self.changelog.tip() == nullid:
832 self.ui.status("requesting all changes\n")
858 self.ui.status("requesting all changes\n")
833 return [nullid]
859 return [nullid]
834
860
835 # otherwise, assume we're closer to the tip than the root
861 # otherwise, assume we're closer to the tip than the root
836 self.ui.status("searching for changes\n")
862 self.ui.status("searching for changes\n")
837 heads = remote.heads()
863 heads = remote.heads()
838 unknown = []
864 unknown = []
839 for h in heads:
865 for h in heads:
840 if h not in m:
866 if h not in m:
841 unknown.append(h)
867 unknown.append(h)
842
868
843 if not unknown:
869 if not unknown:
844 return None
870 return None
845
871
846 rep = {}
872 rep = {}
847 reqcnt = 0
873 reqcnt = 0
848
874
849 unknown = remote.branches(unknown)
875 unknown = remote.branches(unknown)
850 while unknown:
876 while unknown:
851 r = []
877 r = []
852 while unknown:
878 while unknown:
853 n = unknown.pop(0)
879 n = unknown.pop(0)
854 if n[0] in seen:
880 if n[0] in seen:
855 continue
881 continue
856
882
857 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
883 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
858 if n[0] == nullid:
884 if n[0] == nullid:
859 break
885 break
860 if n in seenbranch:
886 if n in seenbranch:
861 self.ui.debug("branch already found\n")
887 self.ui.debug("branch already found\n")
862 continue
888 continue
863 if n[1] and n[1] in m: # do we know the base?
889 if n[1] and n[1] in m: # do we know the base?
864 self.ui.debug("found incomplete branch %s:%s\n"
890 self.ui.debug("found incomplete branch %s:%s\n"
865 % (short(n[0]), short(n[1])))
891 % (short(n[0]), short(n[1])))
866 search.append(n) # schedule branch range for scanning
892 search.append(n) # schedule branch range for scanning
867 seenbranch[n] = 1
893 seenbranch[n] = 1
868 else:
894 else:
869 if n[1] not in seen and n[1] not in fetch:
895 if n[1] not in seen and n[1] not in fetch:
870 if n[2] in m and n[3] in m:
896 if n[2] in m and n[3] in m:
871 self.ui.debug("found new changeset %s\n" %
897 self.ui.debug("found new changeset %s\n" %
872 short(n[1]))
898 short(n[1]))
873 fetch.append(n[1]) # earliest unknown
899 fetch.append(n[1]) # earliest unknown
874 continue
900 continue
875
901
876 for a in n[2:4]:
902 for a in n[2:4]:
877 if a not in rep:
903 if a not in rep:
878 r.append(a)
904 r.append(a)
879 rep[a] = 1
905 rep[a] = 1
880
906
881 seen[n[0]] = 1
907 seen[n[0]] = 1
882
908
883 if r:
909 if r:
884 reqcnt += 1
910 reqcnt += 1
885 self.ui.debug("request %d: %s\n" %
911 self.ui.debug("request %d: %s\n" %
886 (reqcnt, " ".join(map(short, r))))
912 (reqcnt, " ".join(map(short, r))))
887 for p in range(0, len(r), 10):
913 for p in range(0, len(r), 10):
888 for b in remote.branches(r[p:p+10]):
914 for b in remote.branches(r[p:p+10]):
889 self.ui.debug("received %s:%s\n" %
915 self.ui.debug("received %s:%s\n" %
890 (short(b[0]), short(b[1])))
916 (short(b[0]), short(b[1])))
891 if b[0] not in m and b[0] not in seen:
917 if b[0] not in m and b[0] not in seen:
892 unknown.append(b)
918 unknown.append(b)
893
919
894 while search:
920 while search:
895 n = search.pop(0)
921 n = search.pop(0)
896 reqcnt += 1
922 reqcnt += 1
897 l = remote.between([(n[0], n[1])])[0]
923 l = remote.between([(n[0], n[1])])[0]
898 l.append(n[1])
924 l.append(n[1])
899 p = n[0]
925 p = n[0]
900 f = 1
926 f = 1
901 for i in l:
927 for i in l:
902 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
928 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
903 if i in m:
929 if i in m:
904 if f <= 2:
930 if f <= 2:
905 self.ui.debug("found new branch changeset %s\n" %
931 self.ui.debug("found new branch changeset %s\n" %
906 short(p))
932 short(p))
907 fetch.append(p)
933 fetch.append(p)
908 else:
934 else:
909 self.ui.debug("narrowed branch search to %s:%s\n"
935 self.ui.debug("narrowed branch search to %s:%s\n"
910 % (short(p), short(i)))
936 % (short(p), short(i)))
911 search.append((p, i))
937 search.append((p, i))
912 break
938 break
913 p, f = i, f * 2
939 p, f = i, f * 2
914
940
915 for f in fetch:
941 for f in fetch:
916 if f in m:
942 if f in m:
917 raise RepoError("already have changeset " + short(f[:4]))
943 raise RepoError("already have changeset " + short(f[:4]))
918
944
919 if fetch == [nullid]:
945 if fetch == [nullid]:
920 self.ui.warn("warning: pulling from an unrelated repository!\n")
946 self.ui.warn("warning: pulling from an unrelated repository!\n")
921
947
922 self.ui.note("adding new changesets starting at " +
948 self.ui.note("adding new changesets starting at " +
923 " ".join([short(f) for f in fetch]) + "\n")
949 " ".join([short(f) for f in fetch]) + "\n")
924
950
925 self.ui.debug("%d total queries\n" % reqcnt)
951 self.ui.debug("%d total queries\n" % reqcnt)
926
952
927 return fetch
953 return fetch
928
954
929 def changegroup(self, basenodes):
955 def changegroup(self, basenodes):
930 nodes = self.newer(basenodes)
956 nodes = self.newer(basenodes)
931
957
932 # construct the link map
958 # construct the link map
933 linkmap = {}
959 linkmap = {}
934 for n in nodes:
960 for n in nodes:
935 linkmap[self.changelog.rev(n)] = n
961 linkmap[self.changelog.rev(n)] = n
936
962
937 # construct a list of all changed files
963 # construct a list of all changed files
938 changed = {}
964 changed = {}
939 for n in nodes:
965 for n in nodes:
940 c = self.changelog.read(n)
966 c = self.changelog.read(n)
941 for f in c[3]:
967 for f in c[3]:
942 changed[f] = 1
968 changed[f] = 1
943 changed = changed.keys()
969 changed = changed.keys()
944 changed.sort()
970 changed.sort()
945
971
946 # the changegroup is changesets + manifests + all file revs
972 # the changegroup is changesets + manifests + all file revs
947 revs = [ self.changelog.rev(n) for n in nodes ]
973 revs = [ self.changelog.rev(n) for n in nodes ]
948
974
949 for y in self.changelog.group(linkmap): yield y
975 for y in self.changelog.group(linkmap): yield y
950 for y in self.manifest.group(linkmap): yield y
976 for y in self.manifest.group(linkmap): yield y
951 for f in changed:
977 for f in changed:
952 yield struct.pack(">l", len(f) + 4) + f
978 yield struct.pack(">l", len(f) + 4) + f
953 g = self.file(f).group(linkmap)
979 g = self.file(f).group(linkmap)
954 for y in g:
980 for y in g:
955 yield y
981 yield y
956
982
957 def addchangegroup(self, generator):
983 def addchangegroup(self, generator):
958
984
959 class genread:
985 class genread:
960 def __init__(self, generator):
986 def __init__(self, generator):
961 self.g = generator
987 self.g = generator
962 self.buf = ""
988 self.buf = ""
963 def read(self, l):
989 def read(self, l):
964 while l > len(self.buf):
990 while l > len(self.buf):
965 try:
991 try:
966 self.buf += self.g.next()
992 self.buf += self.g.next()
967 except StopIteration:
993 except StopIteration:
968 break
994 break
969 d, self.buf = self.buf[:l], self.buf[l:]
995 d, self.buf = self.buf[:l], self.buf[l:]
970 return d
996 return d
971
997
972 def getchunk():
998 def getchunk():
973 d = source.read(4)
999 d = source.read(4)
974 if not d: return ""
1000 if not d: return ""
975 l = struct.unpack(">l", d)[0]
1001 l = struct.unpack(">l", d)[0]
976 if l <= 4: return ""
1002 if l <= 4: return ""
977 return source.read(l - 4)
1003 return source.read(l - 4)
978
1004
979 def getgroup():
1005 def getgroup():
980 while 1:
1006 while 1:
981 c = getchunk()
1007 c = getchunk()
982 if not c: break
1008 if not c: break
983 yield c
1009 yield c
984
1010
985 def csmap(x):
1011 def csmap(x):
986 self.ui.debug("add changeset %s\n" % short(x))
1012 self.ui.debug("add changeset %s\n" % short(x))
987 return self.changelog.count()
1013 return self.changelog.count()
988
1014
989 def revmap(x):
1015 def revmap(x):
990 return self.changelog.rev(x)
1016 return self.changelog.rev(x)
991
1017
992 if not generator: return
1018 if not generator: return
993 changesets = files = revisions = 0
1019 changesets = files = revisions = 0
994
1020
995 source = genread(generator)
1021 source = genread(generator)
996 lock = self.lock()
1022 lock = self.lock()
997 tr = self.transaction()
1023 tr = self.transaction()
998
1024
999 # pull off the changeset group
1025 # pull off the changeset group
1000 self.ui.status("adding changesets\n")
1026 self.ui.status("adding changesets\n")
1001 co = self.changelog.tip()
1027 co = self.changelog.tip()
1002 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1028 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1003 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1029 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1004
1030
1005 # pull off the manifest group
1031 # pull off the manifest group
1006 self.ui.status("adding manifests\n")
1032 self.ui.status("adding manifests\n")
1007 mm = self.manifest.tip()
1033 mm = self.manifest.tip()
1008 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1034 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1009
1035
1010 # process the files
1036 # process the files
1011 self.ui.status("adding file revisions\n")
1037 self.ui.status("adding file revisions\n")
1012 while 1:
1038 while 1:
1013 f = getchunk()
1039 f = getchunk()
1014 if not f: break
1040 if not f: break
1015 self.ui.debug("adding %s revisions\n" % f)
1041 self.ui.debug("adding %s revisions\n" % f)
1016 fl = self.file(f)
1042 fl = self.file(f)
1017 o = fl.count()
1043 o = fl.count()
1018 n = fl.addgroup(getgroup(), revmap, tr)
1044 n = fl.addgroup(getgroup(), revmap, tr)
1019 revisions += fl.count() - o
1045 revisions += fl.count() - o
1020 files += 1
1046 files += 1
1021
1047
1022 self.ui.status(("modified %d files, added %d changesets" +
1048 self.ui.status(("modified %d files, added %d changesets" +
1023 " and %d new revisions\n")
1049 " and %d new revisions\n")
1024 % (files, changesets, revisions))
1050 % (files, changesets, revisions))
1025
1051
1026 tr.close()
1052 tr.close()
1027 return
1053 return
1028
1054
1029 def update(self, node, allow=False, force=False):
1055 def update(self, node, allow=False, force=False):
1030 pl = self.dirstate.parents()
1056 pl = self.dirstate.parents()
1031 if not force and pl[1] != nullid:
1057 if not force and pl[1] != nullid:
1032 self.ui.warn("aborting: outstanding uncommitted merges\n")
1058 self.ui.warn("aborting: outstanding uncommitted merges\n")
1033 return
1059 return
1034
1060
1035 p1, p2 = pl[0], node
1061 p1, p2 = pl[0], node
1036 pa = self.changelog.ancestor(p1, p2)
1062 pa = self.changelog.ancestor(p1, p2)
1037 m1n = self.changelog.read(p1)[0]
1063 m1n = self.changelog.read(p1)[0]
1038 m2n = self.changelog.read(p2)[0]
1064 m2n = self.changelog.read(p2)[0]
1039 man = self.manifest.ancestor(m1n, m2n)
1065 man = self.manifest.ancestor(m1n, m2n)
1040 m1 = self.manifest.read(m1n)
1066 m1 = self.manifest.read(m1n)
1041 mf1 = self.manifest.readflags(m1n)
1067 mf1 = self.manifest.readflags(m1n)
1042 m2 = self.manifest.read(m2n)
1068 m2 = self.manifest.read(m2n)
1043 mf2 = self.manifest.readflags(m2n)
1069 mf2 = self.manifest.readflags(m2n)
1044 ma = self.manifest.read(man)
1070 ma = self.manifest.read(man)
1045 mfa = self.manifest.readflags(man)
1071 mfa = self.manifest.readflags(man)
1046
1072
1047 (c, a, d, u) = self.diffdir(self.root)
1073 (c, a, d, u) = self.changes(None, None)
1048
1074
1049 # is this a jump, or a merge? i.e. is there a linear path
1075 # is this a jump, or a merge? i.e. is there a linear path
1050 # from p1 to p2?
1076 # from p1 to p2?
1051 linear_path = (pa == p1 or pa == p2)
1077 linear_path = (pa == p1 or pa == p2)
1052
1078
1053 # resolve the manifest to determine which files
1079 # resolve the manifest to determine which files
1054 # we care about merging
1080 # we care about merging
1055 self.ui.note("resolving manifests\n")
1081 self.ui.note("resolving manifests\n")
1056 self.ui.debug(" ancestor %s local %s remote %s\n" %
1082 self.ui.debug(" ancestor %s local %s remote %s\n" %
1057 (short(man), short(m1n), short(m2n)))
1083 (short(man), short(m1n), short(m2n)))
1058
1084
1059 merge = {}
1085 merge = {}
1060 get = {}
1086 get = {}
1061 remove = []
1087 remove = []
1062 mark = {}
1088 mark = {}
1063
1089
1064 # construct a working dir manifest
1090 # construct a working dir manifest
1065 mw = m1.copy()
1091 mw = m1.copy()
1066 mfw = mf1.copy()
1092 mfw = mf1.copy()
1067 for f in a + c + u:
1093 for f in a + c + u:
1068 mw[f] = ""
1094 mw[f] = ""
1069 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1095 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1070 for f in d:
1096 for f in d:
1071 if f in mw: del mw[f]
1097 if f in mw: del mw[f]
1072
1098
1073 # If we're jumping between revisions (as opposed to merging),
1099 # If we're jumping between revisions (as opposed to merging),
1074 # and if neither the working directory nor the target rev has
1100 # and if neither the working directory nor the target rev has
1075 # the file, then we need to remove it from the dirstate, to
1101 # the file, then we need to remove it from the dirstate, to
1076 # prevent the dirstate from listing the file when it is no
1102 # prevent the dirstate from listing the file when it is no
1077 # longer in the manifest.
1103 # longer in the manifest.
1078 if linear_path and f not in m2:
1104 if linear_path and f not in m2:
1079 self.dirstate.forget((f,))
1105 self.dirstate.forget((f,))
1080
1106
1081 for f, n in mw.iteritems():
1107 for f, n in mw.iteritems():
1082 if f in m2:
1108 if f in m2:
1083 s = 0
1109 s = 0
1084
1110
1085 # is the wfile new since m1, and match m2?
1111 # is the wfile new since m1, and match m2?
1086 if f not in m1:
1112 if f not in m1:
1087 t1 = self.wfile(f).read()
1113 t1 = self.wfile(f).read()
1088 t2 = self.file(f).revision(m2[f])
1114 t2 = self.file(f).revision(m2[f])
1089 if cmp(t1, t2) == 0:
1115 if cmp(t1, t2) == 0:
1090 mark[f] = 1
1116 mark[f] = 1
1091 n = m2[f]
1117 n = m2[f]
1092 del t1, t2
1118 del t1, t2
1093
1119
1094 # are files different?
1120 # are files different?
1095 if n != m2[f]:
1121 if n != m2[f]:
1096 a = ma.get(f, nullid)
1122 a = ma.get(f, nullid)
1097 # are both different from the ancestor?
1123 # are both different from the ancestor?
1098 if n != a and m2[f] != a:
1124 if n != a and m2[f] != a:
1099 self.ui.debug(" %s versions differ, resolve\n" % f)
1125 self.ui.debug(" %s versions differ, resolve\n" % f)
1100 # merge executable bits
1126 # merge executable bits
1101 # "if we changed or they changed, change in merge"
1127 # "if we changed or they changed, change in merge"
1102 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1128 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1103 mode = ((a^b) | (a^c)) ^ a
1129 mode = ((a^b) | (a^c)) ^ a
1104 merge[f] = (m1.get(f, nullid), m2[f], mode)
1130 merge[f] = (m1.get(f, nullid), m2[f], mode)
1105 s = 1
1131 s = 1
1106 # are we clobbering?
1132 # are we clobbering?
1107 # is remote's version newer?
1133 # is remote's version newer?
1108 # or are we going back in time?
1134 # or are we going back in time?
1109 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1135 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1110 self.ui.debug(" remote %s is newer, get\n" % f)
1136 self.ui.debug(" remote %s is newer, get\n" % f)
1111 get[f] = m2[f]
1137 get[f] = m2[f]
1112 s = 1
1138 s = 1
1113 else:
1139 else:
1114 mark[f] = 1
1140 mark[f] = 1
1115
1141
1116 if not s and mfw[f] != mf2[f]:
1142 if not s and mfw[f] != mf2[f]:
1117 if force:
1143 if force:
1118 self.ui.debug(" updating permissions for %s\n" % f)
1144 self.ui.debug(" updating permissions for %s\n" % f)
1119 util.set_exec(self.wjoin(f), mf2[f])
1145 util.set_exec(self.wjoin(f), mf2[f])
1120 else:
1146 else:
1121 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1147 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1122 mode = ((a^b) | (a^c)) ^ a
1148 mode = ((a^b) | (a^c)) ^ a
1123 if mode != b:
1149 if mode != b:
1124 self.ui.debug(" updating permissions for %s\n" % f)
1150 self.ui.debug(" updating permissions for %s\n" % f)
1125 util.set_exec(self.wjoin(f), mode)
1151 util.set_exec(self.wjoin(f), mode)
1126 mark[f] = 1
1152 mark[f] = 1
1127 del m2[f]
1153 del m2[f]
1128 elif f in ma:
1154 elif f in ma:
1129 if not force and n != ma[f]:
1155 if not force and n != ma[f]:
1130 r = ""
1156 r = ""
1131 if linear_path or allow:
1157 if linear_path or allow:
1132 r = self.ui.prompt(
1158 r = self.ui.prompt(
1133 (" local changed %s which remote deleted\n" % f) +
1159 (" local changed %s which remote deleted\n" % f) +
1134 "(k)eep or (d)elete?", "[kd]", "k")
1160 "(k)eep or (d)elete?", "[kd]", "k")
1135 if r == "d":
1161 if r == "d":
1136 remove.append(f)
1162 remove.append(f)
1137 else:
1163 else:
1138 self.ui.debug("other deleted %s\n" % f)
1164 self.ui.debug("other deleted %s\n" % f)
1139 remove.append(f) # other deleted it
1165 remove.append(f) # other deleted it
1140 else:
1166 else:
1141 if n == m1.get(f, nullid): # same as parent
1167 if n == m1.get(f, nullid): # same as parent
1142 if p2 == pa: # going backwards?
1168 if p2 == pa: # going backwards?
1143 self.ui.debug("remote deleted %s\n" % f)
1169 self.ui.debug("remote deleted %s\n" % f)
1144 remove.append(f)
1170 remove.append(f)
1145 else:
1171 else:
1146 self.ui.debug("local created %s, keeping\n" % f)
1172 self.ui.debug("local created %s, keeping\n" % f)
1147 else:
1173 else:
1148 self.ui.debug("working dir created %s, keeping\n" % f)
1174 self.ui.debug("working dir created %s, keeping\n" % f)
1149
1175
1150 for f, n in m2.iteritems():
1176 for f, n in m2.iteritems():
1151 if f[0] == "/": continue
1177 if f[0] == "/": continue
1152 if not force and f in ma and n != ma[f]:
1178 if not force and f in ma and n != ma[f]:
1153 r = ""
1179 r = ""
1154 if linear_path or allow:
1180 if linear_path or allow:
1155 r = self.ui.prompt(
1181 r = self.ui.prompt(
1156 ("remote changed %s which local deleted\n" % f) +
1182 ("remote changed %s which local deleted\n" % f) +
1157 "(k)eep or (d)elete?", "[kd]", "k")
1183 "(k)eep or (d)elete?", "[kd]", "k")
1158 if r == "d": remove.append(f)
1184 if r == "d": remove.append(f)
1159 else:
1185 else:
1160 self.ui.debug("remote created %s\n" % f)
1186 self.ui.debug("remote created %s\n" % f)
1161 get[f] = n
1187 get[f] = n
1162
1188
1163 del mw, m1, m2, ma
1189 del mw, m1, m2, ma
1164
1190
1165 if force:
1191 if force:
1166 for f in merge:
1192 for f in merge:
1167 get[f] = merge[f][1]
1193 get[f] = merge[f][1]
1168 merge = {}
1194 merge = {}
1169
1195
1170 if linear_path:
1196 if linear_path:
1171 # we don't need to do any magic, just jump to the new rev
1197 # we don't need to do any magic, just jump to the new rev
1172 mode = 'n'
1198 mode = 'n'
1173 p1, p2 = p2, nullid
1199 p1, p2 = p2, nullid
1174 else:
1200 else:
1175 if not allow:
1201 if not allow:
1176 self.ui.status("this update spans a branch" +
1202 self.ui.status("this update spans a branch" +
1177 " affecting the following files:\n")
1203 " affecting the following files:\n")
1178 fl = merge.keys() + get.keys()
1204 fl = merge.keys() + get.keys()
1179 fl.sort()
1205 fl.sort()
1180 for f in fl:
1206 for f in fl:
1181 cf = ""
1207 cf = ""
1182 if f in merge: cf = " (resolve)"
1208 if f in merge: cf = " (resolve)"
1183 self.ui.status(" %s%s\n" % (f, cf))
1209 self.ui.status(" %s%s\n" % (f, cf))
1184 self.ui.warn("aborting update spanning branches!\n")
1210 self.ui.warn("aborting update spanning branches!\n")
1185 self.ui.status("(use update -m to perform a branch merge)\n")
1211 self.ui.status("(use update -m to perform a branch merge)\n")
1186 return 1
1212 return 1
1187 # we have to remember what files we needed to get/change
1213 # we have to remember what files we needed to get/change
1188 # because any file that's different from either one of its
1214 # because any file that's different from either one of its
1189 # parents must be in the changeset
1215 # parents must be in the changeset
1190 mode = 'm'
1216 mode = 'm'
1191 self.dirstate.update(mark.keys(), "m")
1217 self.dirstate.update(mark.keys(), "m")
1192
1218
1193 self.dirstate.setparents(p1, p2)
1219 self.dirstate.setparents(p1, p2)
1194
1220
1195 # get the files we don't need to change
1221 # get the files we don't need to change
1196 files = get.keys()
1222 files = get.keys()
1197 files.sort()
1223 files.sort()
1198 for f in files:
1224 for f in files:
1199 if f[0] == "/": continue
1225 if f[0] == "/": continue
1200 self.ui.note("getting %s\n" % f)
1226 self.ui.note("getting %s\n" % f)
1201 t = self.file(f).read(get[f])
1227 t = self.file(f).read(get[f])
1202 try:
1228 try:
1203 self.wfile(f, "w").write(t)
1229 self.wfile(f, "w").write(t)
1204 except IOError:
1230 except IOError:
1205 os.makedirs(os.path.dirname(self.wjoin(f)))
1231 os.makedirs(os.path.dirname(self.wjoin(f)))
1206 self.wfile(f, "w").write(t)
1232 self.wfile(f, "w").write(t)
1207 util.set_exec(self.wjoin(f), mf2[f])
1233 util.set_exec(self.wjoin(f), mf2[f])
1208 self.dirstate.update([f], mode)
1234 self.dirstate.update([f], mode)
1209
1235
1210 # merge the tricky bits
1236 # merge the tricky bits
1211 files = merge.keys()
1237 files = merge.keys()
1212 files.sort()
1238 files.sort()
1213 for f in files:
1239 for f in files:
1214 self.ui.status("merging %s\n" % f)
1240 self.ui.status("merging %s\n" % f)
1215 m, o, flag = merge[f]
1241 m, o, flag = merge[f]
1216 self.merge3(f, m, o)
1242 self.merge3(f, m, o)
1217 util.set_exec(self.wjoin(f), flag)
1243 util.set_exec(self.wjoin(f), flag)
1218 self.dirstate.update([f], 'm')
1244 self.dirstate.update([f], 'm')
1219
1245
1220 for f in remove:
1246 for f in remove:
1221 self.ui.note("removing %s\n" % f)
1247 self.ui.note("removing %s\n" % f)
1222 os.unlink(f)
1248 os.unlink(f)
1223 if mode == 'n':
1249 if mode == 'n':
1224 self.dirstate.forget(remove)
1250 self.dirstate.forget(remove)
1225 else:
1251 else:
1226 self.dirstate.update(remove, 'r')
1252 self.dirstate.update(remove, 'r')
1227
1253
1228 def merge3(self, fn, my, other):
1254 def merge3(self, fn, my, other):
1229 """perform a 3-way merge in the working directory"""
1255 """perform a 3-way merge in the working directory"""
1230
1256
1231 def temp(prefix, node):
1257 def temp(prefix, node):
1232 pre = "%s~%s." % (os.path.basename(fn), prefix)
1258 pre = "%s~%s." % (os.path.basename(fn), prefix)
1233 (fd, name) = tempfile.mkstemp("", pre)
1259 (fd, name) = tempfile.mkstemp("", pre)
1234 f = os.fdopen(fd, "wb")
1260 f = os.fdopen(fd, "wb")
1235 f.write(fl.revision(node))
1261 f.write(fl.revision(node))
1236 f.close()
1262 f.close()
1237 return name
1263 return name
1238
1264
1239 fl = self.file(fn)
1265 fl = self.file(fn)
1240 base = fl.ancestor(my, other)
1266 base = fl.ancestor(my, other)
1241 a = self.wjoin(fn)
1267 a = self.wjoin(fn)
1242 b = temp("base", base)
1268 b = temp("base", base)
1243 c = temp("other", other)
1269 c = temp("other", other)
1244
1270
1245 self.ui.note("resolving %s\n" % fn)
1271 self.ui.note("resolving %s\n" % fn)
1246 self.ui.debug("file %s: other %s ancestor %s\n" %
1272 self.ui.debug("file %s: other %s ancestor %s\n" %
1247 (fn, short(other), short(base)))
1273 (fn, short(other), short(base)))
1248
1274
1249 cmd = os.environ.get("HGMERGE", "hgmerge")
1275 cmd = os.environ.get("HGMERGE", "hgmerge")
1250 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1276 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1251 if r:
1277 if r:
1252 self.ui.warn("merging %s failed!\n" % fn)
1278 self.ui.warn("merging %s failed!\n" % fn)
1253
1279
1254 os.unlink(b)
1280 os.unlink(b)
1255 os.unlink(c)
1281 os.unlink(c)
1256
1282
1257 def verify(self):
1283 def verify(self):
1258 filelinkrevs = {}
1284 filelinkrevs = {}
1259 filenodes = {}
1285 filenodes = {}
1260 changesets = revisions = files = 0
1286 changesets = revisions = files = 0
1261 errors = 0
1287 errors = 0
1262
1288
1263 seen = {}
1289 seen = {}
1264 self.ui.status("checking changesets\n")
1290 self.ui.status("checking changesets\n")
1265 for i in range(self.changelog.count()):
1291 for i in range(self.changelog.count()):
1266 changesets += 1
1292 changesets += 1
1267 n = self.changelog.node(i)
1293 n = self.changelog.node(i)
1268 if n in seen:
1294 if n in seen:
1269 self.ui.warn("duplicate changeset at revision %d\n" % i)
1295 self.ui.warn("duplicate changeset at revision %d\n" % i)
1270 errors += 1
1296 errors += 1
1271 seen[n] = 1
1297 seen[n] = 1
1272
1298
1273 for p in self.changelog.parents(n):
1299 for p in self.changelog.parents(n):
1274 if p not in self.changelog.nodemap:
1300 if p not in self.changelog.nodemap:
1275 self.ui.warn("changeset %s has unknown parent %s\n" %
1301 self.ui.warn("changeset %s has unknown parent %s\n" %
1276 (short(n), short(p)))
1302 (short(n), short(p)))
1277 errors += 1
1303 errors += 1
1278 try:
1304 try:
1279 changes = self.changelog.read(n)
1305 changes = self.changelog.read(n)
1280 except Exception, inst:
1306 except Exception, inst:
1281 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1307 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1282 errors += 1
1308 errors += 1
1283
1309
1284 for f in changes[3]:
1310 for f in changes[3]:
1285 filelinkrevs.setdefault(f, []).append(i)
1311 filelinkrevs.setdefault(f, []).append(i)
1286
1312
1287 seen = {}
1313 seen = {}
1288 self.ui.status("checking manifests\n")
1314 self.ui.status("checking manifests\n")
1289 for i in range(self.manifest.count()):
1315 for i in range(self.manifest.count()):
1290 n = self.manifest.node(i)
1316 n = self.manifest.node(i)
1291 if n in seen:
1317 if n in seen:
1292 self.ui.warn("duplicate manifest at revision %d\n" % i)
1318 self.ui.warn("duplicate manifest at revision %d\n" % i)
1293 errors += 1
1319 errors += 1
1294 seen[n] = 1
1320 seen[n] = 1
1295
1321
1296 for p in self.manifest.parents(n):
1322 for p in self.manifest.parents(n):
1297 if p not in self.manifest.nodemap:
1323 if p not in self.manifest.nodemap:
1298 self.ui.warn("manifest %s has unknown parent %s\n" %
1324 self.ui.warn("manifest %s has unknown parent %s\n" %
1299 (short(n), short(p)))
1325 (short(n), short(p)))
1300 errors += 1
1326 errors += 1
1301
1327
1302 try:
1328 try:
1303 delta = mdiff.patchtext(self.manifest.delta(n))
1329 delta = mdiff.patchtext(self.manifest.delta(n))
1304 except KeyboardInterrupt:
1330 except KeyboardInterrupt:
1305 print "aborted"
1331 print "aborted"
1306 sys.exit(0)
1332 sys.exit(0)
1307 except Exception, inst:
1333 except Exception, inst:
1308 self.ui.warn("unpacking manifest %s: %s\n"
1334 self.ui.warn("unpacking manifest %s: %s\n"
1309 % (short(n), inst))
1335 % (short(n), inst))
1310 errors += 1
1336 errors += 1
1311
1337
1312 ff = [ l.split('\0') for l in delta.splitlines() ]
1338 ff = [ l.split('\0') for l in delta.splitlines() ]
1313 for f, fn in ff:
1339 for f, fn in ff:
1314 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1340 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1315
1341
1316 self.ui.status("crosschecking files in changesets and manifests\n")
1342 self.ui.status("crosschecking files in changesets and manifests\n")
1317 for f in filenodes:
1343 for f in filenodes:
1318 if f not in filelinkrevs:
1344 if f not in filelinkrevs:
1319 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1345 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1320 errors += 1
1346 errors += 1
1321
1347
1322 for f in filelinkrevs:
1348 for f in filelinkrevs:
1323 if f not in filenodes:
1349 if f not in filenodes:
1324 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1350 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1325 errors += 1
1351 errors += 1
1326
1352
1327 self.ui.status("checking files\n")
1353 self.ui.status("checking files\n")
1328 ff = filenodes.keys()
1354 ff = filenodes.keys()
1329 ff.sort()
1355 ff.sort()
1330 for f in ff:
1356 for f in ff:
1331 if f == "/dev/null": continue
1357 if f == "/dev/null": continue
1332 files += 1
1358 files += 1
1333 fl = self.file(f)
1359 fl = self.file(f)
1334 nodes = { nullid: 1 }
1360 nodes = { nullid: 1 }
1335 seen = {}
1361 seen = {}
1336 for i in range(fl.count()):
1362 for i in range(fl.count()):
1337 revisions += 1
1363 revisions += 1
1338 n = fl.node(i)
1364 n = fl.node(i)
1339
1365
1340 if n in seen:
1366 if n in seen:
1341 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1367 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1342 errors += 1
1368 errors += 1
1343
1369
1344 if n not in filenodes[f]:
1370 if n not in filenodes[f]:
1345 self.ui.warn("%s: %d:%s not in manifests\n"
1371 self.ui.warn("%s: %d:%s not in manifests\n"
1346 % (f, i, short(n)))
1372 % (f, i, short(n)))
1347 print len(filenodes[f].keys()), fl.count(), f
1373 print len(filenodes[f].keys()), fl.count(), f
1348 errors += 1
1374 errors += 1
1349 else:
1375 else:
1350 del filenodes[f][n]
1376 del filenodes[f][n]
1351
1377
1352 flr = fl.linkrev(n)
1378 flr = fl.linkrev(n)
1353 if flr not in filelinkrevs[f]:
1379 if flr not in filelinkrevs[f]:
1354 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1380 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1355 % (f, short(n), fl.linkrev(n)))
1381 % (f, short(n), fl.linkrev(n)))
1356 errors += 1
1382 errors += 1
1357 else:
1383 else:
1358 filelinkrevs[f].remove(flr)
1384 filelinkrevs[f].remove(flr)
1359
1385
1360 # verify contents
1386 # verify contents
1361 try:
1387 try:
1362 t = fl.read(n)
1388 t = fl.read(n)
1363 except Exception, inst:
1389 except Exception, inst:
1364 self.ui.warn("unpacking file %s %s: %s\n"
1390 self.ui.warn("unpacking file %s %s: %s\n"
1365 % (f, short(n), inst))
1391 % (f, short(n), inst))
1366 errors += 1
1392 errors += 1
1367
1393
1368 # verify parents
1394 # verify parents
1369 (p1, p2) = fl.parents(n)
1395 (p1, p2) = fl.parents(n)
1370 if p1 not in nodes:
1396 if p1 not in nodes:
1371 self.ui.warn("file %s:%s unknown parent 1 %s" %
1397 self.ui.warn("file %s:%s unknown parent 1 %s" %
1372 (f, short(n), short(p1)))
1398 (f, short(n), short(p1)))
1373 errors += 1
1399 errors += 1
1374 if p2 not in nodes:
1400 if p2 not in nodes:
1375 self.ui.warn("file %s:%s unknown parent 2 %s" %
1401 self.ui.warn("file %s:%s unknown parent 2 %s" %
1376 (f, short(n), short(p1)))
1402 (f, short(n), short(p1)))
1377 errors += 1
1403 errors += 1
1378 nodes[n] = 1
1404 nodes[n] = 1
1379
1405
1380 # cross-check
1406 # cross-check
1381 for node in filenodes[f]:
1407 for node in filenodes[f]:
1382 self.ui.warn("node %s in manifests not in %s\n"
1408 self.ui.warn("node %s in manifests not in %s\n"
1383 % (hex(n), f))
1409 % (hex(n), f))
1384 errors += 1
1410 errors += 1
1385
1411
1386 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1412 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1387 (files, changesets, revisions))
1413 (files, changesets, revisions))
1388
1414
1389 if errors:
1415 if errors:
1390 self.ui.warn("%d integrity errors encountered!\n" % errors)
1416 self.ui.warn("%d integrity errors encountered!\n" % errors)
1391 return 1
1417 return 1
1392
1418
1393 class remoterepository:
1419 class remoterepository:
1394 def __init__(self, ui, path):
1420 def __init__(self, ui, path):
1395 self.url = path
1421 self.url = path
1396 self.ui = ui
1422 self.ui = ui
1397 no_list = [ "localhost", "127.0.0.1" ]
1423 no_list = [ "localhost", "127.0.0.1" ]
1398 host = ui.config("http_proxy", "host")
1424 host = ui.config("http_proxy", "host")
1399 if host is None:
1425 if host is None:
1400 host = os.environ.get("http_proxy")
1426 host = os.environ.get("http_proxy")
1401 if host and host.startswith('http://'):
1427 if host and host.startswith('http://'):
1402 host = host[7:]
1428 host = host[7:]
1403 user = ui.config("http_proxy", "user")
1429 user = ui.config("http_proxy", "user")
1404 passwd = ui.config("http_proxy", "passwd")
1430 passwd = ui.config("http_proxy", "passwd")
1405 no = ui.config("http_proxy", "no")
1431 no = ui.config("http_proxy", "no")
1406 if no is None:
1432 if no is None:
1407 no = os.environ.get("no_proxy")
1433 no = os.environ.get("no_proxy")
1408 if no:
1434 if no:
1409 no_list = no_list + no.split(",")
1435 no_list = no_list + no.split(",")
1410
1436
1411 no_proxy = 0
1437 no_proxy = 0
1412 for h in no_list:
1438 for h in no_list:
1413 if (path.startswith("http://" + h + "/") or
1439 if (path.startswith("http://" + h + "/") or
1414 path.startswith("http://" + h + ":") or
1440 path.startswith("http://" + h + ":") or
1415 path == "http://" + h):
1441 path == "http://" + h):
1416 no_proxy = 1
1442 no_proxy = 1
1417
1443
1418 # Note: urllib2 takes proxy values from the environment and those will
1444 # Note: urllib2 takes proxy values from the environment and those will
1419 # take precedence
1445 # take precedence
1420 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1446 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1421 if os.environ.has_key(env):
1447 if os.environ.has_key(env):
1422 del os.environ[env]
1448 del os.environ[env]
1423
1449
1424 proxy_handler = urllib2.BaseHandler()
1450 proxy_handler = urllib2.BaseHandler()
1425 if host and not no_proxy:
1451 if host and not no_proxy:
1426 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1452 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1427
1453
1428 authinfo = None
1454 authinfo = None
1429 if user and passwd:
1455 if user and passwd:
1430 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1456 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1431 passmgr.add_password(None, host, user, passwd)
1457 passmgr.add_password(None, host, user, passwd)
1432 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1458 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1433
1459
1434 opener = urllib2.build_opener(proxy_handler, authinfo)
1460 opener = urllib2.build_opener(proxy_handler, authinfo)
1435 urllib2.install_opener(opener)
1461 urllib2.install_opener(opener)
1436
1462
1437 def do_cmd(self, cmd, **args):
1463 def do_cmd(self, cmd, **args):
1438 self.ui.debug("sending %s command\n" % cmd)
1464 self.ui.debug("sending %s command\n" % cmd)
1439 q = {"cmd": cmd}
1465 q = {"cmd": cmd}
1440 q.update(args)
1466 q.update(args)
1441 qs = urllib.urlencode(q)
1467 qs = urllib.urlencode(q)
1442 cu = "%s?%s" % (self.url, qs)
1468 cu = "%s?%s" % (self.url, qs)
1443 return urllib2.urlopen(cu)
1469 return urllib2.urlopen(cu)
1444
1470
1445 def heads(self):
1471 def heads(self):
1446 d = self.do_cmd("heads").read()
1472 d = self.do_cmd("heads").read()
1447 try:
1473 try:
1448 return map(bin, d[:-1].split(" "))
1474 return map(bin, d[:-1].split(" "))
1449 except:
1475 except:
1450 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1476 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1451 raise
1477 raise
1452
1478
1453 def branches(self, nodes):
1479 def branches(self, nodes):
1454 n = " ".join(map(hex, nodes))
1480 n = " ".join(map(hex, nodes))
1455 d = self.do_cmd("branches", nodes=n).read()
1481 d = self.do_cmd("branches", nodes=n).read()
1456 try:
1482 try:
1457 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1483 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1458 return br
1484 return br
1459 except:
1485 except:
1460 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1486 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1461 raise
1487 raise
1462
1488
1463 def between(self, pairs):
1489 def between(self, pairs):
1464 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1490 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1465 d = self.do_cmd("between", pairs=n).read()
1491 d = self.do_cmd("between", pairs=n).read()
1466 try:
1492 try:
1467 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1493 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1468 return p
1494 return p
1469 except:
1495 except:
1470 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1496 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1471 raise
1497 raise
1472
1498
1473 def changegroup(self, nodes):
1499 def changegroup(self, nodes):
1474 n = " ".join(map(hex, nodes))
1500 n = " ".join(map(hex, nodes))
1475 zd = zlib.decompressobj()
1501 zd = zlib.decompressobj()
1476 f = self.do_cmd("changegroup", roots=n)
1502 f = self.do_cmd("changegroup", roots=n)
1477 bytes = 0
1503 bytes = 0
1478 while 1:
1504 while 1:
1479 d = f.read(4096)
1505 d = f.read(4096)
1480 bytes += len(d)
1506 bytes += len(d)
1481 if not d:
1507 if not d:
1482 yield zd.flush()
1508 yield zd.flush()
1483 break
1509 break
1484 yield zd.decompress(d)
1510 yield zd.decompress(d)
1485 self.ui.note("%d bytes of data transfered\n" % bytes)
1511 self.ui.note("%d bytes of data transfered\n" % bytes)
1486
1512
1487 def repository(ui, path=None, create=0):
1513 def repository(ui, path=None, create=0):
1488 if path and path[:7] == "http://":
1514 if path and path[:7] == "http://":
1489 return remoterepository(ui, path)
1515 return remoterepository(ui, path)
1490 if path and path[:5] == "hg://":
1516 if path and path[:5] == "hg://":
1491 return remoterepository(ui, path.replace("hg://", "http://"))
1517 return remoterepository(ui, path.replace("hg://", "http://"))
1492 if path and path[:11] == "old-http://":
1518 if path and path[:11] == "old-http://":
1493 return localrepository(ui, path.replace("old-http://", "http://"))
1519 return localrepository(ui, path.replace("old-http://", "http://"))
1494 else:
1520 else:
1495 return localrepository(ui, path, create)
1521 return localrepository(ui, path, create)
1496
1522
@@ -1,722 +1,722 b''
1 # hgweb.py - web interface to a mercurial repository
1 # hgweb.py - web interface to a mercurial repository
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os, cgi, time, re, difflib, sys, zlib
9 import os, cgi, time, re, difflib, sys, zlib
10 from mercurial.hg import *
10 from mercurial.hg import *
11 from mercurial.ui import *
11 from mercurial.ui import *
12
12
13 def templatepath():
13 def templatepath():
14 for f in "templates", "../templates":
14 for f in "templates", "../templates":
15 p = os.path.join(os.path.dirname(__file__), f)
15 p = os.path.join(os.path.dirname(__file__), f)
16 if os.path.isdir(p): return p
16 if os.path.isdir(p): return p
17
17
18 def age(t):
18 def age(t):
19 def plural(t, c):
19 def plural(t, c):
20 if c == 1: return t
20 if c == 1: return t
21 return t + "s"
21 return t + "s"
22 def fmt(t, c):
22 def fmt(t, c):
23 return "%d %s" % (c, plural(t, c))
23 return "%d %s" % (c, plural(t, c))
24
24
25 now = time.time()
25 now = time.time()
26 delta = max(1, int(now - t))
26 delta = max(1, int(now - t))
27
27
28 scales = [["second", 1],
28 scales = [["second", 1],
29 ["minute", 60],
29 ["minute", 60],
30 ["hour", 3600],
30 ["hour", 3600],
31 ["day", 3600 * 24],
31 ["day", 3600 * 24],
32 ["week", 3600 * 24 * 7],
32 ["week", 3600 * 24 * 7],
33 ["month", 3600 * 24 * 30],
33 ["month", 3600 * 24 * 30],
34 ["year", 3600 * 24 * 365]]
34 ["year", 3600 * 24 * 365]]
35
35
36 scales.reverse()
36 scales.reverse()
37
37
38 for t, s in scales:
38 for t, s in scales:
39 n = delta / s
39 n = delta / s
40 if n >= 2 or s == 1: return fmt(t, n)
40 if n >= 2 or s == 1: return fmt(t, n)
41
41
42 def nl2br(text):
42 def nl2br(text):
43 return text.replace('\n', '<br/>\n')
43 return text.replace('\n', '<br/>\n')
44
44
45 def obfuscate(text):
45 def obfuscate(text):
46 return ''.join([ '&#%d;' % ord(c) for c in text ])
46 return ''.join([ '&#%d;' % ord(c) for c in text ])
47
47
48 def up(p):
48 def up(p):
49 if p[0] != "/": p = "/" + p
49 if p[0] != "/": p = "/" + p
50 if p[-1] == "/": p = p[:-1]
50 if p[-1] == "/": p = p[:-1]
51 up = os.path.dirname(p)
51 up = os.path.dirname(p)
52 if up == "/":
52 if up == "/":
53 return "/"
53 return "/"
54 return up + "/"
54 return up + "/"
55
55
56 def httphdr(type):
56 def httphdr(type):
57 print 'Content-type: %s\n' % type
57 print 'Content-type: %s\n' % type
58
58
59 def write(*things):
59 def write(*things):
60 for thing in things:
60 for thing in things:
61 if hasattr(thing, "__iter__"):
61 if hasattr(thing, "__iter__"):
62 for part in thing:
62 for part in thing:
63 write(part)
63 write(part)
64 else:
64 else:
65 sys.stdout.write(str(thing))
65 sys.stdout.write(str(thing))
66
66
67 def template(tmpl, filters = {}, **map):
67 def template(tmpl, filters = {}, **map):
68 while tmpl:
68 while tmpl:
69 m = re.search(r"#([a-zA-Z0-9]+)((\|[a-zA-Z0-9]+)*)#", tmpl)
69 m = re.search(r"#([a-zA-Z0-9]+)((\|[a-zA-Z0-9]+)*)#", tmpl)
70 if m:
70 if m:
71 yield tmpl[:m.start(0)]
71 yield tmpl[:m.start(0)]
72 v = map.get(m.group(1), "")
72 v = map.get(m.group(1), "")
73 v = callable(v) and v() or v
73 v = callable(v) and v() or v
74
74
75 fl = m.group(2)
75 fl = m.group(2)
76 if fl:
76 if fl:
77 for f in fl.split("|")[1:]:
77 for f in fl.split("|")[1:]:
78 v = filters[f](v)
78 v = filters[f](v)
79
79
80 yield v
80 yield v
81 tmpl = tmpl[m.end(0):]
81 tmpl = tmpl[m.end(0):]
82 else:
82 else:
83 yield tmpl
83 yield tmpl
84 return
84 return
85
85
86 class templater:
86 class templater:
87 def __init__(self, mapfile, filters = {}):
87 def __init__(self, mapfile, filters = {}):
88 self.cache = {}
88 self.cache = {}
89 self.map = {}
89 self.map = {}
90 self.base = os.path.dirname(mapfile)
90 self.base = os.path.dirname(mapfile)
91 self.filters = filters
91 self.filters = filters
92
92
93 for l in file(mapfile):
93 for l in file(mapfile):
94 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
94 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
95 if m:
95 if m:
96 self.cache[m.group(1)] = m.group(2)
96 self.cache[m.group(1)] = m.group(2)
97 else:
97 else:
98 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
98 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
99 if m:
99 if m:
100 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
100 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
101 else:
101 else:
102 raise "unknown map entry '%s'" % l
102 raise "unknown map entry '%s'" % l
103
103
104 def __call__(self, t, **map):
104 def __call__(self, t, **map):
105 try:
105 try:
106 tmpl = self.cache[t]
106 tmpl = self.cache[t]
107 except KeyError:
107 except KeyError:
108 tmpl = self.cache[t] = file(self.map[t]).read()
108 tmpl = self.cache[t] = file(self.map[t]).read()
109 return template(tmpl, self.filters, **map)
109 return template(tmpl, self.filters, **map)
110
110
111 class hgweb:
111 class hgweb:
112 maxchanges = 10
112 maxchanges = 10
113 maxfiles = 10
113 maxfiles = 10
114
114
115 def __init__(self, path, name, templates = ""):
115 def __init__(self, path, name, templates = ""):
116 self.templates = templates or templatepath()
116 self.templates = templates or templatepath()
117 self.reponame = name
117 self.reponame = name
118 self.path = path
118 self.path = path
119 self.mtime = -1
119 self.mtime = -1
120 self.viewonly = 0
120 self.viewonly = 0
121
121
122 self.filters = {
122 self.filters = {
123 "escape": cgi.escape,
123 "escape": cgi.escape,
124 "age": age,
124 "age": age,
125 "date": (lambda x: time.asctime(time.gmtime(x))),
125 "date": (lambda x: time.asctime(time.gmtime(x))),
126 "addbreaks": nl2br,
126 "addbreaks": nl2br,
127 "obfuscate": obfuscate,
127 "obfuscate": obfuscate,
128 "short": (lambda x: x[:12]),
128 "short": (lambda x: x[:12]),
129 "firstline": (lambda x: x.splitlines(1)[0]),
129 "firstline": (lambda x: x.splitlines(1)[0]),
130 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--")
130 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--")
131 }
131 }
132
132
133 def refresh(self):
133 def refresh(self):
134 s = os.stat(os.path.join(self.path, ".hg", "00changelog.i"))
134 s = os.stat(os.path.join(self.path, ".hg", "00changelog.i"))
135 if s.st_mtime != self.mtime:
135 if s.st_mtime != self.mtime:
136 self.mtime = s.st_mtime
136 self.mtime = s.st_mtime
137 self.repo = repository(ui(), self.path)
137 self.repo = repository(ui(), self.path)
138
138
139 def date(self, cs):
139 def date(self, cs):
140 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
140 return time.asctime(time.gmtime(float(cs[2].split(' ')[0])))
141
141
142 def listfiles(self, files, mf):
142 def listfiles(self, files, mf):
143 for f in files[:self.maxfiles]:
143 for f in files[:self.maxfiles]:
144 yield self.t("filenodelink", node = hex(mf[f]), file = f)
144 yield self.t("filenodelink", node = hex(mf[f]), file = f)
145 if len(files) > self.maxfiles:
145 if len(files) > self.maxfiles:
146 yield self.t("fileellipses")
146 yield self.t("fileellipses")
147
147
148 def listfilediffs(self, files, changeset):
148 def listfilediffs(self, files, changeset):
149 for f in files[:self.maxfiles]:
149 for f in files[:self.maxfiles]:
150 yield self.t("filedifflink", node = hex(changeset), file = f)
150 yield self.t("filedifflink", node = hex(changeset), file = f)
151 if len(files) > self.maxfiles:
151 if len(files) > self.maxfiles:
152 yield self.t("fileellipses")
152 yield self.t("fileellipses")
153
153
154 def parent(self, t1, node=nullid, rev=-1, **args):
154 def parent(self, t1, node=nullid, rev=-1, **args):
155 if node != hex(nullid):
155 if node != hex(nullid):
156 yield self.t(t1, node = node, rev = rev, **args)
156 yield self.t(t1, node = node, rev = rev, **args)
157
157
158 def diff(self, node1, node2, files):
158 def diff(self, node1, node2, files):
159 def filterfiles(list, files):
159 def filterfiles(list, files):
160 l = [ x for x in list if x in files ]
160 l = [ x for x in list if x in files ]
161
161
162 for f in files:
162 for f in files:
163 if f[-1] != os.sep: f += os.sep
163 if f[-1] != os.sep: f += os.sep
164 l += [ x for x in list if x.startswith(f) ]
164 l += [ x for x in list if x.startswith(f) ]
165 return l
165 return l
166
166
167 parity = [0]
167 parity = [0]
168 def diffblock(diff, f, fn):
168 def diffblock(diff, f, fn):
169 yield self.t("diffblock",
169 yield self.t("diffblock",
170 lines = prettyprintlines(diff),
170 lines = prettyprintlines(diff),
171 parity = parity[0],
171 parity = parity[0],
172 file = f,
172 file = f,
173 filenode = hex(fn or nullid))
173 filenode = hex(fn or nullid))
174 parity[0] = 1 - parity[0]
174 parity[0] = 1 - parity[0]
175
175
176 def prettyprintlines(diff):
176 def prettyprintlines(diff):
177 for l in diff.splitlines(1):
177 for l in diff.splitlines(1):
178 if l.startswith('+'):
178 if l.startswith('+'):
179 yield self.t("difflineplus", line = l)
179 yield self.t("difflineplus", line = l)
180 elif l.startswith('-'):
180 elif l.startswith('-'):
181 yield self.t("difflineminus", line = l)
181 yield self.t("difflineminus", line = l)
182 elif l.startswith('@'):
182 elif l.startswith('@'):
183 yield self.t("difflineat", line = l)
183 yield self.t("difflineat", line = l)
184 else:
184 else:
185 yield self.t("diffline", line = l)
185 yield self.t("diffline", line = l)
186
186
187 r = self.repo
187 r = self.repo
188 cl = r.changelog
188 cl = r.changelog
189 mf = r.manifest
189 mf = r.manifest
190 change1 = cl.read(node1)
190 change1 = cl.read(node1)
191 change2 = cl.read(node2)
191 change2 = cl.read(node2)
192 mmap1 = mf.read(change1[0])
192 mmap1 = mf.read(change1[0])
193 mmap2 = mf.read(change2[0])
193 mmap2 = mf.read(change2[0])
194 date1 = self.date(change1)
194 date1 = self.date(change1)
195 date2 = self.date(change2)
195 date2 = self.date(change2)
196
196
197 c, a, d = r.diffrevs(node1, node2)
197 c, a, d = r.changes(node1, node2)
198 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
198 c, a, d = map(lambda x: filterfiles(x, files), (c, a, d))
199
199
200 for f in c:
200 for f in c:
201 to = r.file(f).read(mmap1[f])
201 to = r.file(f).read(mmap1[f])
202 tn = r.file(f).read(mmap2[f])
202 tn = r.file(f).read(mmap2[f])
203 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
203 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
204 for f in a:
204 for f in a:
205 to = None
205 to = None
206 tn = r.file(f).read(mmap2[f])
206 tn = r.file(f).read(mmap2[f])
207 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
207 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
208 for f in d:
208 for f in d:
209 to = r.file(f).read(mmap1[f])
209 to = r.file(f).read(mmap1[f])
210 tn = None
210 tn = None
211 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
211 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f), f, tn)
212
212
213 def header(self):
213 def header(self):
214 yield self.t("header", repo = self.reponame)
214 yield self.t("header", repo = self.reponame)
215
215
216 def footer(self):
216 def footer(self):
217 yield self.t("footer", repo = self.reponame)
217 yield self.t("footer", repo = self.reponame)
218
218
219 def changelog(self, pos):
219 def changelog(self, pos):
220 def changenav():
220 def changenav():
221 def seq(factor = 1):
221 def seq(factor = 1):
222 yield 1 * factor
222 yield 1 * factor
223 yield 3 * factor
223 yield 3 * factor
224 #yield 5 * factor
224 #yield 5 * factor
225 for f in seq(factor * 10):
225 for f in seq(factor * 10):
226 yield f
226 yield f
227
227
228 l = []
228 l = []
229 for f in seq():
229 for f in seq():
230 if f < self.maxchanges / 2: continue
230 if f < self.maxchanges / 2: continue
231 if f > count: break
231 if f > count: break
232 r = "%d" % f
232 r = "%d" % f
233 if pos + f < count: l.append(("+" + r, pos + f))
233 if pos + f < count: l.append(("+" + r, pos + f))
234 if pos - f >= 0: l.insert(0, ("-" + r, pos - f))
234 if pos - f >= 0: l.insert(0, ("-" + r, pos - f))
235
235
236 yield self.t("naventry", rev = 0, label="(0)")
236 yield self.t("naventry", rev = 0, label="(0)")
237
237
238 for label, rev in l:
238 for label, rev in l:
239 yield self.t("naventry", label = label, rev = rev)
239 yield self.t("naventry", label = label, rev = rev)
240
240
241 yield self.t("naventry", label="tip")
241 yield self.t("naventry", label="tip")
242
242
243 def changelist():
243 def changelist():
244 parity = (start - end) & 1
244 parity = (start - end) & 1
245 cl = self.repo.changelog
245 cl = self.repo.changelog
246 l = [] # build a list in forward order for efficiency
246 l = [] # build a list in forward order for efficiency
247 for i in range(start, end):
247 for i in range(start, end):
248 n = cl.node(i)
248 n = cl.node(i)
249 changes = cl.read(n)
249 changes = cl.read(n)
250 hn = hex(n)
250 hn = hex(n)
251 p1, p2 = cl.parents(n)
251 p1, p2 = cl.parents(n)
252 t = float(changes[2].split(' ')[0])
252 t = float(changes[2].split(' ')[0])
253
253
254 l.insert(0, self.t(
254 l.insert(0, self.t(
255 'changelogentry',
255 'changelogentry',
256 parity = parity,
256 parity = parity,
257 author = changes[1],
257 author = changes[1],
258 parent1 = self.parent("changelogparent",
258 parent1 = self.parent("changelogparent",
259 hex(p1), cl.rev(p1)),
259 hex(p1), cl.rev(p1)),
260 parent2 = self.parent("changelogparent",
260 parent2 = self.parent("changelogparent",
261 hex(p2), cl.rev(p2)),
261 hex(p2), cl.rev(p2)),
262 p1 = hex(p1), p2 = hex(p2),
262 p1 = hex(p1), p2 = hex(p2),
263 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
263 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
264 manifest = hex(changes[0]),
264 manifest = hex(changes[0]),
265 desc = changes[4],
265 desc = changes[4],
266 date = t,
266 date = t,
267 files = self.listfilediffs(changes[3], n),
267 files = self.listfilediffs(changes[3], n),
268 rev = i,
268 rev = i,
269 node = hn))
269 node = hn))
270 parity = 1 - parity
270 parity = 1 - parity
271
271
272 yield l
272 yield l
273
273
274 cl = self.repo.changelog
274 cl = self.repo.changelog
275 mf = cl.read(cl.tip())[0]
275 mf = cl.read(cl.tip())[0]
276 count = cl.count()
276 count = cl.count()
277 start = max(0, pos - self.maxchanges + 1)
277 start = max(0, pos - self.maxchanges + 1)
278 end = min(count, start + self.maxchanges)
278 end = min(count, start + self.maxchanges)
279 pos = end - 1
279 pos = end - 1
280
280
281 yield self.t('changelog',
281 yield self.t('changelog',
282 header = self.header(),
282 header = self.header(),
283 footer = self.footer(),
283 footer = self.footer(),
284 repo = self.reponame,
284 repo = self.reponame,
285 changenav = changenav,
285 changenav = changenav,
286 manifest = hex(mf),
286 manifest = hex(mf),
287 rev = pos, changesets = count, entries = changelist)
287 rev = pos, changesets = count, entries = changelist)
288
288
289 def changeset(self, nodeid):
289 def changeset(self, nodeid):
290 n = bin(nodeid)
290 n = bin(nodeid)
291 cl = self.repo.changelog
291 cl = self.repo.changelog
292 changes = cl.read(n)
292 changes = cl.read(n)
293 p1, p2 = cl.parents(n)
293 p1, p2 = cl.parents(n)
294 p1rev, p2rev = cl.rev(p1), cl.rev(p2)
294 p1rev, p2rev = cl.rev(p1), cl.rev(p2)
295 t = float(changes[2].split(' ')[0])
295 t = float(changes[2].split(' ')[0])
296
296
297 files = []
297 files = []
298 mf = self.repo.manifest.read(changes[0])
298 mf = self.repo.manifest.read(changes[0])
299 for f in changes[3]:
299 for f in changes[3]:
300 files.append(self.t("filenodelink",
300 files.append(self.t("filenodelink",
301 filenode = hex(mf.get(f, nullid)), file = f))
301 filenode = hex(mf.get(f, nullid)), file = f))
302
302
303 def diff():
303 def diff():
304 yield self.diff(p1, n, changes[3])
304 yield self.diff(p1, n, changes[3])
305
305
306 yield self.t('changeset',
306 yield self.t('changeset',
307 header = self.header(),
307 header = self.header(),
308 footer = self.footer(),
308 footer = self.footer(),
309 repo = self.reponame,
309 repo = self.reponame,
310 diff = diff,
310 diff = diff,
311 rev = cl.rev(n),
311 rev = cl.rev(n),
312 node = nodeid,
312 node = nodeid,
313 parent1 = self.parent("changesetparent",
313 parent1 = self.parent("changesetparent",
314 hex(p1), cl.rev(p1)),
314 hex(p1), cl.rev(p1)),
315 parent2 = self.parent("changesetparent",
315 parent2 = self.parent("changesetparent",
316 hex(p2), cl.rev(p2)),
316 hex(p2), cl.rev(p2)),
317 p1 = hex(p1), p2 = hex(p2),
317 p1 = hex(p1), p2 = hex(p2),
318 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
318 p1rev = cl.rev(p1), p2rev = cl.rev(p2),
319 manifest = hex(changes[0]),
319 manifest = hex(changes[0]),
320 author = changes[1],
320 author = changes[1],
321 desc = changes[4],
321 desc = changes[4],
322 date = t,
322 date = t,
323 files = files)
323 files = files)
324
324
325 def filelog(self, f, filenode):
325 def filelog(self, f, filenode):
326 cl = self.repo.changelog
326 cl = self.repo.changelog
327 fl = self.repo.file(f)
327 fl = self.repo.file(f)
328 count = fl.count()
328 count = fl.count()
329
329
330 def entries():
330 def entries():
331 l = []
331 l = []
332 parity = (count - 1) & 1
332 parity = (count - 1) & 1
333
333
334 for i in range(count):
334 for i in range(count):
335
335
336 n = fl.node(i)
336 n = fl.node(i)
337 lr = fl.linkrev(n)
337 lr = fl.linkrev(n)
338 cn = cl.node(lr)
338 cn = cl.node(lr)
339 cs = cl.read(cl.node(lr))
339 cs = cl.read(cl.node(lr))
340 p1, p2 = fl.parents(n)
340 p1, p2 = fl.parents(n)
341 t = float(cs[2].split(' ')[0])
341 t = float(cs[2].split(' ')[0])
342
342
343 l.insert(0, self.t("filelogentry",
343 l.insert(0, self.t("filelogentry",
344 parity = parity,
344 parity = parity,
345 filenode = hex(n),
345 filenode = hex(n),
346 filerev = i,
346 filerev = i,
347 file = f,
347 file = f,
348 node = hex(cn),
348 node = hex(cn),
349 author = cs[1],
349 author = cs[1],
350 date = t,
350 date = t,
351 desc = cs[4],
351 desc = cs[4],
352 p1 = hex(p1), p2 = hex(p2),
352 p1 = hex(p1), p2 = hex(p2),
353 p1rev = fl.rev(p1), p2rev = fl.rev(p2)))
353 p1rev = fl.rev(p1), p2rev = fl.rev(p2)))
354 parity = 1 - parity
354 parity = 1 - parity
355
355
356 yield l
356 yield l
357
357
358 yield self.t("filelog",
358 yield self.t("filelog",
359 header = self.header(),
359 header = self.header(),
360 footer = self.footer(),
360 footer = self.footer(),
361 repo = self.reponame,
361 repo = self.reponame,
362 file = f,
362 file = f,
363 filenode = filenode,
363 filenode = filenode,
364 entries = entries)
364 entries = entries)
365
365
366 def filerevision(self, f, node):
366 def filerevision(self, f, node):
367 fl = self.repo.file(f)
367 fl = self.repo.file(f)
368 n = bin(node)
368 n = bin(node)
369 text = fl.read(n)
369 text = fl.read(n)
370 changerev = fl.linkrev(n)
370 changerev = fl.linkrev(n)
371 cl = self.repo.changelog
371 cl = self.repo.changelog
372 cn = cl.node(changerev)
372 cn = cl.node(changerev)
373 cs = cl.read(cn)
373 cs = cl.read(cn)
374 p1, p2 = fl.parents(n)
374 p1, p2 = fl.parents(n)
375 t = float(cs[2].split(' ')[0])
375 t = float(cs[2].split(' ')[0])
376 mfn = cs[0]
376 mfn = cs[0]
377
377
378 def lines():
378 def lines():
379 for l, t in enumerate(text.splitlines(1)):
379 for l, t in enumerate(text.splitlines(1)):
380 yield self.t("fileline", line = t,
380 yield self.t("fileline", line = t,
381 linenumber = "% 6d" % (l + 1),
381 linenumber = "% 6d" % (l + 1),
382 parity = l & 1)
382 parity = l & 1)
383
383
384 yield self.t("filerevision", file = f,
384 yield self.t("filerevision", file = f,
385 header = self.header(),
385 header = self.header(),
386 footer = self.footer(),
386 footer = self.footer(),
387 repo = self.reponame,
387 repo = self.reponame,
388 filenode = node,
388 filenode = node,
389 path = up(f),
389 path = up(f),
390 text = lines(),
390 text = lines(),
391 rev = changerev,
391 rev = changerev,
392 node = hex(cn),
392 node = hex(cn),
393 manifest = hex(mfn),
393 manifest = hex(mfn),
394 author = cs[1],
394 author = cs[1],
395 date = t,
395 date = t,
396 parent1 = self.parent("filerevparent",
396 parent1 = self.parent("filerevparent",
397 hex(p1), fl.rev(p1), file=f),
397 hex(p1), fl.rev(p1), file=f),
398 parent2 = self.parent("filerevparent",
398 parent2 = self.parent("filerevparent",
399 hex(p2), fl.rev(p2), file=f),
399 hex(p2), fl.rev(p2), file=f),
400 p1 = hex(p1), p2 = hex(p2),
400 p1 = hex(p1), p2 = hex(p2),
401 permissions = self.repo.manifest.readflags(mfn)[f],
401 permissions = self.repo.manifest.readflags(mfn)[f],
402 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
402 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
403
403
404 def fileannotate(self, f, node):
404 def fileannotate(self, f, node):
405 bcache = {}
405 bcache = {}
406 ncache = {}
406 ncache = {}
407 fl = self.repo.file(f)
407 fl = self.repo.file(f)
408 n = bin(node)
408 n = bin(node)
409 changerev = fl.linkrev(n)
409 changerev = fl.linkrev(n)
410
410
411 cl = self.repo.changelog
411 cl = self.repo.changelog
412 cn = cl.node(changerev)
412 cn = cl.node(changerev)
413 cs = cl.read(cn)
413 cs = cl.read(cn)
414 p1, p2 = fl.parents(n)
414 p1, p2 = fl.parents(n)
415 t = float(cs[2].split(' ')[0])
415 t = float(cs[2].split(' ')[0])
416 mfn = cs[0]
416 mfn = cs[0]
417
417
418 def annotate():
418 def annotate():
419 parity = 1
419 parity = 1
420 last = None
420 last = None
421 for r, l in fl.annotate(n):
421 for r, l in fl.annotate(n):
422 try:
422 try:
423 cnode = ncache[r]
423 cnode = ncache[r]
424 except KeyError:
424 except KeyError:
425 cnode = ncache[r] = self.repo.changelog.node(r)
425 cnode = ncache[r] = self.repo.changelog.node(r)
426
426
427 try:
427 try:
428 name = bcache[r]
428 name = bcache[r]
429 except KeyError:
429 except KeyError:
430 cl = self.repo.changelog.read(cnode)
430 cl = self.repo.changelog.read(cnode)
431 name = cl[1]
431 name = cl[1]
432 f = name.find('@')
432 f = name.find('@')
433 if f >= 0:
433 if f >= 0:
434 name = name[:f]
434 name = name[:f]
435 f = name.find('<')
435 f = name.find('<')
436 if f >= 0:
436 if f >= 0:
437 name = name[f+1:]
437 name = name[f+1:]
438 bcache[r] = name
438 bcache[r] = name
439
439
440 if last != cnode:
440 if last != cnode:
441 parity = 1 - parity
441 parity = 1 - parity
442 last = cnode
442 last = cnode
443
443
444 yield self.t("annotateline",
444 yield self.t("annotateline",
445 parity = parity,
445 parity = parity,
446 node = hex(cnode),
446 node = hex(cnode),
447 rev = r,
447 rev = r,
448 author = name,
448 author = name,
449 file = f,
449 file = f,
450 line = l)
450 line = l)
451
451
452 yield self.t("fileannotate",
452 yield self.t("fileannotate",
453 header = self.header(),
453 header = self.header(),
454 footer = self.footer(),
454 footer = self.footer(),
455 repo = self.reponame,
455 repo = self.reponame,
456 file = f,
456 file = f,
457 filenode = node,
457 filenode = node,
458 annotate = annotate,
458 annotate = annotate,
459 path = up(f),
459 path = up(f),
460 rev = changerev,
460 rev = changerev,
461 node = hex(cn),
461 node = hex(cn),
462 manifest = hex(mfn),
462 manifest = hex(mfn),
463 author = cs[1],
463 author = cs[1],
464 date = t,
464 date = t,
465 parent1 = self.parent("fileannotateparent",
465 parent1 = self.parent("fileannotateparent",
466 hex(p1), fl.rev(p1), file=f),
466 hex(p1), fl.rev(p1), file=f),
467 parent2 = self.parent("fileannotateparent",
467 parent2 = self.parent("fileannotateparent",
468 hex(p2), fl.rev(p2), file=f),
468 hex(p2), fl.rev(p2), file=f),
469 p1 = hex(p1), p2 = hex(p2),
469 p1 = hex(p1), p2 = hex(p2),
470 permissions = self.repo.manifest.readflags(mfn)[f],
470 permissions = self.repo.manifest.readflags(mfn)[f],
471 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
471 p1rev = fl.rev(p1), p2rev = fl.rev(p2))
472
472
473 def manifest(self, mnode, path):
473 def manifest(self, mnode, path):
474 mf = self.repo.manifest.read(bin(mnode))
474 mf = self.repo.manifest.read(bin(mnode))
475 rev = self.repo.manifest.rev(bin(mnode))
475 rev = self.repo.manifest.rev(bin(mnode))
476 node = self.repo.changelog.node(rev)
476 node = self.repo.changelog.node(rev)
477 mff=self.repo.manifest.readflags(bin(mnode))
477 mff=self.repo.manifest.readflags(bin(mnode))
478
478
479 files = {}
479 files = {}
480
480
481 p = path[1:]
481 p = path[1:]
482 l = len(p)
482 l = len(p)
483
483
484 for f,n in mf.items():
484 for f,n in mf.items():
485 if f[:l] != p:
485 if f[:l] != p:
486 continue
486 continue
487 remain = f[l:]
487 remain = f[l:]
488 if "/" in remain:
488 if "/" in remain:
489 short = remain[:remain.find("/") + 1] # bleah
489 short = remain[:remain.find("/") + 1] # bleah
490 files[short] = (f, None)
490 files[short] = (f, None)
491 else:
491 else:
492 short = os.path.basename(remain)
492 short = os.path.basename(remain)
493 files[short] = (f, n)
493 files[short] = (f, n)
494
494
495 def filelist():
495 def filelist():
496 parity = 0
496 parity = 0
497 fl = files.keys()
497 fl = files.keys()
498 fl.sort()
498 fl.sort()
499 for f in fl:
499 for f in fl:
500 full, fnode = files[f]
500 full, fnode = files[f]
501 if fnode:
501 if fnode:
502 yield self.t("manifestfileentry",
502 yield self.t("manifestfileentry",
503 file = full,
503 file = full,
504 manifest = mnode,
504 manifest = mnode,
505 filenode = hex(fnode),
505 filenode = hex(fnode),
506 parity = parity,
506 parity = parity,
507 basename = f,
507 basename = f,
508 permissions = mff[full])
508 permissions = mff[full])
509 else:
509 else:
510 yield self.t("manifestdirentry",
510 yield self.t("manifestdirentry",
511 parity = parity,
511 parity = parity,
512 path = os.path.join(path, f),
512 path = os.path.join(path, f),
513 manifest = mnode, basename = f[:-1])
513 manifest = mnode, basename = f[:-1])
514 parity = 1 - parity
514 parity = 1 - parity
515
515
516 yield self.t("manifest",
516 yield self.t("manifest",
517 header = self.header(),
517 header = self.header(),
518 footer = self.footer(),
518 footer = self.footer(),
519 repo = self.reponame,
519 repo = self.reponame,
520 manifest = mnode,
520 manifest = mnode,
521 rev = rev,
521 rev = rev,
522 node = hex(node),
522 node = hex(node),
523 path = path,
523 path = path,
524 up = up(path),
524 up = up(path),
525 entries = filelist)
525 entries = filelist)
526
526
527 def tags(self):
527 def tags(self):
528 cl = self.repo.changelog
528 cl = self.repo.changelog
529 mf = cl.read(cl.tip())[0]
529 mf = cl.read(cl.tip())[0]
530
530
531 i = self.repo.tagslist()
531 i = self.repo.tagslist()
532 i.reverse()
532 i.reverse()
533
533
534 def entries():
534 def entries():
535 parity = 0
535 parity = 0
536 for k,n in i:
536 for k,n in i:
537 yield self.t("tagentry",
537 yield self.t("tagentry",
538 parity = parity,
538 parity = parity,
539 tag = k,
539 tag = k,
540 node = hex(n))
540 node = hex(n))
541 parity = 1 - parity
541 parity = 1 - parity
542
542
543 yield self.t("tags",
543 yield self.t("tags",
544 header = self.header(),
544 header = self.header(),
545 footer = self.footer(),
545 footer = self.footer(),
546 repo = self.reponame,
546 repo = self.reponame,
547 manifest = hex(mf),
547 manifest = hex(mf),
548 entries = entries)
548 entries = entries)
549
549
550 def filediff(self, file, changeset):
550 def filediff(self, file, changeset):
551 n = bin(changeset)
551 n = bin(changeset)
552 cl = self.repo.changelog
552 cl = self.repo.changelog
553 p1 = cl.parents(n)[0]
553 p1 = cl.parents(n)[0]
554 cs = cl.read(n)
554 cs = cl.read(n)
555 mf = self.repo.manifest.read(cs[0])
555 mf = self.repo.manifest.read(cs[0])
556
556
557 def diff():
557 def diff():
558 yield self.diff(p1, n, file)
558 yield self.diff(p1, n, file)
559
559
560 yield self.t("filediff",
560 yield self.t("filediff",
561 header = self.header(),
561 header = self.header(),
562 footer = self.footer(),
562 footer = self.footer(),
563 repo = self.reponame,
563 repo = self.reponame,
564 file = file,
564 file = file,
565 filenode = hex(mf.get(file, nullid)),
565 filenode = hex(mf.get(file, nullid)),
566 node = changeset,
566 node = changeset,
567 rev = self.repo.changelog.rev(n),
567 rev = self.repo.changelog.rev(n),
568 p1 = hex(p1),
568 p1 = hex(p1),
569 p1rev = self.repo.changelog.rev(p1),
569 p1rev = self.repo.changelog.rev(p1),
570 diff = diff)
570 diff = diff)
571
571
572 # add tags to things
572 # add tags to things
573 # tags -> list of changesets corresponding to tags
573 # tags -> list of changesets corresponding to tags
574 # find tag, changeset, file
574 # find tag, changeset, file
575
575
576 def run(self):
576 def run(self):
577 self.refresh()
577 self.refresh()
578 args = cgi.parse()
578 args = cgi.parse()
579
579
580 m = os.path.join(self.templates, "map")
580 m = os.path.join(self.templates, "map")
581 if args.has_key('style'):
581 if args.has_key('style'):
582 b = os.path.basename("map-" + args['style'][0])
582 b = os.path.basename("map-" + args['style'][0])
583 p = os.path.join(self.templates, b)
583 p = os.path.join(self.templates, b)
584 if os.path.isfile(p): m = p
584 if os.path.isfile(p): m = p
585
585
586 self.t = templater(m, self.filters)
586 self.t = templater(m, self.filters)
587
587
588 if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
588 if not args.has_key('cmd') or args['cmd'][0] == 'changelog':
589 hi = self.repo.changelog.count() - 1
589 hi = self.repo.changelog.count() - 1
590 if args.has_key('rev'):
590 if args.has_key('rev'):
591 hi = args['rev'][0]
591 hi = args['rev'][0]
592 try:
592 try:
593 hi = self.repo.changelog.rev(self.repo.lookup(hi))
593 hi = self.repo.changelog.rev(self.repo.lookup(hi))
594 except KeyError: pass
594 except KeyError: pass
595
595
596 write(self.changelog(hi))
596 write(self.changelog(hi))
597
597
598 elif args['cmd'][0] == 'changeset':
598 elif args['cmd'][0] == 'changeset':
599 write(self.changeset(args['node'][0]))
599 write(self.changeset(args['node'][0]))
600
600
601 elif args['cmd'][0] == 'manifest':
601 elif args['cmd'][0] == 'manifest':
602 write(self.manifest(args['manifest'][0], args['path'][0]))
602 write(self.manifest(args['manifest'][0], args['path'][0]))
603
603
604 elif args['cmd'][0] == 'tags':
604 elif args['cmd'][0] == 'tags':
605 write(self.tags())
605 write(self.tags())
606
606
607 elif args['cmd'][0] == 'filediff':
607 elif args['cmd'][0] == 'filediff':
608 write(self.filediff(args['file'][0], args['node'][0]))
608 write(self.filediff(args['file'][0], args['node'][0]))
609
609
610 elif args['cmd'][0] == 'file':
610 elif args['cmd'][0] == 'file':
611 write(self.filerevision(args['file'][0], args['filenode'][0]))
611 write(self.filerevision(args['file'][0], args['filenode'][0]))
612
612
613 elif args['cmd'][0] == 'annotate':
613 elif args['cmd'][0] == 'annotate':
614 write(self.fileannotate(args['file'][0], args['filenode'][0]))
614 write(self.fileannotate(args['file'][0], args['filenode'][0]))
615
615
616 elif args['cmd'][0] == 'filelog':
616 elif args['cmd'][0] == 'filelog':
617 write(self.filelog(args['file'][0], args['filenode'][0]))
617 write(self.filelog(args['file'][0], args['filenode'][0]))
618
618
619 elif args['cmd'][0] == 'heads':
619 elif args['cmd'][0] == 'heads':
620 httphdr("text/plain")
620 httphdr("text/plain")
621 h = self.repo.heads()
621 h = self.repo.heads()
622 sys.stdout.write(" ".join(map(hex, h)) + "\n")
622 sys.stdout.write(" ".join(map(hex, h)) + "\n")
623
623
624 elif args['cmd'][0] == 'branches':
624 elif args['cmd'][0] == 'branches':
625 httphdr("text/plain")
625 httphdr("text/plain")
626 nodes = []
626 nodes = []
627 if args.has_key('nodes'):
627 if args.has_key('nodes'):
628 nodes = map(bin, args['nodes'][0].split(" "))
628 nodes = map(bin, args['nodes'][0].split(" "))
629 for b in self.repo.branches(nodes):
629 for b in self.repo.branches(nodes):
630 sys.stdout.write(" ".join(map(hex, b)) + "\n")
630 sys.stdout.write(" ".join(map(hex, b)) + "\n")
631
631
632 elif args['cmd'][0] == 'between':
632 elif args['cmd'][0] == 'between':
633 httphdr("text/plain")
633 httphdr("text/plain")
634 nodes = []
634 nodes = []
635 if args.has_key('pairs'):
635 if args.has_key('pairs'):
636 pairs = [ map(bin, p.split("-"))
636 pairs = [ map(bin, p.split("-"))
637 for p in args['pairs'][0].split(" ") ]
637 for p in args['pairs'][0].split(" ") ]
638 for b in self.repo.between(pairs):
638 for b in self.repo.between(pairs):
639 sys.stdout.write(" ".join(map(hex, b)) + "\n")
639 sys.stdout.write(" ".join(map(hex, b)) + "\n")
640
640
641 elif args['cmd'][0] == 'changegroup':
641 elif args['cmd'][0] == 'changegroup':
642 httphdr("application/hg-changegroup")
642 httphdr("application/hg-changegroup")
643 nodes = []
643 nodes = []
644 if self.viewonly:
644 if self.viewonly:
645 return
645 return
646
646
647 if args.has_key('roots'):
647 if args.has_key('roots'):
648 nodes = map(bin, args['roots'][0].split(" "))
648 nodes = map(bin, args['roots'][0].split(" "))
649
649
650 z = zlib.compressobj()
650 z = zlib.compressobj()
651 for chunk in self.repo.changegroup(nodes):
651 for chunk in self.repo.changegroup(nodes):
652 sys.stdout.write(z.compress(chunk))
652 sys.stdout.write(z.compress(chunk))
653
653
654 sys.stdout.write(z.flush())
654 sys.stdout.write(z.flush())
655
655
656 else:
656 else:
657 write(self.t("error"))
657 write(self.t("error"))
658
658
659 def server(path, name, templates, address, port):
659 def server(path, name, templates, address, port):
660
660
661 import BaseHTTPServer
661 import BaseHTTPServer
662 import sys, os
662 import sys, os
663
663
664 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
664 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
665 def do_POST(self):
665 def do_POST(self):
666 try:
666 try:
667 self.do_hgweb()
667 self.do_hgweb()
668 except socket.error, inst:
668 except socket.error, inst:
669 if inst.args[0] != 32: raise
669 if inst.args[0] != 32: raise
670
670
671 def do_GET(self):
671 def do_GET(self):
672 self.do_POST()
672 self.do_POST()
673
673
674 def do_hgweb(self):
674 def do_hgweb(self):
675 query = ""
675 query = ""
676 p = self.path.find("?")
676 p = self.path.find("?")
677 if p:
677 if p:
678 query = self.path[p + 1:]
678 query = self.path[p + 1:]
679 query = query.replace('+', ' ')
679 query = query.replace('+', ' ')
680
680
681 env = {}
681 env = {}
682 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
682 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
683 env['REQUEST_METHOD'] = self.command
683 env['REQUEST_METHOD'] = self.command
684 if query:
684 if query:
685 env['QUERY_STRING'] = query
685 env['QUERY_STRING'] = query
686 host = self.address_string()
686 host = self.address_string()
687 if host != self.client_address[0]:
687 if host != self.client_address[0]:
688 env['REMOTE_HOST'] = host
688 env['REMOTE_HOST'] = host
689 env['REMOTE_ADDR'] = self.client_address[0]
689 env['REMOTE_ADDR'] = self.client_address[0]
690
690
691 if self.headers.typeheader is None:
691 if self.headers.typeheader is None:
692 env['CONTENT_TYPE'] = self.headers.type
692 env['CONTENT_TYPE'] = self.headers.type
693 else:
693 else:
694 env['CONTENT_TYPE'] = self.headers.typeheader
694 env['CONTENT_TYPE'] = self.headers.typeheader
695 length = self.headers.getheader('content-length')
695 length = self.headers.getheader('content-length')
696 if length:
696 if length:
697 env['CONTENT_LENGTH'] = length
697 env['CONTENT_LENGTH'] = length
698 accept = []
698 accept = []
699 for line in self.headers.getallmatchingheaders('accept'):
699 for line in self.headers.getallmatchingheaders('accept'):
700 if line[:1] in "\t\n\r ":
700 if line[:1] in "\t\n\r ":
701 accept.append(line.strip())
701 accept.append(line.strip())
702 else:
702 else:
703 accept = accept + line[7:].split(',')
703 accept = accept + line[7:].split(',')
704 env['HTTP_ACCEPT'] = ','.join(accept)
704 env['HTTP_ACCEPT'] = ','.join(accept)
705
705
706 os.environ.update(env)
706 os.environ.update(env)
707
707
708 save = sys.argv, sys.stdin, sys.stdout, sys.stderr
708 save = sys.argv, sys.stdin, sys.stdout, sys.stderr
709 try:
709 try:
710 sys.stdin = self.rfile
710 sys.stdin = self.rfile
711 sys.stdout = self.wfile
711 sys.stdout = self.wfile
712 sys.argv = ["hgweb.py"]
712 sys.argv = ["hgweb.py"]
713 if '=' not in query:
713 if '=' not in query:
714 sys.argv.append(query)
714 sys.argv.append(query)
715 self.send_response(200, "Script output follows")
715 self.send_response(200, "Script output follows")
716 hg.run()
716 hg.run()
717 finally:
717 finally:
718 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
718 sys.argv, sys.stdin, sys.stdout, sys.stderr = save
719
719
720 hg = hgweb(path, name, templates)
720 hg = hgweb(path, name, templates)
721 httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
721 httpd = BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
722 httpd.serve_forever()
722 httpd.serve_forever()
@@ -1,20 +1,21 b''
1 + hg clone http://localhost:20059/ copy
1 + hg clone http://localhost:20059/ copy
2 requesting all changes
2 requesting all changes
3 adding changesets
3 adding changesets
4 abort: error 111: Connection refused
4 abort: error 111: Connection refused
5 transaction abort!
5 transaction abort!
6 rollback completed
6 rollback completed
7 + echo 255
7 + echo 255
8 255
8 255
9 + ls copy
9 + ls copy
10 ls: copy: No such file or directory
10 + cat
11 + cat
11 + python dumb.py
12 + python dumb.py
12 + hg clone http://localhost:20059/foo copy2
13 + hg clone http://localhost:20059/foo copy2
13 requesting all changes
14 requesting all changes
14 adding changesets
15 adding changesets
15 abort: HTTP Error 404: File not found
16 abort: HTTP Error 404: File not found
16 transaction abort!
17 transaction abort!
17 rollback completed
18 rollback completed
18 + echo 255
19 + echo 255
19 255
20 255
20 + set +x
21 + set +x
General Comments 0
You need to be logged in to leave comments. Login now