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