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