##// END OF EJS Templates
Support for 0, 1, or 2 diff revs
mpm@selenic.com -
r33:98633e60 default
parent child Browse files
Show More
@@ -1,323 +1,339 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # mercurial - a minimal scalable distributed SCM
3 # mercurial - a minimal scalable distributed SCM
4 # v0.4d "oedipa maas"
4 # v0.4d "oedipa maas"
5 #
5 #
6 # Copyright 2005 Matt Mackall <mpm@selenic.com>
6 # Copyright 2005 Matt Mackall <mpm@selenic.com>
7 #
7 #
8 # This software may be used and distributed according to the terms
8 # This software may be used and distributed according to the terms
9 # of the GNU General Public License, incorporated herein by reference.
9 # of the GNU General Public License, incorporated herein by reference.
10
10
11 # the psyco compiler makes commits about twice as fast
11 # the psyco compiler makes commits about twice as fast
12 try:
12 try:
13 import psyco
13 import psyco
14 psyco.full()
14 psyco.full()
15 except:
15 except:
16 pass
16 pass
17
17
18 import sys, os, time
18 import sys, os, time
19 from mercurial import hg, mdiff, fancyopts
19 from mercurial import hg, mdiff, fancyopts
20
20
21 def help():
21 def help():
22 print """\
22 print """\
23 commands:
23 commands:
24
24
25 init create a new repository in this directory
25 init create a new repository in this directory
26 branch <path> create a branch of <path> in this directory
26 branch <path> create a branch of <path> in this directory
27 merge <path> merge changes from <path> into local repository
27 merge <path> merge changes from <path> into local repository
28 checkout [changeset] checkout the latest or given changeset
28 checkout [changeset] checkout the latest or given changeset
29 status show new, missing, and changed files in working dir
29 status show new, missing, and changed files in working dir
30 add [files...] add the given files in the next commit
30 add [files...] add the given files in the next commit
31 remove [files...] remove the given files in the next commit
31 remove [files...] remove the given files in the next commit
32 addremove add all new files, delete all missing files
32 addremove add all new files, delete all missing files
33 commit commit all changes to the repository
33 commit commit all changes to the repository
34 history show changeset history
34 history show changeset history
35 log <file> show revision history of a single file
35 log <file> show revision history of a single file
36 dump <file> [rev] dump the latest or given revision of a file
36 dump <file> [rev] dump the latest or given revision of a file
37 dumpmanifest [rev] dump the latest or given revision of the manifest
37 dumpmanifest [rev] dump the latest or given revision of the manifest
38 diff [files...] diff working directory (or selected files)
38 diff [files...] diff working directory (or selected files)
39 """
39 """
40
40
41 def diffdir(node, files = None):
41 def filterfiles(list, files):
42 (c, a, d) = repo.diffdir(repo.root, node)
42 l = [ x for x in list if x in files ]
43
43
44 if args:
44 for f in files:
45 nc = [ x for x in c if x in args ]
45 if f[-1] != os.sep: f += os.sep
46 na = [ x for x in a if x in args ]
46 l += [ x for x in list if x.startswith(f) ]
47 nd = [ x for x in d if x in args ]
47 return l
48 for arg in args:
49 if not os.path.isdir(arg): continue
50 if arg[-1] != os.sep: arg += os.sep
51 nc += [ x for x in c if x.startswith(arg) ]
52 na += [ x for x in a if x.startswith(arg) ]
53 nd += [ x for x in d if x.startswith(arg) ]
54 (c, a, d) = (nc, na, nd)
55
56 return (c, a, d)
57
58
48
59 options = {}
49 options = {}
60 opts = [('v', 'verbose', None, 'verbose'),
50 opts = [('v', 'verbose', None, 'verbose'),
61 ('d', 'debug', None, 'debug')]
51 ('d', 'debug', None, 'debug')]
62
52
63 args = fancyopts.fancyopts(sys.argv[1:], opts, options,
53 args = fancyopts.fancyopts(sys.argv[1:], opts, options,
64 'hg [options] <command> [command options] [files]')
54 'hg [options] <command> [command options] [files]')
65
55
66 try:
56 try:
67 cmd = args[0]
57 cmd = args[0]
68 args = args[1:]
58 args = args[1:]
69 except:
59 except:
70 cmd = ""
60 cmd = ""
71
61
72 ui = hg.ui(options["verbose"], options["debug"])
62 ui = hg.ui(options["verbose"], options["debug"])
73
63
74 if cmd == "init":
64 if cmd == "init":
75 repo = hg.repository(ui, ".", create=1)
65 repo = hg.repository(ui, ".", create=1)
76 sys.exit(0)
66 sys.exit(0)
77 elif cmd == "branch" or cmd == "clone":
67 elif cmd == "branch" or cmd == "clone":
78 os.system("cp -al %s/.hg .hg" % args[0])
68 os.system("cp -al %s/.hg .hg" % args[0])
79 sys.exit(0)
69 sys.exit(0)
80 elif cmd == "help":
70 elif cmd == "help":
81 help()
71 help()
82 sys.exit(0)
72 sys.exit(0)
83 else:
73 else:
84 try:
74 try:
85 repo = hg.repository(ui=ui)
75 repo = hg.repository(ui=ui)
86 except:
76 except:
87 print "Unable to open repository"
77 print "Unable to open repository"
88 sys.exit(0)
78 sys.exit(0)
89
79
90 if cmd == "checkout" or cmd == "co":
80 if cmd == "checkout" or cmd == "co":
91 node = repo.changelog.tip()
81 node = repo.changelog.tip()
92 if len(args):
82 if len(args):
93 if len(args[0]) < 40:
83 if len(args[0]) < 40:
94 rev = int(args[0])
84 rev = int(args[0])
95 node = repo.changelog.node(rev)
85 node = repo.changelog.node(rev)
96 else:
86 else:
97 node = args[0]
87 node = args[0]
98 repo.checkout(node)
88 repo.checkout(node)
99
89
100 elif cmd == "add":
90 elif cmd == "add":
101 repo.add(args)
91 repo.add(args)
102
92
103 elif cmd == "remove" or cmd == "rm" or cmd == "del" or cmd == "delete":
93 elif cmd == "remove" or cmd == "rm" or cmd == "del" or cmd == "delete":
104 repo.remove(args)
94 repo.remove(args)
105
95
106 elif cmd == "commit" or cmd == "checkin" or cmd == "ci":
96 elif cmd == "commit" or cmd == "checkin" or cmd == "ci":
107 if 1:
97 if 1:
108 if len(args) > 0:
98 if len(args) > 0:
109 repo.commit(repo.current, args)
99 repo.commit(repo.current, args)
110 else:
100 else:
111 repo.commit(repo.current)
101 repo.commit(repo.current)
112
102
113 elif cmd == "import" or cmd == "patch":
103 elif cmd == "import" or cmd == "patch":
114 ioptions = {}
104 ioptions = {}
115 opts = [('p', 'strip', 1, 'path strip'),
105 opts = [('p', 'strip', 1, 'path strip'),
116 ('b', 'base', "", 'base path')]
106 ('b', 'base', "", 'base path')]
117
107
118 args = fancyopts.fancyopts(args, opts, ioptions,
108 args = fancyopts.fancyopts(args, opts, ioptions,
119 'hg import [options] <patch names>')
109 'hg import [options] <patch names>')
120 d = ioptions["base"]
110 d = ioptions["base"]
121 strip = ioptions["strip"]
111 strip = ioptions["strip"]
122
112
123 for patch in args:
113 for patch in args:
124 ui.status("applying %s\n" % patch)
114 ui.status("applying %s\n" % patch)
125 pf = d + patch
115 pf = d + patch
126 os.system("patch -p%d < %s > /dev/null" % (strip, pf))
116 os.system("patch -p%d < %s > /dev/null" % (strip, pf))
127 f = os.popen("lsdiff --strip %d %s" % (strip, pf))
117 f = os.popen("lsdiff --strip %d %s" % (strip, pf))
128 files = f.read().splitlines()
118 files = f.read().splitlines()
129 f.close()
119 f.close()
130 repo.commit(files)
120 repo.commit(files)
131
121
132 elif cmd == "status":
122 elif cmd == "status":
133 (c, a, d) = diffdir(repo.current)
123 (c, a, d) = repo.diffdir(repo.root, repo.current)
134 for f in c: print "C", f
124 for f in c: print "C", f
135 for f in a: print "?", f
125 for f in a: print "?", f
136 for f in d: print "R", f
126 for f in d: print "R", f
137
127
138 elif cmd == "diff":
128 elif cmd == "diff":
139 (c, a, d) = diffdir(repo.current, args)
129 doptions = {}
130 revs = [repo.current]
140
131
141 mmap = {}
132 if args:
142 if repo.current:
133 opts = [('r', 'revision', [], 'revision')]
143 change = repo.changelog.read(repo.current)
134 args = fancyopts.fancyopts(args, opts, doptions,
144 mmap = repo.manifest.read(change[0])
135 'hg diff [options] [files]')
136 # revs = [ repo.lookup(x) for x in doptions['revision'] ]
137 revs = [hg.bin(x) for x in doptions['revision']]
138
139 if len(revs) > 2:
140 print "too many revisions to diff"
141 sys.exit(1)
142 elif len(revs) == 2:
143 change = repo.changelog.read(revs[1])
144 mmap2 = repo.manifest.read(change[0])
145 (c, a, d) = repo.diffrevs(revs[0], revs[1])
146 def read(f): return repo.file(f).read(mmap2[f])
147 else:
148 if len(revs) < 1:
149 if not repo.current:
150 sys.exit(0)
151 (c, a, d) = repo.diffdir(repo.root, revs[0])
152 def read(f): return file(f).read()
153
154 change = repo.changelog.read(revs[0])
155 mmap = repo.manifest.read(change[0])
156
157 if args:
158 c = filterfiles(c, args)
159 a = filterfiles(a, args)
160 d = filterfiles(d, args)
145
161
146 for f in c:
162 for f in c:
147 to = repo.file(f).read(mmap[f])
163 to = repo.file(f).read(mmap[f])
148 tn = file(f).read()
164 tn = read(f)
149 sys.stdout.write(mdiff.unidiff(to, tn, f))
165 sys.stdout.write(mdiff.unidiff(to, tn, f))
150 for f in a:
166 for f in a:
151 to = ""
167 to = ""
152 tn = file(f).read()
168 tn = read(f)
153 sys.stdout.write(mdiff.unidiff(to, tn, f))
169 sys.stdout.write(mdiff.unidiff(to, tn, f))
154 for f in d:
170 for f in d:
155 to = repo.file(f).read(mmap[f])
171 to = repo.file(f).read(mmap[f])
156 tn = ""
172 tn = ""
157 sys.stdout.write(mdiff.unidiff(to, tn, f))
173 sys.stdout.write(mdiff.unidiff(to, tn, f))
158
174
159 elif cmd == "addremove":
175 elif cmd == "addremove":
160 (c, a, d) = repo.diffdir(repo.root, repo.current)
176 (c, a, d) = repo.diffdir(repo.root, repo.current)
161 repo.add(a)
177 repo.add(a)
162 repo.remove(d)
178 repo.remove(d)
163
179
164 elif cmd == "history":
180 elif cmd == "history":
165 for i in range(repo.changelog.count()):
181 for i in range(repo.changelog.count()):
166 n = repo.changelog.node(i)
182 n = repo.changelog.node(i)
167 changes = repo.changelog.read(n)
183 changes = repo.changelog.read(n)
168 (p1, p2) = repo.changelog.parents(n)
184 (p1, p2) = repo.changelog.parents(n)
169 (h, h1, h2) = map(hg.hex, (n, p1, p2))
185 (h, h1, h2) = map(hg.hex, (n, p1, p2))
170 (i1, i2) = map(repo.changelog.rev, (p1, p2))
186 (i1, i2) = map(repo.changelog.rev, (p1, p2))
171 print "rev: %4d:%s" % (i, h)
187 print "rev: %4d:%s" % (i, h)
172 print "parents: %4d:%s" % (i1, h1)
188 print "parents: %4d:%s" % (i1, h1)
173 if i2: print " %4d:%s" % (i2, h2)
189 if i2: print " %4d:%s" % (i2, h2)
174 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
190 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
175 hg.hex(changes[0]))
191 hg.hex(changes[0]))
176 print "user:", changes[1]
192 print "user:", changes[1]
177 print "date:", time.asctime(
193 print "date:", time.asctime(
178 time.localtime(float(changes[2].split(' ')[0])))
194 time.localtime(float(changes[2].split(' ')[0])))
179 print "files:", " ".join(changes[3])
195 print "files:", " ".join(changes[3])
180 print "description:"
196 print "description:"
181 print changes[4]
197 print changes[4]
182
198
183 elif cmd == "log":
199 elif cmd == "log":
184 if args:
200 if args:
185 r = repo.file(args[0])
201 r = repo.file(args[0])
186 for i in range(r.count()):
202 for i in range(r.count()):
187 n = r.node(i)
203 n = r.node(i)
188 (p1, p2) = r.parents(n)
204 (p1, p2) = r.parents(n)
189 (h, h1, h2) = map(hg.hex, (n, p1, p2))
205 (h, h1, h2) = map(hg.hex, (n, p1, p2))
190 (i1, i2) = map(r.rev, (p1, p2))
206 (i1, i2) = map(r.rev, (p1, p2))
191 cr = r.linkrev(n)
207 cr = r.linkrev(n)
192 cn = hg.hex(repo.changelog.node(cr))
208 cn = hg.hex(repo.changelog.node(cr))
193 print "rev: %4d:%s" % (i, h)
209 print "rev: %4d:%s" % (i, h)
194 print "changeset: %4d:%s" % (cr, cn)
210 print "changeset: %4d:%s" % (cr, cn)
195 print "parents: %4d:%s" % (i1, h1)
211 print "parents: %4d:%s" % (i1, h1)
196 if i2: print " %4d:%s" % (i2, h2)
212 if i2: print " %4d:%s" % (i2, h2)
197 else:
213 else:
198 print "missing filename"
214 print "missing filename"
199
215
200 elif cmd == "dump":
216 elif cmd == "dump":
201 if args:
217 if args:
202 r = repo.file(args[0])
218 r = repo.file(args[0])
203 n = r.tip()
219 n = r.tip()
204 if len(args) > 1: n = hg.bin(args[1])
220 if len(args) > 1: n = hg.bin(args[1])
205 sys.stdout.write(r.read(n))
221 sys.stdout.write(r.read(n))
206 else:
222 else:
207 print "missing filename"
223 print "missing filename"
208
224
209 elif cmd == "dumpmanifest":
225 elif cmd == "dumpmanifest":
210 n = repo.manifest.tip()
226 n = repo.manifest.tip()
211 if len(args) > 0:
227 if len(args) > 0:
212 n = hg.bin(args[0])
228 n = hg.bin(args[0])
213 m = repo.manifest.read(n)
229 m = repo.manifest.read(n)
214 files = m.keys()
230 files = m.keys()
215 files.sort()
231 files.sort()
216
232
217 for f in files:
233 for f in files:
218 print hg.hex(m[f]), f
234 print hg.hex(m[f]), f
219
235
220 elif cmd == "debughash":
236 elif cmd == "debughash":
221 f = repo.file(args[0])
237 f = repo.file(args[0])
222 print f.encodepath(args[0])
238 print f.encodepath(args[0])
223
239
224 elif cmd == "debugindex":
240 elif cmd == "debugindex":
225 r = hg.revlog(open, args[0], "")
241 r = hg.revlog(open, args[0], "")
226 print " rev offset length base linkrev"+\
242 print " rev offset length base linkrev"+\
227 " p1 p2 nodeid"
243 " p1 p2 nodeid"
228 for i in range(r.count()):
244 for i in range(r.count()):
229 e = r.index[i]
245 e = r.index[i]
230 print "% 6d % 9d % 7d % 5d % 7d %s.. %s.. %s.." % (
246 print "% 6d % 9d % 7d % 5d % 7d %s.. %s.. %s.." % (
231 i, e[0], e[1], e[2], e[3],
247 i, e[0], e[1], e[2], e[3],
232 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
248 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
233
249
234 elif cmd == "merge":
250 elif cmd == "merge":
235 if args:
251 if args:
236 other = hg.repository(ui, args[0])
252 other = hg.repository(ui, args[0])
237 repo.merge(other)
253 repo.merge(other)
238 else:
254 else:
239 print "missing source repository"
255 print "missing source repository"
240
256
241 elif cmd == "verify":
257 elif cmd == "verify":
242 filelinkrevs = {}
258 filelinkrevs = {}
243 filenodes = {}
259 filenodes = {}
244 manifestchangeset = {}
260 manifestchangeset = {}
245 changesets = revisions = files = 0
261 changesets = revisions = files = 0
246
262
247 print "checking changesets"
263 print "checking changesets"
248 for i in range(repo.changelog.count()):
264 for i in range(repo.changelog.count()):
249 changesets += 1
265 changesets += 1
250 n = repo.changelog.node(i)
266 n = repo.changelog.node(i)
251 changes = repo.changelog.read(n)
267 changes = repo.changelog.read(n)
252 manifestchangeset[changes[0]] = n
268 manifestchangeset[changes[0]] = n
253 for f in changes[3]:
269 for f in changes[3]:
254 revisions += 1
270 revisions += 1
255 filelinkrevs.setdefault(f, []).append(i)
271 filelinkrevs.setdefault(f, []).append(i)
256
272
257 print "checking manifests"
273 print "checking manifests"
258 for i in range(repo.manifest.count()):
274 for i in range(repo.manifest.count()):
259 n = repo.manifest.node(i)
275 n = repo.manifest.node(i)
260 ca = repo.changelog.node(repo.manifest.linkrev(n))
276 ca = repo.changelog.node(repo.manifest.linkrev(n))
261 cc = manifestchangeset[n]
277 cc = manifestchangeset[n]
262 if ca != cc:
278 if ca != cc:
263 print "manifest %s points to %s, not %s" % \
279 print "manifest %s points to %s, not %s" % \
264 (hg.hex(n), hg.hex(ca), hg.hex(cc))
280 (hg.hex(n), hg.hex(ca), hg.hex(cc))
265 m = repo.manifest.read(n)
281 m = repo.manifest.read(n)
266 for f, fn in m.items():
282 for f, fn in m.items():
267 filenodes.setdefault(f, {})[fn] = 1
283 filenodes.setdefault(f, {})[fn] = 1
268
284
269 print "crosschecking files in changesets and manifests"
285 print "crosschecking files in changesets and manifests"
270 for f in filenodes:
286 for f in filenodes:
271 if f not in filelinkrevs:
287 if f not in filelinkrevs:
272 print "file %s in manifest but not in changesets"
288 print "file %s in manifest but not in changesets"
273
289
274 for f in filelinkrevs:
290 for f in filelinkrevs:
275 if f not in filenodes:
291 if f not in filenodes:
276 print "file %s in changeset but not in manifest"
292 print "file %s in changeset but not in manifest"
277
293
278 print "checking files"
294 print "checking files"
279 for f in filenodes:
295 for f in filenodes:
280 files += 1
296 files += 1
281 fl = repo.file(f)
297 fl = repo.file(f)
282 nodes = {"\0"*20: 1}
298 nodes = {"\0"*20: 1}
283 for i in range(fl.count()):
299 for i in range(fl.count()):
284 n = fl.node(i)
300 n = fl.node(i)
285
301
286 if n not in filenodes[f]:
302 if n not in filenodes[f]:
287 print "%s:%s not in manifests" % (f, hg.hex(n))
303 print "%s:%s not in manifests" % (f, hg.hex(n))
288 else:
304 else:
289 del filenodes[f][n]
305 del filenodes[f][n]
290
306
291 flr = fl.linkrev(n)
307 flr = fl.linkrev(n)
292 if flr not in filelinkrevs[f]:
308 if flr not in filelinkrevs[f]:
293 print "%s:%s points to unexpected changeset rev %d" \
309 print "%s:%s points to unexpected changeset rev %d" \
294 % (f, hg.hex(n), fl.linkrev(n))
310 % (f, hg.hex(n), fl.linkrev(n))
295 else:
311 else:
296 filelinkrevs[f].remove(flr)
312 filelinkrevs[f].remove(flr)
297
313
298 # verify contents
314 # verify contents
299 t = fl.read(n)
315 t = fl.read(n)
300
316
301 # verify parents
317 # verify parents
302 (p1, p2) = fl.parents(n)
318 (p1, p2) = fl.parents(n)
303 if p1 not in nodes:
319 if p1 not in nodes:
304 print "%s:%s unknown parent 1 %s" % (f, hg.hex(n), hg.hex(p1))
320 print "%s:%s unknown parent 1 %s" % (f, hg.hex(n), hg.hex(p1))
305 if p2 not in nodes:
321 if p2 not in nodes:
306 print "file %s:%s unknown parent %s" % (f, hg.hex(n), hg.hex(p1))
322 print "file %s:%s unknown parent %s" % (f, hg.hex(n), hg.hex(p1))
307 nodes[n] = 1
323 nodes[n] = 1
308
324
309 # cross-check
325 # cross-check
310 for flr in filelinkrevs[f]:
326 for flr in filelinkrevs[f]:
311 print "changeset rev %d not in %s" % (flr, f)
327 print "changeset rev %d not in %s" % (flr, f)
312
328
313 for node in filenodes[f]:
329 for node in filenodes[f]:
314 print "node %s in manifests not in %s" % (hg.hex(n), f)
330 print "node %s in manifests not in %s" % (hg.hex(n), f)
315
331
316
332
317 print "%d files, %d changesets, %d total revisions" % (files, changesets,
333 print "%d files, %d changesets, %d total revisions" % (files, changesets,
318 revisions)
334 revisions)
319
335
320 else:
336 else:
321 print "unknown command\n"
337 print "unknown command\n"
322 help()
338 help()
323 sys.exit(1)
339 sys.exit(1)
@@ -1,614 +1,614 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, sha, socket, os, time, base64, re, urllib2, binascii
8 import sys, struct, sha, socket, os, time, base64, re, urllib2, binascii
9 import urllib
9 import urllib
10 from mercurial import byterange
10 from mercurial import byterange
11 from mercurial.transaction import *
11 from mercurial.transaction import *
12 from mercurial.revlog import *
12 from mercurial.revlog import *
13
13
14 def hex(node): return binascii.hexlify(node)
14 def hex(node): return binascii.hexlify(node)
15 def bin(node): return binascii.unhexlify(node)
15 def bin(node): return binascii.unhexlify(node)
16
16
17 class filelog(revlog):
17 class filelog(revlog):
18 def __init__(self, opener, path):
18 def __init__(self, opener, path):
19 s = self.encodepath(path)
19 s = self.encodepath(path)
20 revlog.__init__(self, opener, os.path.join("data", s + "i"),
20 revlog.__init__(self, opener, os.path.join("data", s + "i"),
21 os.path.join("data", s))
21 os.path.join("data", s))
22
22
23 def encodepath(self, path):
23 def encodepath(self, path):
24 s = sha.sha(path).digest()
24 s = sha.sha(path).digest()
25 s = base64.encodestring(s)[:-3]
25 s = base64.encodestring(s)[:-3]
26 s = re.sub("\+", "%", s)
26 s = re.sub("\+", "%", s)
27 s = re.sub("/", "_", s)
27 s = re.sub("/", "_", s)
28 return s
28 return s
29
29
30 def read(self, node):
30 def read(self, node):
31 return self.revision(node)
31 return self.revision(node)
32 def add(self, text, transaction, link, p1=None, p2=None):
32 def add(self, text, transaction, link, p1=None, p2=None):
33 return self.addrevision(text, transaction, link, p1, p2)
33 return self.addrevision(text, transaction, link, p1, p2)
34
34
35 def resolvedag(self, old, new, transaction, link):
35 def resolvedag(self, old, new, transaction, link):
36 """resolve unmerged heads in our DAG"""
36 """resolve unmerged heads in our DAG"""
37 if old == new: return None
37 if old == new: return None
38 a = self.ancestor(old, new)
38 a = self.ancestor(old, new)
39 if old == a: return new
39 if old == a: return new
40 return self.merge3(old, new, a, transaction, link)
40 return self.merge3(old, new, a, transaction, link)
41
41
42 def merge3(self, my, other, base, transaction, link):
42 def merge3(self, my, other, base, transaction, link):
43 """perform a 3-way merge and append the result"""
43 """perform a 3-way merge and append the result"""
44 def temp(prefix, node):
44 def temp(prefix, node):
45 (fd, name) = tempfile.mkstemp(prefix)
45 (fd, name) = tempfile.mkstemp(prefix)
46 f = os.fdopen(fd, "w")
46 f = os.fdopen(fd, "w")
47 f.write(self.revision(node))
47 f.write(self.revision(node))
48 f.close()
48 f.close()
49 return name
49 return name
50
50
51 a = temp("local", my)
51 a = temp("local", my)
52 b = temp("remote", other)
52 b = temp("remote", other)
53 c = temp("parent", base)
53 c = temp("parent", base)
54
54
55 cmd = os.environ["HGMERGE"]
55 cmd = os.environ["HGMERGE"]
56 r = os.system("%s %s %s %s" % (cmd, a, b, c))
56 r = os.system("%s %s %s %s" % (cmd, a, b, c))
57 if r:
57 if r:
58 raise "Merge failed, implement rollback!"
58 raise "Merge failed, implement rollback!"
59
59
60 t = open(a).read()
60 t = open(a).read()
61 os.unlink(a)
61 os.unlink(a)
62 os.unlink(b)
62 os.unlink(b)
63 os.unlink(c)
63 os.unlink(c)
64 return self.addrevision(t, transaction, link, my, other)
64 return self.addrevision(t, transaction, link, my, other)
65
65
66 def merge(self, other, transaction, linkseq, link):
66 def merge(self, other, transaction, linkseq, link):
67 """perform a merge and resolve resulting heads"""
67 """perform a merge and resolve resulting heads"""
68 (o, n) = self.mergedag(other, transaction, linkseq)
68 (o, n) = self.mergedag(other, transaction, linkseq)
69 return self.resolvedag(o, n, transaction, link)
69 return self.resolvedag(o, n, transaction, link)
70
70
71 class manifest(revlog):
71 class manifest(revlog):
72 def __init__(self, opener):
72 def __init__(self, opener):
73 self.mapcache = None
73 self.mapcache = None
74 self.listcache = None
74 self.listcache = None
75 self.addlist = None
75 self.addlist = None
76 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
76 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
77
77
78 def read(self, node):
78 def read(self, node):
79 if self.mapcache and self.mapcache[0] == node:
79 if self.mapcache and self.mapcache[0] == node:
80 return self.mapcache[1]
80 return self.mapcache[1]
81 text = self.revision(node)
81 text = self.revision(node)
82 map = {}
82 map = {}
83 self.listcache = (text, text.splitlines(1))
83 self.listcache = (text, text.splitlines(1))
84 for l in self.listcache[1]:
84 for l in self.listcache[1]:
85 (f, n) = l.split('\0')
85 (f, n) = l.split('\0')
86 map[f] = bin(n[:40])
86 map[f] = bin(n[:40])
87 self.mapcache = (node, map)
87 self.mapcache = (node, map)
88 return map
88 return map
89
89
90 def diff(self, a, b):
90 def diff(self, a, b):
91 # this is sneaky, as we're not actually using a and b
91 # this is sneaky, as we're not actually using a and b
92 if self.listcache and len(self.listcache[0]) == len(a):
92 if self.listcache and len(self.listcache[0]) == len(a):
93 return mdiff.diff(self.listcache[1], self.addlist, 1)
93 return mdiff.diff(self.listcache[1], self.addlist, 1)
94 else:
94 else:
95 return mdiff.diff(a, b)
95 return mdiff.diff(a, b)
96
96
97 def add(self, map, transaction, link, p1=None, p2=None):
97 def add(self, map, transaction, link, p1=None, p2=None):
98 files = map.keys()
98 files = map.keys()
99 files.sort()
99 files.sort()
100
100
101 self.addlist = ["%s\000%s\n" % (f, hex(map[f])) for f in files]
101 self.addlist = ["%s\000%s\n" % (f, hex(map[f])) for f in files]
102 text = "".join(self.addlist)
102 text = "".join(self.addlist)
103
103
104 n = self.addrevision(text, transaction, link, p1, p2)
104 n = self.addrevision(text, transaction, link, p1, p2)
105 self.mapcache = (n, map)
105 self.mapcache = (n, map)
106 self.listcache = (text, self.addlist)
106 self.listcache = (text, self.addlist)
107
107
108 return n
108 return n
109
109
110 class changelog(revlog):
110 class changelog(revlog):
111 def __init__(self, opener):
111 def __init__(self, opener):
112 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
112 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
113
113
114 def extract(self, text):
114 def extract(self, text):
115 last = text.index("\n\n")
115 last = text.index("\n\n")
116 desc = text[last + 2:]
116 desc = text[last + 2:]
117 l = text[:last].splitlines()
117 l = text[:last].splitlines()
118 manifest = bin(l[0])
118 manifest = bin(l[0])
119 user = l[1]
119 user = l[1]
120 date = l[2]
120 date = l[2]
121 files = l[3:]
121 files = l[3:]
122 return (manifest, user, date, files, desc)
122 return (manifest, user, date, files, desc)
123
123
124 def read(self, node):
124 def read(self, node):
125 return self.extract(self.revision(node))
125 return self.extract(self.revision(node))
126
126
127 def add(self, manifest, list, desc, transaction, p1=None, p2=None):
127 def add(self, manifest, list, desc, transaction, p1=None, p2=None):
128 try: user = os.environ["HGUSER"]
128 try: user = os.environ["HGUSER"]
129 except: user = os.environ["LOGNAME"] + '@' + socket.getfqdn()
129 except: user = os.environ["LOGNAME"] + '@' + socket.getfqdn()
130 date = "%d %d" % (time.time(), time.timezone)
130 date = "%d %d" % (time.time(), time.timezone)
131 list.sort()
131 list.sort()
132 l = [hex(manifest), user, date] + list + ["", desc]
132 l = [hex(manifest), user, date] + list + ["", desc]
133 text = "\n".join(l)
133 text = "\n".join(l)
134 return self.addrevision(text, transaction, self.count(), p1, p2)
134 return self.addrevision(text, transaction, self.count(), p1, p2)
135
135
136 def merge3(self, my, other, base):
136 def merge3(self, my, other, base):
137 pass
137 pass
138
138
139 class dircache:
139 class dircache:
140 def __init__(self, opener, ui):
140 def __init__(self, opener, ui):
141 self.opener = opener
141 self.opener = opener
142 self.dirty = 0
142 self.dirty = 0
143 self.ui = ui
143 self.ui = ui
144 self.map = None
144 self.map = None
145 def __del__(self):
145 def __del__(self):
146 if self.dirty: self.write()
146 if self.dirty: self.write()
147 def __getitem__(self, key):
147 def __getitem__(self, key):
148 try:
148 try:
149 return self.map[key]
149 return self.map[key]
150 except TypeError:
150 except TypeError:
151 self.read()
151 self.read()
152 return self[key]
152 return self[key]
153
153
154 def read(self):
154 def read(self):
155 if self.map is not None: return self.map
155 if self.map is not None: return self.map
156
156
157 self.map = {}
157 self.map = {}
158 try:
158 try:
159 st = self.opener("dircache").read()
159 st = self.opener("dircache").read()
160 except: return
160 except: return
161
161
162 pos = 0
162 pos = 0
163 while pos < len(st):
163 while pos < len(st):
164 e = struct.unpack(">llll", st[pos:pos+16])
164 e = struct.unpack(">llll", st[pos:pos+16])
165 l = e[3]
165 l = e[3]
166 pos += 16
166 pos += 16
167 f = st[pos:pos + l]
167 f = st[pos:pos + l]
168 self.map[f] = e[:3]
168 self.map[f] = e[:3]
169 pos += l
169 pos += l
170
170
171 def update(self, files):
171 def update(self, files):
172 if not files: return
172 if not files: return
173 self.read()
173 self.read()
174 self.dirty = 1
174 self.dirty = 1
175 for f in files:
175 for f in files:
176 try:
176 try:
177 s = os.stat(f)
177 s = os.stat(f)
178 self.map[f] = (s.st_mode, s.st_size, s.st_mtime)
178 self.map[f] = (s.st_mode, s.st_size, s.st_mtime)
179 except IOError:
179 except IOError:
180 self.remove(f)
180 self.remove(f)
181
181
182 def taint(self, files):
182 def taint(self, files):
183 if not files: return
183 if not files: return
184 self.read()
184 self.read()
185 self.dirty = 1
185 self.dirty = 1
186 for f in files:
186 for f in files:
187 self.map[f] = (0, -1, 0)
187 self.map[f] = (0, -1, 0)
188
188
189 def remove(self, files):
189 def remove(self, files):
190 if not files: return
190 if not files: return
191 self.read()
191 self.read()
192 self.dirty = 1
192 self.dirty = 1
193 for f in files:
193 for f in files:
194 try:
194 try:
195 del self.map[f]
195 del self.map[f]
196 except KeyError:
196 except KeyError:
197 self.ui.warn("Not in dircache: %s\n" % f)
197 self.ui.warn("Not in dircache: %s\n" % f)
198 pass
198 pass
199
199
200 def clear(self):
200 def clear(self):
201 self.map = {}
201 self.map = {}
202 self.dirty = 1
202 self.dirty = 1
203
203
204 def write(self):
204 def write(self):
205 st = self.opener("dircache", "w")
205 st = self.opener("dircache", "w")
206 for f, e in self.map.items():
206 for f, e in self.map.items():
207 e = struct.pack(">llll", e[0], e[1], e[2], len(f))
207 e = struct.pack(">llll", e[0], e[1], e[2], len(f))
208 st.write(e + f)
208 st.write(e + f)
209 self.dirty = 0
209 self.dirty = 0
210
210
211 def copy(self):
211 def copy(self):
212 self.read()
212 self.read()
213 return self.map.copy()
213 return self.map.copy()
214
214
215 # used to avoid circular references so destructors work
215 # used to avoid circular references so destructors work
216 def opener(base):
216 def opener(base):
217 p = base
217 p = base
218 def o(path, mode="r"):
218 def o(path, mode="r"):
219 if p[:7] == "http://":
219 if p[:7] == "http://":
220 f = os.path.join(p, urllib.quote(path))
220 f = os.path.join(p, urllib.quote(path))
221 return httprangereader(f)
221 return httprangereader(f)
222
222
223 f = os.path.join(p, path)
223 f = os.path.join(p, path)
224
224
225 if mode != "r" and os.path.isfile(f):
225 if mode != "r" and os.path.isfile(f):
226 s = os.stat(f)
226 s = os.stat(f)
227 if s.st_nlink > 1:
227 if s.st_nlink > 1:
228 file(f + ".tmp", "w").write(file(f).read())
228 file(f + ".tmp", "w").write(file(f).read())
229 os.rename(f+".tmp", f)
229 os.rename(f+".tmp", f)
230
230
231 return file(f, mode)
231 return file(f, mode)
232
232
233 return o
233 return o
234
234
235 class repository:
235 class repository:
236 def __init__(self, ui, path=None, create=0):
236 def __init__(self, ui, path=None, create=0):
237 self.remote = 0
237 self.remote = 0
238 if path and path[:7] == "http://":
238 if path and path[:7] == "http://":
239 self.remote = 1
239 self.remote = 1
240 self.path = path
240 self.path = path
241 else:
241 else:
242 if not path:
242 if not path:
243 p = os.getcwd()
243 p = os.getcwd()
244 while not os.path.isdir(os.path.join(p, ".hg")):
244 while not os.path.isdir(os.path.join(p, ".hg")):
245 p = os.path.dirname(p)
245 p = os.path.dirname(p)
246 if p == "/": raise "No repo found"
246 if p == "/": raise "No repo found"
247 path = p
247 path = p
248 self.path = os.path.join(path, ".hg")
248 self.path = os.path.join(path, ".hg")
249
249
250 self.root = path
250 self.root = path
251 self.ui = ui
251 self.ui = ui
252
252
253 if create:
253 if create:
254 os.mkdir(self.path)
254 os.mkdir(self.path)
255 os.mkdir(self.join("data"))
255 os.mkdir(self.join("data"))
256
256
257 self.opener = opener(self.path)
257 self.opener = opener(self.path)
258 self.manifest = manifest(self.opener)
258 self.manifest = manifest(self.opener)
259 self.changelog = changelog(self.opener)
259 self.changelog = changelog(self.opener)
260 self.ignorelist = None
260 self.ignorelist = None
261
261
262 if not self.remote:
262 if not self.remote:
263 self.dircache = dircache(self.opener, ui)
263 self.dircache = dircache(self.opener, ui)
264 try:
264 try:
265 self.current = bin(self.opener("current").read())
265 self.current = bin(self.opener("current").read())
266 except IOError:
266 except IOError:
267 self.current = None
267 self.current = None
268
268
269 def setcurrent(self, node):
269 def setcurrent(self, node):
270 self.current = node
270 self.current = node
271 self.opener("current", "w").write(hex(node))
271 self.opener("current", "w").write(hex(node))
272
272
273 def ignore(self, f):
273 def ignore(self, f):
274 if self.ignorelist is None:
274 if self.ignorelist is None:
275 self.ignorelist = []
275 self.ignorelist = []
276 try:
276 try:
277 l = open(os.path.join(self.root, ".hgignore")).readlines()
277 l = open(os.path.join(self.root, ".hgignore")).readlines()
278 for pat in l:
278 for pat in l:
279 if pat != "\n":
279 if pat != "\n":
280 self.ignorelist.append(re.compile(pat[:-1]))
280 self.ignorelist.append(re.compile(pat[:-1]))
281 except IOError: pass
281 except IOError: pass
282 for pat in self.ignorelist:
282 for pat in self.ignorelist:
283 if pat.search(f): return True
283 if pat.search(f): return True
284 return False
284 return False
285
285
286 def join(self, f):
286 def join(self, f):
287 return os.path.join(self.path, f)
287 return os.path.join(self.path, f)
288
288
289 def file(self, f):
289 def file(self, f):
290 return filelog(self.opener, f)
290 return filelog(self.opener, f)
291
291
292 def transaction(self):
292 def transaction(self):
293 return transaction(self.opener, self.join("journal"))
293 return transaction(self.opener, self.join("journal"))
294
294
295 def merge(self, other):
295 def merge(self, other):
296 tr = self.transaction()
296 tr = self.transaction()
297 changed = {}
297 changed = {}
298 new = {}
298 new = {}
299 seqrev = self.changelog.count()
299 seqrev = self.changelog.count()
300 # some magic to allow fiddling in nested scope
300 # some magic to allow fiddling in nested scope
301 nextrev = [seqrev]
301 nextrev = [seqrev]
302
302
303 # helpers for back-linking file revisions to local changeset
303 # helpers for back-linking file revisions to local changeset
304 # revisions so we can immediately get to changeset from annotate
304 # revisions so we can immediately get to changeset from annotate
305 def accumulate(text):
305 def accumulate(text):
306 # track which files are added in which changeset and the
306 # track which files are added in which changeset and the
307 # corresponding _local_ changeset revision
307 # corresponding _local_ changeset revision
308 files = self.changelog.extract(text)[3]
308 files = self.changelog.extract(text)[3]
309 for f in files:
309 for f in files:
310 changed.setdefault(f, []).append(nextrev[0])
310 changed.setdefault(f, []).append(nextrev[0])
311 nextrev[0] += 1
311 nextrev[0] += 1
312
312
313 def seq(start):
313 def seq(start):
314 while 1:
314 while 1:
315 yield start
315 yield start
316 start += 1
316 start += 1
317
317
318 def lseq(l):
318 def lseq(l):
319 for r in l:
319 for r in l:
320 yield r
320 yield r
321
321
322 # begin the import/merge of changesets
322 # begin the import/merge of changesets
323 self.ui.status("merging new changesets\n")
323 self.ui.status("merging new changesets\n")
324 (co, cn) = self.changelog.mergedag(other.changelog, tr,
324 (co, cn) = self.changelog.mergedag(other.changelog, tr,
325 seq(seqrev), accumulate)
325 seq(seqrev), accumulate)
326 resolverev = self.changelog.count()
326 resolverev = self.changelog.count()
327
327
328 # is there anything to do?
328 # is there anything to do?
329 if co == cn:
329 if co == cn:
330 tr.close()
330 tr.close()
331 return
331 return
332
332
333 # do we need to resolve?
333 # do we need to resolve?
334 simple = (co == self.changelog.ancestor(co, cn))
334 simple = (co == self.changelog.ancestor(co, cn))
335
335
336 # merge all files changed by the changesets,
336 # merge all files changed by the changesets,
337 # keeping track of the new tips
337 # keeping track of the new tips
338 changelist = changed.keys()
338 changelist = changed.keys()
339 changelist.sort()
339 changelist.sort()
340 for f in changelist:
340 for f in changelist:
341 sys.stdout.write(".")
341 sys.stdout.write(".")
342 sys.stdout.flush()
342 sys.stdout.flush()
343 r = self.file(f)
343 r = self.file(f)
344 node = r.merge(other.file(f), tr, lseq(changed[f]), resolverev)
344 node = r.merge(other.file(f), tr, lseq(changed[f]), resolverev)
345 if node:
345 if node:
346 new[f] = node
346 new[f] = node
347 sys.stdout.write("\n")
347 sys.stdout.write("\n")
348
348
349 # begin the merge of the manifest
349 # begin the merge of the manifest
350 self.ui.status("merging manifests\n")
350 self.ui.status("merging manifests\n")
351 (mm, mo) = self.manifest.mergedag(other.manifest, tr, seq(seqrev))
351 (mm, mo) = self.manifest.mergedag(other.manifest, tr, seq(seqrev))
352
352
353 # For simple merges, we don't need to resolve manifests or changesets
353 # For simple merges, we don't need to resolve manifests or changesets
354 if simple:
354 if simple:
355 tr.close()
355 tr.close()
356 return
356 return
357
357
358 ma = self.manifest.ancestor(mm, mo)
358 ma = self.manifest.ancestor(mm, mo)
359
359
360 # resolve the manifest to point to all the merged files
360 # resolve the manifest to point to all the merged files
361 self.ui.status("resolving manifests\n")
361 self.ui.status("resolving manifests\n")
362 mmap = self.manifest.read(mm) # mine
362 mmap = self.manifest.read(mm) # mine
363 omap = self.manifest.read(mo) # other
363 omap = self.manifest.read(mo) # other
364 amap = self.manifest.read(ma) # ancestor
364 amap = self.manifest.read(ma) # ancestor
365 nmap = {}
365 nmap = {}
366
366
367 for f, mid in mmap.iteritems():
367 for f, mid in mmap.iteritems():
368 if f in omap:
368 if f in omap:
369 if mid != omap[f]:
369 if mid != omap[f]:
370 nmap[f] = new.get(f, mid) # use merged version
370 nmap[f] = new.get(f, mid) # use merged version
371 else:
371 else:
372 nmap[f] = new.get(f, mid) # they're the same
372 nmap[f] = new.get(f, mid) # they're the same
373 del omap[f]
373 del omap[f]
374 elif f in amap:
374 elif f in amap:
375 if mid != amap[f]:
375 if mid != amap[f]:
376 pass # we should prompt here
376 pass # we should prompt here
377 else:
377 else:
378 pass # other deleted it
378 pass # other deleted it
379 else:
379 else:
380 nmap[f] = new.get(f, mid) # we created it
380 nmap[f] = new.get(f, mid) # we created it
381
381
382 del mmap
382 del mmap
383
383
384 for f, oid in omap.iteritems():
384 for f, oid in omap.iteritems():
385 if f in amap:
385 if f in amap:
386 if oid != amap[f]:
386 if oid != amap[f]:
387 pass # this is the nasty case, we should prompt
387 pass # this is the nasty case, we should prompt
388 else:
388 else:
389 pass # probably safe
389 pass # probably safe
390 else:
390 else:
391 nmap[f] = new.get(f, oid) # remote created it
391 nmap[f] = new.get(f, oid) # remote created it
392
392
393 del omap
393 del omap
394 del amap
394 del amap
395
395
396 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
396 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
397
397
398 # Now all files and manifests are merged, we add the changed files
398 # Now all files and manifests are merged, we add the changed files
399 # and manifest id to the changelog
399 # and manifest id to the changelog
400 self.ui.status("committing merge changeset\n")
400 self.ui.status("committing merge changeset\n")
401 new = new.keys()
401 new = new.keys()
402 new.sort()
402 new.sort()
403 if co == cn: cn = -1
403 if co == cn: cn = -1
404
404
405 edittext = "\n"+"".join(["HG: changed %s\n" % f for f in new])
405 edittext = "\n"+"".join(["HG: changed %s\n" % f for f in new])
406 edittext = self.ui.edit(edittext)
406 edittext = self.ui.edit(edittext)
407 n = self.changelog.add(node, new, edittext, tr, co, cn)
407 n = self.changelog.add(node, new, edittext, tr, co, cn)
408
408
409 tr.close()
409 tr.close()
410
410
411 def commit(self, parent, update = None, text = ""):
411 def commit(self, parent, update = None, text = ""):
412 tr = self.transaction()
412 tr = self.transaction()
413
413
414 try:
414 try:
415 remove = [ l[:-1] for l in self.opener("to-remove") ]
415 remove = [ l[:-1] for l in self.opener("to-remove") ]
416 os.unlink(self.join("to-remove"))
416 os.unlink(self.join("to-remove"))
417
417
418 except IOError:
418 except IOError:
419 remove = []
419 remove = []
420
420
421 if update == None:
421 if update == None:
422 update = self.diffdir(self.root, parent)[0]
422 update = self.diffdir(self.root, parent)[0]
423
423
424 # check in files
424 # check in files
425 new = {}
425 new = {}
426 linkrev = self.changelog.count()
426 linkrev = self.changelog.count()
427 for f in update:
427 for f in update:
428 try:
428 try:
429 t = file(f).read()
429 t = file(f).read()
430 except IOError:
430 except IOError:
431 remove.append(f)
431 remove.append(f)
432 continue
432 continue
433 r = self.file(f)
433 r = self.file(f)
434 new[f] = r.add(t, tr, linkrev)
434 new[f] = r.add(t, tr, linkrev)
435
435
436 # update manifest
436 # update manifest
437 mmap = self.manifest.read(self.manifest.tip())
437 mmap = self.manifest.read(self.manifest.tip())
438 mmap.update(new)
438 mmap.update(new)
439 for f in remove:
439 for f in remove:
440 del mmap[f]
440 del mmap[f]
441 mnode = self.manifest.add(mmap, tr, linkrev)
441 mnode = self.manifest.add(mmap, tr, linkrev)
442
442
443 # add changeset
443 # add changeset
444 new = new.keys()
444 new = new.keys()
445 new.sort()
445 new.sort()
446
446
447 edittext = text + "\n"+"".join(["HG: changed %s\n" % f for f in new])
447 edittext = text + "\n"+"".join(["HG: changed %s\n" % f for f in new])
448 edittext += "".join(["HG: removed %s\n" % f for f in remove])
448 edittext += "".join(["HG: removed %s\n" % f for f in remove])
449 edittext = self.ui.edit(edittext)
449 edittext = self.ui.edit(edittext)
450
450
451 n = self.changelog.add(mnode, new, edittext, tr)
451 n = self.changelog.add(mnode, new, edittext, tr)
452 tr.close()
452 tr.close()
453
453
454 self.setcurrent(n)
454 self.setcurrent(n)
455 self.dircache.update(new)
455 self.dircache.update(new)
456 self.dircache.remove(remove)
456 self.dircache.remove(remove)
457
457
458 def checkdir(self, path):
458 def checkdir(self, path):
459 d = os.path.dirname(path)
459 d = os.path.dirname(path)
460 if not d: return
460 if not d: return
461 if not os.path.isdir(d):
461 if not os.path.isdir(d):
462 self.checkdir(d)
462 self.checkdir(d)
463 os.mkdir(d)
463 os.mkdir(d)
464
464
465 def checkout(self, node):
465 def checkout(self, node):
466 # checkout is really dumb at the moment
466 # checkout is really dumb at the moment
467 # it ought to basically merge
467 # it ought to basically merge
468 change = self.changelog.read(node)
468 change = self.changelog.read(node)
469 mmap = self.manifest.read(change[0])
469 mmap = self.manifest.read(change[0])
470
470
471 l = mmap.keys()
471 l = mmap.keys()
472 l.sort()
472 l.sort()
473 stats = []
473 stats = []
474 for f in l:
474 for f in l:
475 r = self.file(f)
475 r = self.file(f)
476 t = r.revision(mmap[f])
476 t = r.revision(mmap[f])
477 try:
477 try:
478 file(f, "w").write(t)
478 file(f, "w").write(t)
479 except:
479 except:
480 self.checkdir(f)
480 self.checkdir(f)
481 file(f, "w").write(t)
481 file(f, "w").write(t)
482
482
483 self.setcurrent(node)
483 self.setcurrent(node)
484 self.dircache.clear()
484 self.dircache.clear()
485 self.dircache.update(l)
485 self.dircache.update(l)
486
486
487 def diffdir(self, path, changeset):
487 def diffdir(self, path, changeset):
488 changed = []
488 changed = []
489 mf = {}
489 mf = {}
490 added = []
490 added = []
491
491
492 if changeset:
492 if changeset:
493 change = self.changelog.read(changeset)
493 change = self.changelog.read(changeset)
494 mf = self.manifest.read(change[0])
494 mf = self.manifest.read(change[0])
495
495
496 if changeset == self.current:
496 if changeset == self.current:
497 dc = self.dircache.copy()
497 dc = self.dircache.copy()
498 else:
498 else:
499 dc = dict.fromkeys(mf)
499 dc = dict.fromkeys(mf)
500
500
501 def fcmp(fn):
501 def fcmp(fn):
502 t1 = file(fn).read()
502 t1 = file(fn).read()
503 t2 = self.file(fn).revision(mf[fn])
503 t2 = self.file(fn).revision(mf[fn])
504 return cmp(t1, t2)
504 return cmp(t1, t2)
505
505
506 for dir, subdirs, files in os.walk(self.root):
506 for dir, subdirs, files in os.walk(self.root):
507 d = dir[len(self.root)+1:]
507 d = dir[len(self.root)+1:]
508 if ".hg" in subdirs: subdirs.remove(".hg")
508 if ".hg" in subdirs: subdirs.remove(".hg")
509
509
510 for f in files:
510 for f in files:
511 fn = os.path.join(d, f)
511 fn = os.path.join(d, f)
512 try: s = os.stat(fn)
512 try: s = os.stat(fn)
513 except: continue
513 except: continue
514 if fn in dc:
514 if fn in dc:
515 c = dc[fn]
515 c = dc[fn]
516 del dc[fn]
516 del dc[fn]
517 if not c:
517 if not c:
518 if fcmp(fn):
518 if fcmp(fn):
519 changed.append(fn)
519 changed.append(fn)
520 if c[1] != s.st_size:
520 elif c[1] != s.st_size:
521 changed.append(fn)
521 changed.append(fn)
522 elif c[0] != s.st_mode or c[2] != s.st_mtime:
522 elif c[0] != s.st_mode or c[2] != s.st_mtime:
523 if fcmp(fn):
523 if fcmp(fn):
524 changed.append(fn)
524 changed.append(fn)
525 else:
525 else:
526 if self.ignore(fn): continue
526 if self.ignore(fn): continue
527 added.append(fn)
527 added.append(fn)
528
528
529 deleted = dc.keys()
529 deleted = dc.keys()
530 deleted.sort()
530 deleted.sort()
531
531
532 return (changed, added, deleted)
532 return (changed, added, deleted)
533
533
534 def diffrevs(self, node1, node2):
534 def diffrevs(self, node1, node2):
535 changed, added = [], [], []
535 changed, added = [], []
536
536
537 change = self.changelog.read(node1)
537 change = self.changelog.read(node1)
538 mf1 = self.manifest.read(change[0])
538 mf1 = self.manifest.read(change[0])
539 change = self.changelog.read(revs[1])
539 change = self.changelog.read(node2)
540 mf2 = self.manifest.read(change[0])
540 mf2 = self.manifest.read(change[0])
541
541
542 for fn in mf2:
542 for fn in mf2:
543 if mf1.has_key(fn):
543 if mf1.has_key(fn):
544 if mf1[fn] != mf2[fn]:
544 if mf1[fn] != mf2[fn]:
545 changed.append(fn)
545 changed.append(fn)
546 del mf1[fn]
546 del mf1[fn]
547 else:
547 else:
548 added.append(fn)
548 added.append(fn)
549
549
550 deleted = mf1.keys()
550 deleted = mf1.keys()
551 deleted.sort()
551 deleted.sort()
552
552
553 return (changed, added, deleted)
553 return (changed, added, deleted)
554
554
555 def add(self, list):
555 def add(self, list):
556 self.dircache.taint(list)
556 self.dircache.taint(list)
557
557
558 def remove(self, list):
558 def remove(self, list):
559 dl = self.opener("to-remove", "a")
559 dl = self.opener("to-remove", "a")
560 for f in list:
560 for f in list:
561 dl.write(f + "\n")
561 dl.write(f + "\n")
562
562
563 class ui:
563 class ui:
564 def __init__(self, verbose=False, debug=False):
564 def __init__(self, verbose=False, debug=False):
565 self.verbose = verbose
565 self.verbose = verbose
566 def write(self, *args):
566 def write(self, *args):
567 for a in args:
567 for a in args:
568 sys.stdout.write(str(a))
568 sys.stdout.write(str(a))
569 def prompt(self, msg, pat):
569 def prompt(self, msg, pat):
570 while 1:
570 while 1:
571 sys.stdout.write(msg)
571 sys.stdout.write(msg)
572 r = sys.stdin.readline()[:-1]
572 r = sys.stdin.readline()[:-1]
573 if re.match(pat, r):
573 if re.match(pat, r):
574 return r
574 return r
575 def status(self, *msg):
575 def status(self, *msg):
576 self.write(*msg)
576 self.write(*msg)
577 def warn(self, msg):
577 def warn(self, msg):
578 self.write(*msg)
578 self.write(*msg)
579 def note(self, msg):
579 def note(self, msg):
580 if self.verbose: self.write(*msg)
580 if self.verbose: self.write(*msg)
581 def debug(self, msg):
581 def debug(self, msg):
582 if self.debug: self.write(*msg)
582 if self.debug: self.write(*msg)
583 def edit(self, text):
583 def edit(self, text):
584 (fd, name) = tempfile.mkstemp("hg")
584 (fd, name) = tempfile.mkstemp("hg")
585 f = os.fdopen(fd, "w")
585 f = os.fdopen(fd, "w")
586 f.write(text)
586 f.write(text)
587 f.close()
587 f.close()
588
588
589 editor = os.environ.get("EDITOR", "vi")
589 editor = os.environ.get("EDITOR", "vi")
590 r = os.system("%s %s" % (editor, name))
590 r = os.system("%s %s" % (editor, name))
591 if r:
591 if r:
592 raise "Edit failed!"
592 raise "Edit failed!"
593
593
594 t = open(name).read()
594 t = open(name).read()
595 t = re.sub("(?m)^HG:.*\n", "", t)
595 t = re.sub("(?m)^HG:.*\n", "", t)
596
596
597 return t
597 return t
598
598
599
599
600 class httprangereader:
600 class httprangereader:
601 def __init__(self, url):
601 def __init__(self, url):
602 self.url = url
602 self.url = url
603 self.pos = 0
603 self.pos = 0
604 def seek(self, pos):
604 def seek(self, pos):
605 self.pos = pos
605 self.pos = pos
606 def read(self, bytes=None):
606 def read(self, bytes=None):
607 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
607 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
608 urllib2.install_opener(opener)
608 urllib2.install_opener(opener)
609 req = urllib2.Request(self.url)
609 req = urllib2.Request(self.url)
610 end = ''
610 end = ''
611 if bytes: end = self.pos + bytes
611 if bytes: end = self.pos + bytes
612 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
612 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
613 f = urllib2.urlopen(req)
613 f = urllib2.urlopen(req)
614 return f.read()
614 return f.read()
General Comments 0
You need to be logged in to leave comments. Login now