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