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