##// END OF EJS Templates
Add basic annotation support...
mpm@selenic.com -
r79:837d473d default
parent child Browse files
Show More
@@ -1,410 +1,425 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.4f "jane dark"
4 # v0.4f "jane dark"
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 a bit faster
11 # the psyco compiler makes commits a bit faster
12 # and makes changegroup merge about 20 times slower!
12 # and makes changegroup merge about 20 times slower!
13 # try:
13 # try:
14 # import psyco
14 # import psyco
15 # psyco.full()
15 # psyco.full()
16 # except:
16 # except:
17 # pass
17 # pass
18
18
19 import sys, os, time
19 import sys, os, time
20 from mercurial import hg, mdiff, fancyopts
20 from mercurial import hg, mdiff, fancyopts
21
21
22 def help():
22 def help():
23 print """\
23 print """\
24 commands:
24 commands:
25
25
26 init create a new repository in this directory
26 init create a new repository in this directory
27 branch <path> create a branch of <path> in this directory
27 branch <path> create a branch of <path> in this directory
28 merge <path> merge changes from <path> into local repository
28 merge <path> merge changes from <path> into local repository
29 checkout [changeset] checkout the latest or given changeset
29 checkout [changeset] checkout the latest or given changeset
30 status show new, missing, and changed files in working dir
30 status show new, missing, and changed files in working dir
31 add [files...] add the given files in the next commit
31 add [files...] add the given files in the next commit
32 remove [files...] remove the given files in the next commit
32 remove [files...] remove the given files in the next commit
33 addremove add all new files, delete all missing files
33 addremove add all new files, delete all missing files
34 commit commit all changes to the repository
34 commit commit all changes to the repository
35 history show changeset history
35 history show changeset history
36 log <file> show revision history of a single file
36 log <file> show revision history of a single file
37 dump <file> [rev] dump the latest or given revision of a file
37 dump <file> [rev] dump the latest or given revision of a file
38 dumpmanifest [rev] dump the latest or given revision of the manifest
38 dumpmanifest [rev] dump the latest or given revision of the manifest
39 diff [files...] diff working directory (or selected files)
39 diff [files...] diff working directory (or selected files)
40 tags show current changeset tags
40 tags show current changeset tags
41 """
41 """
42
42
43 def filterfiles(list, files):
43 def filterfiles(list, files):
44 l = [ x for x in list if x in files ]
44 l = [ x for x in list if x in files ]
45
45
46 for f in files:
46 for f in files:
47 if f[-1] != os.sep: f += os.sep
47 if f[-1] != os.sep: f += os.sep
48 l += [ x for x in list if x.startswith(f) ]
48 l += [ x for x in list if x.startswith(f) ]
49 return l
49 return l
50
50
51 def diff(files = None, node1 = None, node2 = None):
51 def diff(files = None, node1 = None, node2 = None):
52 def date(c):
52 def date(c):
53 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
53 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
54
54
55 if node2:
55 if node2:
56 change = repo.changelog.read(node2)
56 change = repo.changelog.read(node2)
57 mmap2 = repo.manifest.read(change[0])
57 mmap2 = repo.manifest.read(change[0])
58 (c, a, d) = repo.diffrevs(node1, node2)
58 (c, a, d) = repo.diffrevs(node1, node2)
59 def read(f): return repo.file(f).read(mmap2[f])
59 def read(f): return repo.file(f).read(mmap2[f])
60 date2 = date(change)
60 date2 = date(change)
61 else:
61 else:
62 date2 = time.asctime()
62 date2 = time.asctime()
63 if not node1:
63 if not node1:
64 node1 = repo.current
64 node1 = repo.current
65 (c, a, d) = repo.diffdir(repo.root, node1)
65 (c, a, d) = repo.diffdir(repo.root, node1)
66 def read(f): return file(os.path.join(repo.root, f)).read()
66 def read(f): return file(os.path.join(repo.root, f)).read()
67
67
68 change = repo.changelog.read(node1)
68 change = repo.changelog.read(node1)
69 mmap = repo.manifest.read(change[0])
69 mmap = repo.manifest.read(change[0])
70 date1 = date(change)
70 date1 = date(change)
71
71
72 if files:
72 if files:
73 (c, a, d) = map(lambda x: filterfiles(x, files), (c, a, d))
73 (c, a, d) = map(lambda x: filterfiles(x, files), (c, a, d))
74
74
75 for f in c:
75 for f in c:
76 to = repo.file(f).read(mmap[f])
76 to = repo.file(f).read(mmap[f])
77 tn = read(f)
77 tn = read(f)
78 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
78 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
79 for f in a:
79 for f in a:
80 to = ""
80 to = ""
81 tn = read(f)
81 tn = read(f)
82 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
82 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
83 for f in d:
83 for f in d:
84 to = repo.file(f).read(mmap[f])
84 to = repo.file(f).read(mmap[f])
85 tn = ""
85 tn = ""
86 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
86 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f))
87
87
88 options = {}
88 options = {}
89 opts = [('v', 'verbose', None, 'verbose'),
89 opts = [('v', 'verbose', None, 'verbose'),
90 ('d', 'debug', None, 'debug')]
90 ('d', 'debug', None, 'debug')]
91
91
92 args = fancyopts.fancyopts(sys.argv[1:], opts, options,
92 args = fancyopts.fancyopts(sys.argv[1:], opts, options,
93 'hg [options] <command> [command options] [files]')
93 'hg [options] <command> [command options] [files]')
94
94
95 try:
95 try:
96 cmd = args[0]
96 cmd = args[0]
97 args = args[1:]
97 args = args[1:]
98 except:
98 except:
99 cmd = ""
99 cmd = ""
100
100
101 ui = hg.ui(options["verbose"], options["debug"])
101 ui = hg.ui(options["verbose"], options["debug"])
102
102
103 if cmd == "init":
103 if cmd == "init":
104 repo = hg.repository(ui, ".", create=1)
104 repo = hg.repository(ui, ".", create=1)
105 sys.exit(0)
105 sys.exit(0)
106 elif cmd == "branch" or cmd == "clone":
106 elif cmd == "branch" or cmd == "clone":
107 os.system("cp -al %s/.hg .hg" % args[0])
107 os.system("cp -al %s/.hg .hg" % args[0])
108 sys.exit(0)
108 sys.exit(0)
109 elif cmd == "help":
109 elif cmd == "help":
110 help()
110 help()
111 sys.exit(0)
111 sys.exit(0)
112 else:
112 else:
113 try:
113 try:
114 repo = hg.repository(ui=ui)
114 repo = hg.repository(ui=ui)
115 except IOError:
115 except IOError:
116 print "Unable to open repository"
116 print "Unable to open repository"
117 sys.exit(0)
117 sys.exit(0)
118
118
119 if cmd == "checkout" or cmd == "co":
119 if cmd == "checkout" or cmd == "co":
120 node = repo.changelog.tip()
120 node = repo.changelog.tip()
121 if args:
121 if args:
122 node = repo.lookup(args[0])
122 node = repo.lookup(args[0])
123 repo.checkout(node)
123 repo.checkout(node)
124
124
125 elif cmd == "add":
125 elif cmd == "add":
126 repo.add(args)
126 repo.add(args)
127
127
128 elif cmd == "remove" or cmd == "rm" or cmd == "del" or cmd == "delete":
128 elif cmd == "remove" or cmd == "rm" or cmd == "del" or cmd == "delete":
129 repo.remove(args)
129 repo.remove(args)
130
130
131 elif cmd == "commit" or cmd == "checkin" or cmd == "ci":
131 elif cmd == "commit" or cmd == "checkin" or cmd == "ci":
132 if 1:
132 if 1:
133 if len(args) > 0:
133 if len(args) > 0:
134 repo.commit(repo.current, args)
134 repo.commit(repo.current, args)
135 else:
135 else:
136 repo.commit(repo.current)
136 repo.commit(repo.current)
137
137
138 elif cmd == "import" or cmd == "patch":
138 elif cmd == "import" or cmd == "patch":
139 try:
139 try:
140 import psyco
140 import psyco
141 psyco.full()
141 psyco.full()
142 except:
142 except:
143 pass
143 pass
144
144
145 ioptions = {}
145 ioptions = {}
146 opts = [('p', 'strip', 1, 'path strip'),
146 opts = [('p', 'strip', 1, 'path strip'),
147 ('b', 'base', "", 'base path'),
147 ('b', 'base', "", 'base path'),
148 ('q', 'quiet', "", 'silence diff')
148 ('q', 'quiet', "", 'silence diff')
149 ]
149 ]
150
150
151 args = fancyopts.fancyopts(args, opts, ioptions,
151 args = fancyopts.fancyopts(args, opts, ioptions,
152 'hg import [options] <patch names>')
152 'hg import [options] <patch names>')
153 d = ioptions["base"]
153 d = ioptions["base"]
154 strip = ioptions["strip"]
154 strip = ioptions["strip"]
155 quiet = ioptions["quiet"] and "> /dev/null" or ""
155 quiet = ioptions["quiet"] and "> /dev/null" or ""
156
156
157 for patch in args:
157 for patch in args:
158 ui.status("applying %s\n" % patch)
158 ui.status("applying %s\n" % patch)
159 pf = os.path.join(d, patch)
159 pf = os.path.join(d, patch)
160
160
161 text = ""
161 text = ""
162 for l in file(pf):
162 for l in file(pf):
163 if l[:4] == "--- ": break
163 if l[:4] == "--- ": break
164 text += l
164 text += l
165
165
166 f = os.popen("lsdiff --strip %d %s" % (strip, pf))
166 f = os.popen("lsdiff --strip %d %s" % (strip, pf))
167 files = filter(None, map(lambda x: x.rstrip(), f.read().splitlines()))
167 files = filter(None, map(lambda x: x.rstrip(), f.read().splitlines()))
168 f.close()
168 f.close()
169
169
170 if files:
170 if files:
171 if os.system("patch -p%d < %s %s" % (strip, pf, quiet)):
171 if os.system("patch -p%d < %s %s" % (strip, pf, quiet)):
172 raise "patch failed!"
172 raise "patch failed!"
173 repo.commit(repo.current, files, text)
173 repo.commit(repo.current, files, text)
174
174
175 elif cmd == "status":
175 elif cmd == "status":
176 (c, a, d) = repo.diffdir(repo.root, repo.current)
176 (c, a, d) = repo.diffdir(repo.root, repo.current)
177 for f in c: print "C", f
177 for f in c: print "C", f
178 for f in a: print "?", f
178 for f in a: print "?", f
179 for f in d: print "R", f
179 for f in d: print "R", f
180
180
181 elif cmd == "diff":
181 elif cmd == "diff":
182 revs = []
182 revs = []
183
183
184 if args:
184 if args:
185 doptions = {}
185 doptions = {}
186 opts = [('r', 'revision', [], 'revision')]
186 opts = [('r', 'revision', [], 'revision')]
187 args = fancyopts.fancyopts(args, opts, doptions,
187 args = fancyopts.fancyopts(args, opts, doptions,
188 'hg diff [options] [files]')
188 'hg diff [options] [files]')
189 revs = map(lambda x: repo.lookup(x), doptions['revision'])
189 revs = map(lambda x: repo.lookup(x), doptions['revision'])
190
190
191 if len(revs) > 2:
191 if len(revs) > 2:
192 print "too many revisions to diff"
192 print "too many revisions to diff"
193 sys.exit(1)
193 sys.exit(1)
194
194
195 if os.getcwd() != repo.root:
195 if os.getcwd() != repo.root:
196 relpath = os.getcwd()[len(repo.root) + 1: ]
196 relpath = os.getcwd()[len(repo.root) + 1: ]
197 if not args: args = [ relpath ]
197 if not args: args = [ relpath ]
198 else: args = [ os.path.join(relpath, x) for x in args ]
198 else: args = [ os.path.join(relpath, x) for x in args ]
199
199
200 diff(args, *revs)
200 diff(args, *revs)
201
201
202 elif cmd == "annotate":
203 aoptions = {}
204 opts = [('r', 'revision', '', 'revision')]
205 args = fancyopts.fancyopts(args, opts, aoptions,
206 'hg annotate [-r id] [files]')
207 if args:
208 node = repo.current
209 if aoptions['revision']:
210 node = repo.changelog.lookup(aoptions['revision'])
211 change = repo.changelog.read(node)
212 mmap = repo.manifest.read(change[0])
213 for f in args:
214 for n, l in repo.file(f).annotate(mmap[f]):
215 sys.stdout.write("%s: %s"%(n, l))
216
202 elif cmd == "export":
217 elif cmd == "export":
203 node = repo.lookup(args[0])
218 node = repo.lookup(args[0])
204 prev, other = repo.changelog.parents(node)
219 prev, other = repo.changelog.parents(node)
205 change = repo.changelog.read(node)
220 change = repo.changelog.read(node)
206 print "# HG changeset patch"
221 print "# HG changeset patch"
207 print "# User %s" % change[1]
222 print "# User %s" % change[1]
208 print "# Node ID %s" % hg.hex(node)
223 print "# Node ID %s" % hg.hex(node)
209 print "# Parent %s" % hg.hex(prev)
224 print "# Parent %s" % hg.hex(prev)
210 print
225 print
211 if other != hg.nullid:
226 if other != hg.nullid:
212 print "# Parent %s" % hg.hex(other)
227 print "# Parent %s" % hg.hex(other)
213 print change[4]
228 print change[4]
214
229
215 diff(None, prev, node)
230 diff(None, prev, node)
216
231
217 elif cmd == "debugchangegroup":
232 elif cmd == "debugchangegroup":
218 newer = repo.newer(map(repo.lookup, args))
233 newer = repo.newer(map(repo.lookup, args))
219 for chunk in repo.changegroup(newer):
234 for chunk in repo.changegroup(newer):
220 sys.stdout.write(chunk)
235 sys.stdout.write(chunk)
221
236
222 elif cmd == "debugaddchangegroup":
237 elif cmd == "debugaddchangegroup":
223 data = sys.stdin.read()
238 data = sys.stdin.read()
224 repo.addchangegroup(data)
239 repo.addchangegroup(data)
225
240
226 elif cmd == "addremove":
241 elif cmd == "addremove":
227 (c, a, d) = repo.diffdir(repo.root, repo.current)
242 (c, a, d) = repo.diffdir(repo.root, repo.current)
228 repo.add(a)
243 repo.add(a)
229 repo.remove(d)
244 repo.remove(d)
230
245
231 elif cmd == "history":
246 elif cmd == "history":
232 for i in range(repo.changelog.count()):
247 for i in range(repo.changelog.count()):
233 n = repo.changelog.node(i)
248 n = repo.changelog.node(i)
234 changes = repo.changelog.read(n)
249 changes = repo.changelog.read(n)
235 (p1, p2) = repo.changelog.parents(n)
250 (p1, p2) = repo.changelog.parents(n)
236 (h, h1, h2) = map(hg.hex, (n, p1, p2))
251 (h, h1, h2) = map(hg.hex, (n, p1, p2))
237 (i1, i2) = map(repo.changelog.rev, (p1, p2))
252 (i1, i2) = map(repo.changelog.rev, (p1, p2))
238 print "rev: %4d:%s" % (i, h)
253 print "rev: %4d:%s" % (i, h)
239 print "parents: %4d:%s" % (i1, h1)
254 print "parents: %4d:%s" % (i1, h1)
240 if i2: print " %4d:%s" % (i2, h2)
255 if i2: print " %4d:%s" % (i2, h2)
241 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
256 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
242 hg.hex(changes[0]))
257 hg.hex(changes[0]))
243 print "user:", changes[1]
258 print "user:", changes[1]
244 print "date:", time.asctime(
259 print "date:", time.asctime(
245 time.localtime(float(changes[2].split(' ')[0])))
260 time.localtime(float(changes[2].split(' ')[0])))
246 print "files:", " ".join(changes[3])
261 print "files:", " ".join(changes[3])
247 print "description:"
262 print "description:"
248 print changes[4]
263 print changes[4]
249
264
250 elif cmd == "log":
265 elif cmd == "log":
251 if args:
266 if args:
252 r = repo.file(args[0])
267 r = repo.file(args[0])
253 for i in range(r.count()):
268 for i in range(r.count()):
254 n = r.node(i)
269 n = r.node(i)
255 (p1, p2) = r.parents(n)
270 (p1, p2) = r.parents(n)
256 (h, h1, h2) = map(hg.hex, (n, p1, p2))
271 (h, h1, h2) = map(hg.hex, (n, p1, p2))
257 (i1, i2) = map(r.rev, (p1, p2))
272 (i1, i2) = map(r.rev, (p1, p2))
258 cr = r.linkrev(n)
273 cr = r.linkrev(n)
259 cn = hg.hex(repo.changelog.node(cr))
274 cn = hg.hex(repo.changelog.node(cr))
260 print "rev: %4d:%s" % (i, h)
275 print "rev: %4d:%s" % (i, h)
261 print "changeset: %4d:%s" % (cr, cn)
276 print "changeset: %4d:%s" % (cr, cn)
262 print "parents: %4d:%s" % (i1, h1)
277 print "parents: %4d:%s" % (i1, h1)
263 if i2: print " %4d:%s" % (i2, h2)
278 if i2: print " %4d:%s" % (i2, h2)
264 else:
279 else:
265 print "missing filename"
280 print "missing filename"
266
281
267 elif cmd == "dump":
282 elif cmd == "dump":
268 if args:
283 if args:
269 r = repo.file(args[0])
284 r = repo.file(args[0])
270 n = r.tip()
285 n = r.tip()
271 if len(args) > 1: n = r.lookup(args[1])
286 if len(args) > 1: n = r.lookup(args[1])
272 sys.stdout.write(r.read(n))
287 sys.stdout.write(r.read(n))
273 else:
288 else:
274 print "missing filename"
289 print "missing filename"
275
290
276 elif cmd == "dumpmanifest":
291 elif cmd == "dumpmanifest":
277 n = repo.manifest.tip()
292 n = repo.manifest.tip()
278 if len(args) > 0:
293 if len(args) > 0:
279 n = repo.manifest.lookup(args[0])
294 n = repo.manifest.lookup(args[0])
280 m = repo.manifest.read(n)
295 m = repo.manifest.read(n)
281 files = m.keys()
296 files = m.keys()
282 files.sort()
297 files.sort()
283
298
284 for f in files:
299 for f in files:
285 print hg.hex(m[f]), f
300 print hg.hex(m[f]), f
286
301
287 elif cmd == "debughash":
302 elif cmd == "debughash":
288 f = repo.file(args[0])
303 f = repo.file(args[0])
289 print f.encodepath(args[0])
304 print f.encodepath(args[0])
290
305
291 elif cmd == "debugindex":
306 elif cmd == "debugindex":
292 r = hg.revlog(open, args[0], "")
307 r = hg.revlog(open, args[0], "")
293 print " rev offset length base linkrev"+\
308 print " rev offset length base linkrev"+\
294 " p1 p2 nodeid"
309 " p1 p2 nodeid"
295 for i in range(r.count()):
310 for i in range(r.count()):
296 e = r.index[i]
311 e = r.index[i]
297 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
312 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
298 i, e[0], e[1], e[2], e[3],
313 i, e[0], e[1], e[2], e[3],
299 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
314 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
300
315
301 elif cmd == "merge":
316 elif cmd == "merge":
302 if args:
317 if args:
303 other = hg.repository(ui, args[0])
318 other = hg.repository(ui, args[0])
304 print "requesting changegroup"
319 print "requesting changegroup"
305 cg = repo.getchangegroup(other)
320 cg = repo.getchangegroup(other)
306 repo.addchangegroup(cg)
321 repo.addchangegroup(cg)
307 else:
322 else:
308 print "missing source repository"
323 print "missing source repository"
309
324
310 elif cmd == "tags":
325 elif cmd == "tags":
311 repo.lookup(0) # prime the cache
326 repo.lookup(0) # prime the cache
312 i = repo.tags.items()
327 i = repo.tags.items()
313 i.sort()
328 i.sort()
314 for k, n in i:
329 for k, n in i:
315 try:
330 try:
316 r = repo.changelog.rev(n)
331 r = repo.changelog.rev(n)
317 except KeyError:
332 except KeyError:
318 r = "?"
333 r = "?"
319 print "%-30s %5d:%s" % (k, repo.changelog.rev(n), hg.hex(n))
334 print "%-30s %5d:%s" % (k, repo.changelog.rev(n), hg.hex(n))
320
335
321 elif cmd == "debugoldmerge":
336 elif cmd == "debugoldmerge":
322 if args:
337 if args:
323 other = hg.repository(ui, args[0])
338 other = hg.repository(ui, args[0])
324 repo.merge(other)
339 repo.merge(other)
325 else:
340 else:
326 print "missing source repository"
341 print "missing source repository"
327
342
328 elif cmd == "verify":
343 elif cmd == "verify":
329 filelinkrevs = {}
344 filelinkrevs = {}
330 filenodes = {}
345 filenodes = {}
331 manifestchangeset = {}
346 manifestchangeset = {}
332 changesets = revisions = files = 0
347 changesets = revisions = files = 0
333
348
334 print "checking changesets"
349 print "checking changesets"
335 for i in range(repo.changelog.count()):
350 for i in range(repo.changelog.count()):
336 changesets += 1
351 changesets += 1
337 n = repo.changelog.node(i)
352 n = repo.changelog.node(i)
338 changes = repo.changelog.read(n)
353 changes = repo.changelog.read(n)
339 manifestchangeset[changes[0]] = n
354 manifestchangeset[changes[0]] = n
340 for f in changes[3]:
355 for f in changes[3]:
341 revisions += 1
356 revisions += 1
342 filelinkrevs.setdefault(f, []).append(i)
357 filelinkrevs.setdefault(f, []).append(i)
343
358
344 print "checking manifests"
359 print "checking manifests"
345 for i in range(repo.manifest.count()):
360 for i in range(repo.manifest.count()):
346 n = repo.manifest.node(i)
361 n = repo.manifest.node(i)
347 ca = repo.changelog.node(repo.manifest.linkrev(n))
362 ca = repo.changelog.node(repo.manifest.linkrev(n))
348 cc = manifestchangeset[n]
363 cc = manifestchangeset[n]
349 if ca != cc:
364 if ca != cc:
350 print "manifest %s points to %s, not %s" % \
365 print "manifest %s points to %s, not %s" % \
351 (hg.hex(n), hg.hex(ca), hg.hex(cc))
366 (hg.hex(n), hg.hex(ca), hg.hex(cc))
352 m = repo.manifest.read(n)
367 m = repo.manifest.read(n)
353 for f, fn in m.items():
368 for f, fn in m.items():
354 filenodes.setdefault(f, {})[fn] = 1
369 filenodes.setdefault(f, {})[fn] = 1
355
370
356 print "crosschecking files in changesets and manifests"
371 print "crosschecking files in changesets and manifests"
357 for f in filenodes:
372 for f in filenodes:
358 if f not in filelinkrevs:
373 if f not in filelinkrevs:
359 print "file %s in manifest but not in changesets"
374 print "file %s in manifest but not in changesets"
360
375
361 for f in filelinkrevs:
376 for f in filelinkrevs:
362 if f not in filenodes:
377 if f not in filenodes:
363 print "file %s in changeset but not in manifest"
378 print "file %s in changeset but not in manifest"
364
379
365 print "checking files"
380 print "checking files"
366 for f in filenodes:
381 for f in filenodes:
367 files += 1
382 files += 1
368 fl = repo.file(f)
383 fl = repo.file(f)
369 nodes = {"\0"*20: 1}
384 nodes = {"\0"*20: 1}
370 for i in range(fl.count()):
385 for i in range(fl.count()):
371 n = fl.node(i)
386 n = fl.node(i)
372
387
373 if n not in filenodes[f]:
388 if n not in filenodes[f]:
374 print "%s:%s not in manifests" % (f, hg.hex(n))
389 print "%s:%s not in manifests" % (f, hg.hex(n))
375 else:
390 else:
376 del filenodes[f][n]
391 del filenodes[f][n]
377
392
378 flr = fl.linkrev(n)
393 flr = fl.linkrev(n)
379 if flr not in filelinkrevs[f]:
394 if flr not in filelinkrevs[f]:
380 print "%s:%s points to unexpected changeset rev %d" \
395 print "%s:%s points to unexpected changeset rev %d" \
381 % (f, hg.hex(n), fl.linkrev(n))
396 % (f, hg.hex(n), fl.linkrev(n))
382 else:
397 else:
383 filelinkrevs[f].remove(flr)
398 filelinkrevs[f].remove(flr)
384
399
385 # verify contents
400 # verify contents
386 t = fl.read(n)
401 t = fl.read(n)
387
402
388 # verify parents
403 # verify parents
389 (p1, p2) = fl.parents(n)
404 (p1, p2) = fl.parents(n)
390 if p1 not in nodes:
405 if p1 not in nodes:
391 print "%s:%s unknown parent 1 %s" % (f, hg.hex(n), hg.hex(p1))
406 print "%s:%s unknown parent 1 %s" % (f, hg.hex(n), hg.hex(p1))
392 if p2 not in nodes:
407 if p2 not in nodes:
393 print "file %s:%s unknown parent %s" % (f, hg.hex(n), hg.hex(p1))
408 print "file %s:%s unknown parent %s" % (f, hg.hex(n), hg.hex(p1))
394 nodes[n] = 1
409 nodes[n] = 1
395
410
396 # cross-check
411 # cross-check
397 for flr in filelinkrevs[f]:
412 for flr in filelinkrevs[f]:
398 print "changeset rev %d not in %s" % (flr, f)
413 print "changeset rev %d not in %s" % (flr, f)
399
414
400 for node in filenodes[f]:
415 for node in filenodes[f]:
401 print "node %s in manifests not in %s" % (hg.hex(n), f)
416 print "node %s in manifests not in %s" % (hg.hex(n), f)
402
417
403
418
404 print "%d files, %d changesets, %d total revisions" % (files, changesets,
419 print "%d files, %d changesets, %d total revisions" % (files, changesets,
405 revisions)
420 revisions)
406
421
407 else:
422 else:
408 print "unknown command\n"
423 print "unknown command\n"
409 help()
424 help()
410 sys.exit(1)
425 sys.exit(1)
@@ -1,910 +1,937 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
8 import sys, struct, sha, socket, os, time, base64, re, urllib2
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 from difflib import SequenceMatcher
13
14
14 class filelog(revlog):
15 class filelog(revlog):
15 def __init__(self, opener, path):
16 def __init__(self, opener, path):
16 s = self.encodepath(path)
17 s = self.encodepath(path)
17 revlog.__init__(self, opener, os.path.join("data", s + "i"),
18 revlog.__init__(self, opener, os.path.join("data", s + "i"),
18 os.path.join("data", s))
19 os.path.join("data", s))
19
20
20 def encodepath(self, path):
21 def encodepath(self, path):
21 s = sha.sha(path).digest()
22 s = sha.sha(path).digest()
22 s = base64.encodestring(s)[:-3]
23 s = base64.encodestring(s)[:-3]
23 s = re.sub("\+", "%", s)
24 s = re.sub("\+", "%", s)
24 s = re.sub("/", "_", s)
25 s = re.sub("/", "_", s)
25 return s
26 return s
26
27
27 def read(self, node):
28 def read(self, node):
28 return self.revision(node)
29 return self.revision(node)
29 def add(self, text, transaction, link, p1=None, p2=None):
30 def add(self, text, transaction, link, p1=None, p2=None):
30 return self.addrevision(text, transaction, link, p1, p2)
31 return self.addrevision(text, transaction, link, p1, p2)
31
32
32 def resolvedag(self, old, new, transaction, link):
33 def resolvedag(self, old, new, transaction, link):
33 """resolve unmerged heads in our DAG"""
34 """resolve unmerged heads in our DAG"""
34 if old == new: return None
35 if old == new: return None
35 a = self.ancestor(old, new)
36 a = self.ancestor(old, new)
36 if old == a: return None
37 if old == a: return None
37 return self.merge3(old, new, a, transaction, link)
38 return self.merge3(old, new, a, transaction, link)
38
39
39 def merge3(self, my, other, base, transaction, link):
40 def merge3(self, my, other, base, transaction, link):
40 """perform a 3-way merge and append the result"""
41 """perform a 3-way merge and append the result"""
41 def temp(prefix, node):
42 def temp(prefix, node):
42 (fd, name) = tempfile.mkstemp(prefix)
43 (fd, name) = tempfile.mkstemp(prefix)
43 f = os.fdopen(fd, "w")
44 f = os.fdopen(fd, "w")
44 f.write(self.revision(node))
45 f.write(self.revision(node))
45 f.close()
46 f.close()
46 return name
47 return name
47
48
48 a = temp("local", my)
49 a = temp("local", my)
49 b = temp("remote", other)
50 b = temp("remote", other)
50 c = temp("parent", base)
51 c = temp("parent", base)
51
52
52 cmd = os.environ["HGMERGE"]
53 cmd = os.environ["HGMERGE"]
53 r = os.system("%s %s %s %s" % (cmd, a, b, c))
54 r = os.system("%s %s %s %s" % (cmd, a, b, c))
54 if r:
55 if r:
55 raise "Merge failed, implement rollback!"
56 raise "Merge failed, implement rollback!"
56
57
57 t = open(a).read()
58 t = open(a).read()
58 os.unlink(a)
59 os.unlink(a)
59 os.unlink(b)
60 os.unlink(b)
60 os.unlink(c)
61 os.unlink(c)
61 return self.addrevision(t, transaction, link, my, other)
62 return self.addrevision(t, transaction, link, my, other)
62
63
63 def merge(self, other, transaction, linkseq, link):
64 def merge(self, other, transaction, linkseq, link):
64 """perform a merge and resolve resulting heads"""
65 """perform a merge and resolve resulting heads"""
65 (o, n) = self.mergedag(other, transaction, linkseq)
66 (o, n) = self.mergedag(other, transaction, linkseq)
66 return self.resolvedag(o, n, transaction, link)
67 return self.resolvedag(o, n, transaction, link)
67
68
69 def annotate(self, node):
70 revs = []
71 while node != nullid:
72 revs.append(node)
73 node = self.parents(node)[0]
74 revs.reverse()
75 prev = []
76 annotate = []
77 for node in revs:
78 curr = self.read(node).splitlines(1)
79 linkrev = self.linkrev(node)
80 sm = SequenceMatcher(None, prev, curr)
81 offset = 0
82 for o, m, n, s, t in sm.get_opcodes():
83 if o in ('insert','replace'):
84 annotate[m+offset:n+offset] = \
85 [ (linkrev, l) for l in curr[s:t]]
86 if o == 'insert':
87 offset += m-n
88 elif o == 'delete':
89 del annotate[m+offset:n+offset]
90 offset -= m-n
91 assert len(annotate) == len(curr)
92 prev = curr
93 return annotate
94
68 class manifest(revlog):
95 class manifest(revlog):
69 def __init__(self, opener):
96 def __init__(self, opener):
70 self.mapcache = None
97 self.mapcache = None
71 self.listcache = None
98 self.listcache = None
72 self.addlist = None
99 self.addlist = None
73 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
100 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
74
101
75 def read(self, node):
102 def read(self, node):
76 if self.mapcache and self.mapcache[0] == node:
103 if self.mapcache and self.mapcache[0] == node:
77 return self.mapcache[1]
104 return self.mapcache[1]
78 text = self.revision(node)
105 text = self.revision(node)
79 map = {}
106 map = {}
80 self.listcache = (text, text.splitlines(1))
107 self.listcache = (text, text.splitlines(1))
81 for l in self.listcache[1]:
108 for l in self.listcache[1]:
82 (f, n) = l.split('\0')
109 (f, n) = l.split('\0')
83 map[f] = bin(n[:40])
110 map[f] = bin(n[:40])
84 self.mapcache = (node, map)
111 self.mapcache = (node, map)
85 return map
112 return map
86
113
87 def diff(self, a, b):
114 def diff(self, a, b):
88 # this is sneaky, as we're not actually using a and b
115 # this is sneaky, as we're not actually using a and b
89 if self.listcache and len(self.listcache[0]) == len(a):
116 if self.listcache and len(self.listcache[0]) == len(a):
90 return mdiff.diff(self.listcache[1], self.addlist, 1)
117 return mdiff.diff(self.listcache[1], self.addlist, 1)
91 else:
118 else:
92 return mdiff.textdiff(a, b)
119 return mdiff.textdiff(a, b)
93
120
94 def add(self, map, transaction, link, p1=None, p2=None):
121 def add(self, map, transaction, link, p1=None, p2=None):
95 files = map.keys()
122 files = map.keys()
96 files.sort()
123 files.sort()
97
124
98 self.addlist = ["%s\000%s\n" % (f, hex(map[f])) for f in files]
125 self.addlist = ["%s\000%s\n" % (f, hex(map[f])) for f in files]
99 text = "".join(self.addlist)
126 text = "".join(self.addlist)
100
127
101 n = self.addrevision(text, transaction, link, p1, p2)
128 n = self.addrevision(text, transaction, link, p1, p2)
102 self.mapcache = (n, map)
129 self.mapcache = (n, map)
103 self.listcache = (text, self.addlist)
130 self.listcache = (text, self.addlist)
104
131
105 return n
132 return n
106
133
107 class changelog(revlog):
134 class changelog(revlog):
108 def __init__(self, opener):
135 def __init__(self, opener):
109 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
136 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
110
137
111 def extract(self, text):
138 def extract(self, text):
112 if not text:
139 if not text:
113 return (nullid, "", "0", [], "")
140 return (nullid, "", "0", [], "")
114 last = text.index("\n\n")
141 last = text.index("\n\n")
115 desc = text[last + 2:]
142 desc = text[last + 2:]
116 l = text[:last].splitlines()
143 l = text[:last].splitlines()
117 manifest = bin(l[0])
144 manifest = bin(l[0])
118 user = l[1]
145 user = l[1]
119 date = l[2]
146 date = l[2]
120 files = l[3:]
147 files = l[3:]
121 return (manifest, user, date, files, desc)
148 return (manifest, user, date, files, desc)
122
149
123 def read(self, node):
150 def read(self, node):
124 return self.extract(self.revision(node))
151 return self.extract(self.revision(node))
125
152
126 def add(self, manifest, list, desc, transaction, p1=None, p2=None):
153 def add(self, manifest, list, desc, transaction, p1=None, p2=None):
127 user = (os.environ.get("HGUSER") or
154 user = (os.environ.get("HGUSER") or
128 os.environ.get("EMAIL") or
155 os.environ.get("EMAIL") or
129 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
156 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
130 date = "%d %d" % (time.time(), time.timezone)
157 date = "%d %d" % (time.time(), time.timezone)
131 list.sort()
158 list.sort()
132 l = [hex(manifest), user, date] + list + ["", desc]
159 l = [hex(manifest), user, date] + list + ["", desc]
133 text = "\n".join(l)
160 text = "\n".join(l)
134 return self.addrevision(text, transaction, self.count(), p1, p2)
161 return self.addrevision(text, transaction, self.count(), p1, p2)
135
162
136 def merge3(self, my, other, base):
163 def merge3(self, my, other, base):
137 pass
164 pass
138
165
139 class dircache:
166 class dircache:
140 def __init__(self, opener, ui):
167 def __init__(self, opener, ui):
141 self.opener = opener
168 self.opener = opener
142 self.dirty = 0
169 self.dirty = 0
143 self.ui = ui
170 self.ui = ui
144 self.map = None
171 self.map = None
145 def __del__(self):
172 def __del__(self):
146 if self.dirty: self.write()
173 if self.dirty: self.write()
147 def __getitem__(self, key):
174 def __getitem__(self, key):
148 try:
175 try:
149 return self.map[key]
176 return self.map[key]
150 except TypeError:
177 except TypeError:
151 self.read()
178 self.read()
152 return self[key]
179 return self[key]
153
180
154 def read(self):
181 def read(self):
155 if self.map is not None: return self.map
182 if self.map is not None: return self.map
156
183
157 self.map = {}
184 self.map = {}
158 try:
185 try:
159 st = self.opener("dircache").read()
186 st = self.opener("dircache").read()
160 except: return
187 except: return
161
188
162 pos = 0
189 pos = 0
163 while pos < len(st):
190 while pos < len(st):
164 e = struct.unpack(">llll", st[pos:pos+16])
191 e = struct.unpack(">llll", st[pos:pos+16])
165 l = e[3]
192 l = e[3]
166 pos += 16
193 pos += 16
167 f = st[pos:pos + l]
194 f = st[pos:pos + l]
168 self.map[f] = e[:3]
195 self.map[f] = e[:3]
169 pos += l
196 pos += l
170
197
171 def update(self, files):
198 def update(self, files):
172 if not files: return
199 if not files: return
173 self.read()
200 self.read()
174 self.dirty = 1
201 self.dirty = 1
175 for f in files:
202 for f in files:
176 try:
203 try:
177 s = os.stat(f)
204 s = os.stat(f)
178 self.map[f] = (s.st_mode, s.st_size, s.st_mtime)
205 self.map[f] = (s.st_mode, s.st_size, s.st_mtime)
179 except IOError:
206 except IOError:
180 self.remove(f)
207 self.remove(f)
181
208
182 def taint(self, files):
209 def taint(self, files):
183 if not files: return
210 if not files: return
184 self.read()
211 self.read()
185 self.dirty = 1
212 self.dirty = 1
186 for f in files:
213 for f in files:
187 self.map[f] = (0, -1, 0)
214 self.map[f] = (0, -1, 0)
188
215
189 def remove(self, files):
216 def remove(self, files):
190 if not files: return
217 if not files: return
191 self.read()
218 self.read()
192 self.dirty = 1
219 self.dirty = 1
193 for f in files:
220 for f in files:
194 try:
221 try:
195 del self.map[f]
222 del self.map[f]
196 except KeyError:
223 except KeyError:
197 self.ui.warn("Not in dircache: %s\n" % f)
224 self.ui.warn("Not in dircache: %s\n" % f)
198 pass
225 pass
199
226
200 def clear(self):
227 def clear(self):
201 self.map = {}
228 self.map = {}
202 self.dirty = 1
229 self.dirty = 1
203
230
204 def write(self):
231 def write(self):
205 st = self.opener("dircache", "w")
232 st = self.opener("dircache", "w")
206 for f, e in self.map.items():
233 for f, e in self.map.items():
207 e = struct.pack(">llll", e[0], e[1], e[2], len(f))
234 e = struct.pack(">llll", e[0], e[1], e[2], len(f))
208 st.write(e + f)
235 st.write(e + f)
209 self.dirty = 0
236 self.dirty = 0
210
237
211 def copy(self):
238 def copy(self):
212 self.read()
239 self.read()
213 return self.map.copy()
240 return self.map.copy()
214
241
215 # used to avoid circular references so destructors work
242 # used to avoid circular references so destructors work
216 def opener(base):
243 def opener(base):
217 p = base
244 p = base
218 def o(path, mode="r"):
245 def o(path, mode="r"):
219 if p[:7] == "http://":
246 if p[:7] == "http://":
220 f = os.path.join(p, urllib.quote(path))
247 f = os.path.join(p, urllib.quote(path))
221 return httprangereader(f)
248 return httprangereader(f)
222
249
223 f = os.path.join(p, path)
250 f = os.path.join(p, path)
224
251
225 if mode != "r" and os.path.isfile(f):
252 if mode != "r" and os.path.isfile(f):
226 s = os.stat(f)
253 s = os.stat(f)
227 if s.st_nlink > 1:
254 if s.st_nlink > 1:
228 file(f + ".tmp", "w").write(file(f).read())
255 file(f + ".tmp", "w").write(file(f).read())
229 os.rename(f+".tmp", f)
256 os.rename(f+".tmp", f)
230
257
231 return file(f, mode)
258 return file(f, mode)
232
259
233 return o
260 return o
234
261
235 class localrepository:
262 class localrepository:
236 def __init__(self, ui, path=None, create=0):
263 def __init__(self, ui, path=None, create=0):
237 self.remote = 0
264 self.remote = 0
238 if path and path[:7] == "http://":
265 if path and path[:7] == "http://":
239 self.remote = 1
266 self.remote = 1
240 self.path = path
267 self.path = path
241 else:
268 else:
242 if not path:
269 if not path:
243 p = os.getcwd()
270 p = os.getcwd()
244 while not os.path.isdir(os.path.join(p, ".hg")):
271 while not os.path.isdir(os.path.join(p, ".hg")):
245 p = os.path.dirname(p)
272 p = os.path.dirname(p)
246 if p == "/": raise "No repo found"
273 if p == "/": raise "No repo found"
247 path = p
274 path = p
248 self.path = os.path.join(path, ".hg")
275 self.path = os.path.join(path, ".hg")
249
276
250 self.root = path
277 self.root = path
251 self.ui = ui
278 self.ui = ui
252
279
253 if create:
280 if create:
254 os.mkdir(self.path)
281 os.mkdir(self.path)
255 os.mkdir(self.join("data"))
282 os.mkdir(self.join("data"))
256
283
257 self.opener = opener(self.path)
284 self.opener = opener(self.path)
258 self.manifest = manifest(self.opener)
285 self.manifest = manifest(self.opener)
259 self.changelog = changelog(self.opener)
286 self.changelog = changelog(self.opener)
260 self.ignorelist = None
287 self.ignorelist = None
261 self.tags = None
288 self.tags = None
262
289
263 if not self.remote:
290 if not self.remote:
264 self.dircache = dircache(self.opener, ui)
291 self.dircache = dircache(self.opener, ui)
265 try:
292 try:
266 self.current = bin(self.opener("current").read())
293 self.current = bin(self.opener("current").read())
267 except IOError:
294 except IOError:
268 self.current = None
295 self.current = None
269
296
270 def setcurrent(self, node):
297 def setcurrent(self, node):
271 self.current = node
298 self.current = node
272 self.opener("current", "w").write(hex(node))
299 self.opener("current", "w").write(hex(node))
273
300
274 def ignore(self, f):
301 def ignore(self, f):
275 if self.ignorelist is None:
302 if self.ignorelist is None:
276 self.ignorelist = []
303 self.ignorelist = []
277 try:
304 try:
278 l = open(os.path.join(self.root, ".hgignore"))
305 l = open(os.path.join(self.root, ".hgignore"))
279 for pat in l:
306 for pat in l:
280 if pat != "\n":
307 if pat != "\n":
281 self.ignorelist.append(re.compile(pat[:-1]))
308 self.ignorelist.append(re.compile(pat[:-1]))
282 except IOError: pass
309 except IOError: pass
283 for pat in self.ignorelist:
310 for pat in self.ignorelist:
284 if pat.search(f): return True
311 if pat.search(f): return True
285 return False
312 return False
286
313
287 def lookup(self, key):
314 def lookup(self, key):
288 if self.tags is None:
315 if self.tags is None:
289 self.tags = {}
316 self.tags = {}
290 try:
317 try:
291 fl = self.file(".hgtags")
318 fl = self.file(".hgtags")
292 for l in fl.revision(fl.tip()).splitlines():
319 for l in fl.revision(fl.tip()).splitlines():
293 if l:
320 if l:
294 n, k = l.split(" ")
321 n, k = l.split(" ")
295 self.tags[k] = bin(n)
322 self.tags[k] = bin(n)
296 except KeyError: pass
323 except KeyError: pass
297 try:
324 try:
298 return self.tags[key]
325 return self.tags[key]
299 except KeyError:
326 except KeyError:
300 return self.changelog.lookup(key)
327 return self.changelog.lookup(key)
301
328
302 def join(self, f):
329 def join(self, f):
303 return os.path.join(self.path, f)
330 return os.path.join(self.path, f)
304
331
305 def file(self, f):
332 def file(self, f):
306 return filelog(self.opener, f)
333 return filelog(self.opener, f)
307
334
308 def transaction(self):
335 def transaction(self):
309 return transaction(self.opener, self.join("journal"))
336 return transaction(self.opener, self.join("journal"))
310
337
311 def merge(self, other):
338 def merge(self, other):
312 tr = self.transaction()
339 tr = self.transaction()
313 changed = {}
340 changed = {}
314 new = {}
341 new = {}
315 seqrev = self.changelog.count()
342 seqrev = self.changelog.count()
316 # some magic to allow fiddling in nested scope
343 # some magic to allow fiddling in nested scope
317 nextrev = [seqrev]
344 nextrev = [seqrev]
318
345
319 # helpers for back-linking file revisions to local changeset
346 # helpers for back-linking file revisions to local changeset
320 # revisions so we can immediately get to changeset from annotate
347 # revisions so we can immediately get to changeset from annotate
321 def accumulate(text):
348 def accumulate(text):
322 # track which files are added in which changeset and the
349 # track which files are added in which changeset and the
323 # corresponding _local_ changeset revision
350 # corresponding _local_ changeset revision
324 files = self.changelog.extract(text)[3]
351 files = self.changelog.extract(text)[3]
325 for f in files:
352 for f in files:
326 changed.setdefault(f, []).append(nextrev[0])
353 changed.setdefault(f, []).append(nextrev[0])
327 nextrev[0] += 1
354 nextrev[0] += 1
328
355
329 def seq(start):
356 def seq(start):
330 while 1:
357 while 1:
331 yield start
358 yield start
332 start += 1
359 start += 1
333
360
334 def lseq(l):
361 def lseq(l):
335 for r in l:
362 for r in l:
336 yield r
363 yield r
337
364
338 # begin the import/merge of changesets
365 # begin the import/merge of changesets
339 self.ui.status("merging new changesets\n")
366 self.ui.status("merging new changesets\n")
340 (co, cn) = self.changelog.mergedag(other.changelog, tr,
367 (co, cn) = self.changelog.mergedag(other.changelog, tr,
341 seq(seqrev), accumulate)
368 seq(seqrev), accumulate)
342 resolverev = self.changelog.count()
369 resolverev = self.changelog.count()
343
370
344 # is there anything to do?
371 # is there anything to do?
345 if co == cn:
372 if co == cn:
346 tr.close()
373 tr.close()
347 return
374 return
348
375
349 # do we need to resolve?
376 # do we need to resolve?
350 simple = (co == self.changelog.ancestor(co, cn))
377 simple = (co == self.changelog.ancestor(co, cn))
351
378
352 # merge all files changed by the changesets,
379 # merge all files changed by the changesets,
353 # keeping track of the new tips
380 # keeping track of the new tips
354 changelist = changed.keys()
381 changelist = changed.keys()
355 changelist.sort()
382 changelist.sort()
356 for f in changelist:
383 for f in changelist:
357 sys.stdout.write(".")
384 sys.stdout.write(".")
358 sys.stdout.flush()
385 sys.stdout.flush()
359 r = self.file(f)
386 r = self.file(f)
360 node = r.merge(other.file(f), tr, lseq(changed[f]), resolverev)
387 node = r.merge(other.file(f), tr, lseq(changed[f]), resolverev)
361 if node:
388 if node:
362 new[f] = node
389 new[f] = node
363 sys.stdout.write("\n")
390 sys.stdout.write("\n")
364
391
365 # begin the merge of the manifest
392 # begin the merge of the manifest
366 self.ui.status("merging manifests\n")
393 self.ui.status("merging manifests\n")
367 (mm, mo) = self.manifest.mergedag(other.manifest, tr, seq(seqrev))
394 (mm, mo) = self.manifest.mergedag(other.manifest, tr, seq(seqrev))
368
395
369 # For simple merges, we don't need to resolve manifests or changesets
396 # For simple merges, we don't need to resolve manifests or changesets
370 if simple:
397 if simple:
371 tr.close()
398 tr.close()
372 return
399 return
373
400
374 ma = self.manifest.ancestor(mm, mo)
401 ma = self.manifest.ancestor(mm, mo)
375
402
376 # resolve the manifest to point to all the merged files
403 # resolve the manifest to point to all the merged files
377 self.ui.status("resolving manifests\n")
404 self.ui.status("resolving manifests\n")
378 mmap = self.manifest.read(mm) # mine
405 mmap = self.manifest.read(mm) # mine
379 omap = self.manifest.read(mo) # other
406 omap = self.manifest.read(mo) # other
380 amap = self.manifest.read(ma) # ancestor
407 amap = self.manifest.read(ma) # ancestor
381 nmap = {}
408 nmap = {}
382
409
383 for f, mid in mmap.iteritems():
410 for f, mid in mmap.iteritems():
384 if f in omap:
411 if f in omap:
385 if mid != omap[f]:
412 if mid != omap[f]:
386 nmap[f] = new.get(f, mid) # use merged version
413 nmap[f] = new.get(f, mid) # use merged version
387 else:
414 else:
388 nmap[f] = new.get(f, mid) # they're the same
415 nmap[f] = new.get(f, mid) # they're the same
389 del omap[f]
416 del omap[f]
390 elif f in amap:
417 elif f in amap:
391 if mid != amap[f]:
418 if mid != amap[f]:
392 pass # we should prompt here
419 pass # we should prompt here
393 else:
420 else:
394 pass # other deleted it
421 pass # other deleted it
395 else:
422 else:
396 nmap[f] = new.get(f, mid) # we created it
423 nmap[f] = new.get(f, mid) # we created it
397
424
398 del mmap
425 del mmap
399
426
400 for f, oid in omap.iteritems():
427 for f, oid in omap.iteritems():
401 if f in amap:
428 if f in amap:
402 if oid != amap[f]:
429 if oid != amap[f]:
403 pass # this is the nasty case, we should prompt
430 pass # this is the nasty case, we should prompt
404 else:
431 else:
405 pass # probably safe
432 pass # probably safe
406 else:
433 else:
407 nmap[f] = new.get(f, oid) # remote created it
434 nmap[f] = new.get(f, oid) # remote created it
408
435
409 del omap
436 del omap
410 del amap
437 del amap
411
438
412 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
439 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
413
440
414 # Now all files and manifests are merged, we add the changed files
441 # Now all files and manifests are merged, we add the changed files
415 # and manifest id to the changelog
442 # and manifest id to the changelog
416 self.ui.status("committing merge changeset\n")
443 self.ui.status("committing merge changeset\n")
417 new = new.keys()
444 new = new.keys()
418 new.sort()
445 new.sort()
419 if co == cn: cn = -1
446 if co == cn: cn = -1
420
447
421 edittext = "\n"+"".join(["HG: changed %s\n" % f for f in new])
448 edittext = "\n"+"".join(["HG: changed %s\n" % f for f in new])
422 edittext = self.ui.edit(edittext)
449 edittext = self.ui.edit(edittext)
423 n = self.changelog.add(node, new, edittext, tr, co, cn)
450 n = self.changelog.add(node, new, edittext, tr, co, cn)
424
451
425 tr.close()
452 tr.close()
426
453
427 def commit(self, parent, update = None, text = ""):
454 def commit(self, parent, update = None, text = ""):
428 tr = self.transaction()
455 tr = self.transaction()
429
456
430 try:
457 try:
431 remove = [ l[:-1] for l in self.opener("to-remove") ]
458 remove = [ l[:-1] for l in self.opener("to-remove") ]
432 os.unlink(self.join("to-remove"))
459 os.unlink(self.join("to-remove"))
433
460
434 except IOError:
461 except IOError:
435 remove = []
462 remove = []
436
463
437 if update == None:
464 if update == None:
438 update = self.diffdir(self.root, parent)[0]
465 update = self.diffdir(self.root, parent)[0]
439
466
440 # check in files
467 # check in files
441 new = {}
468 new = {}
442 linkrev = self.changelog.count()
469 linkrev = self.changelog.count()
443 for f in update:
470 for f in update:
444 try:
471 try:
445 t = file(f).read()
472 t = file(f).read()
446 except IOError:
473 except IOError:
447 remove.append(f)
474 remove.append(f)
448 continue
475 continue
449 r = self.file(f)
476 r = self.file(f)
450 new[f] = r.add(t, tr, linkrev)
477 new[f] = r.add(t, tr, linkrev)
451
478
452 # update manifest
479 # update manifest
453 mmap = self.manifest.read(self.manifest.tip())
480 mmap = self.manifest.read(self.manifest.tip())
454 mmap.update(new)
481 mmap.update(new)
455 for f in remove:
482 for f in remove:
456 del mmap[f]
483 del mmap[f]
457 mnode = self.manifest.add(mmap, tr, linkrev)
484 mnode = self.manifest.add(mmap, tr, linkrev)
458
485
459 # add changeset
486 # add changeset
460 new = new.keys()
487 new = new.keys()
461 new.sort()
488 new.sort()
462
489
463 edittext = text + "\n"+"".join(["HG: changed %s\n" % f for f in new])
490 edittext = text + "\n"+"".join(["HG: changed %s\n" % f for f in new])
464 edittext += "".join(["HG: removed %s\n" % f for f in remove])
491 edittext += "".join(["HG: removed %s\n" % f for f in remove])
465 edittext = self.ui.edit(edittext)
492 edittext = self.ui.edit(edittext)
466
493
467 n = self.changelog.add(mnode, new, edittext, tr)
494 n = self.changelog.add(mnode, new, edittext, tr)
468 tr.close()
495 tr.close()
469
496
470 self.setcurrent(n)
497 self.setcurrent(n)
471 self.dircache.update(new)
498 self.dircache.update(new)
472 self.dircache.remove(remove)
499 self.dircache.remove(remove)
473
500
474 def checkdir(self, path):
501 def checkdir(self, path):
475 d = os.path.dirname(path)
502 d = os.path.dirname(path)
476 if not d: return
503 if not d: return
477 if not os.path.isdir(d):
504 if not os.path.isdir(d):
478 self.checkdir(d)
505 self.checkdir(d)
479 os.mkdir(d)
506 os.mkdir(d)
480
507
481 def checkout(self, node):
508 def checkout(self, node):
482 # checkout is really dumb at the moment
509 # checkout is really dumb at the moment
483 # it ought to basically merge
510 # it ought to basically merge
484 change = self.changelog.read(node)
511 change = self.changelog.read(node)
485 mmap = self.manifest.read(change[0])
512 mmap = self.manifest.read(change[0])
486
513
487 l = mmap.keys()
514 l = mmap.keys()
488 l.sort()
515 l.sort()
489 stats = []
516 stats = []
490 for f in l:
517 for f in l:
491 r = self.file(f)
518 r = self.file(f)
492 t = r.revision(mmap[f])
519 t = r.revision(mmap[f])
493 try:
520 try:
494 file(f, "w").write(t)
521 file(f, "w").write(t)
495 except:
522 except:
496 self.checkdir(f)
523 self.checkdir(f)
497 file(f, "w").write(t)
524 file(f, "w").write(t)
498
525
499 self.setcurrent(node)
526 self.setcurrent(node)
500 self.dircache.clear()
527 self.dircache.clear()
501 self.dircache.update(l)
528 self.dircache.update(l)
502
529
503 def diffdir(self, path, changeset):
530 def diffdir(self, path, changeset):
504 changed = []
531 changed = []
505 mf = {}
532 mf = {}
506 added = []
533 added = []
507
534
508 if changeset:
535 if changeset:
509 change = self.changelog.read(changeset)
536 change = self.changelog.read(changeset)
510 mf = self.manifest.read(change[0])
537 mf = self.manifest.read(change[0])
511
538
512 if changeset == self.current:
539 if changeset == self.current:
513 dc = self.dircache.copy()
540 dc = self.dircache.copy()
514 else:
541 else:
515 dc = dict.fromkeys(mf)
542 dc = dict.fromkeys(mf)
516
543
517 def fcmp(fn):
544 def fcmp(fn):
518 t1 = file(os.path.join(self.root, fn)).read()
545 t1 = file(os.path.join(self.root, fn)).read()
519 t2 = self.file(fn).revision(mf[fn])
546 t2 = self.file(fn).revision(mf[fn])
520 return cmp(t1, t2)
547 return cmp(t1, t2)
521
548
522 for dir, subdirs, files in os.walk(self.root):
549 for dir, subdirs, files in os.walk(self.root):
523 d = dir[len(self.root)+1:]
550 d = dir[len(self.root)+1:]
524 if ".hg" in subdirs: subdirs.remove(".hg")
551 if ".hg" in subdirs: subdirs.remove(".hg")
525
552
526 for f in files:
553 for f in files:
527 fn = os.path.join(d, f)
554 fn = os.path.join(d, f)
528 try: s = os.stat(os.path.join(self.root, fn))
555 try: s = os.stat(os.path.join(self.root, fn))
529 except: continue
556 except: continue
530 if fn in dc:
557 if fn in dc:
531 c = dc[fn]
558 c = dc[fn]
532 del dc[fn]
559 del dc[fn]
533 if not c:
560 if not c:
534 if fcmp(fn):
561 if fcmp(fn):
535 changed.append(fn)
562 changed.append(fn)
536 elif c[1] != s.st_size:
563 elif c[1] != s.st_size:
537 changed.append(fn)
564 changed.append(fn)
538 elif c[0] != s.st_mode or c[2] != s.st_mtime:
565 elif c[0] != s.st_mode or c[2] != s.st_mtime:
539 if fcmp(fn):
566 if fcmp(fn):
540 changed.append(fn)
567 changed.append(fn)
541 else:
568 else:
542 if self.ignore(fn): continue
569 if self.ignore(fn): continue
543 added.append(fn)
570 added.append(fn)
544
571
545 deleted = dc.keys()
572 deleted = dc.keys()
546 deleted.sort()
573 deleted.sort()
547
574
548 return (changed, added, deleted)
575 return (changed, added, deleted)
549
576
550 def diffrevs(self, node1, node2):
577 def diffrevs(self, node1, node2):
551 changed, added = [], []
578 changed, added = [], []
552
579
553 change = self.changelog.read(node1)
580 change = self.changelog.read(node1)
554 mf1 = self.manifest.read(change[0])
581 mf1 = self.manifest.read(change[0])
555 change = self.changelog.read(node2)
582 change = self.changelog.read(node2)
556 mf2 = self.manifest.read(change[0])
583 mf2 = self.manifest.read(change[0])
557
584
558 for fn in mf2:
585 for fn in mf2:
559 if mf1.has_key(fn):
586 if mf1.has_key(fn):
560 if mf1[fn] != mf2[fn]:
587 if mf1[fn] != mf2[fn]:
561 changed.append(fn)
588 changed.append(fn)
562 del mf1[fn]
589 del mf1[fn]
563 else:
590 else:
564 added.append(fn)
591 added.append(fn)
565
592
566 deleted = mf1.keys()
593 deleted = mf1.keys()
567 deleted.sort()
594 deleted.sort()
568
595
569 return (changed, added, deleted)
596 return (changed, added, deleted)
570
597
571 def add(self, list):
598 def add(self, list):
572 self.dircache.taint(list)
599 self.dircache.taint(list)
573
600
574 def remove(self, list):
601 def remove(self, list):
575 dl = self.opener("to-remove", "a")
602 dl = self.opener("to-remove", "a")
576 for f in list:
603 for f in list:
577 dl.write(f + "\n")
604 dl.write(f + "\n")
578
605
579 def branches(self, nodes):
606 def branches(self, nodes):
580 if not nodes: nodes = [self.changelog.tip()]
607 if not nodes: nodes = [self.changelog.tip()]
581 b = []
608 b = []
582 for n in nodes:
609 for n in nodes:
583 t = n
610 t = n
584 while n:
611 while n:
585 p = self.changelog.parents(n)
612 p = self.changelog.parents(n)
586 if p[1] != nullid or p[0] == nullid:
613 if p[1] != nullid or p[0] == nullid:
587 b.append((t, n, p[0], p[1]))
614 b.append((t, n, p[0], p[1]))
588 break
615 break
589 n = p[0]
616 n = p[0]
590 return b
617 return b
591
618
592 def between(self, pairs):
619 def between(self, pairs):
593 r = []
620 r = []
594
621
595 for top, bottom in pairs:
622 for top, bottom in pairs:
596 n, l, i = top, [], 0
623 n, l, i = top, [], 0
597 f = 1
624 f = 1
598
625
599 while n != bottom:
626 while n != bottom:
600 p = self.changelog.parents(n)[0]
627 p = self.changelog.parents(n)[0]
601 if i == f:
628 if i == f:
602 l.append(n)
629 l.append(n)
603 f = f * 2
630 f = f * 2
604 n = p
631 n = p
605 i += 1
632 i += 1
606
633
607 r.append(l)
634 r.append(l)
608
635
609 return r
636 return r
610
637
611 def newer(self, nodes):
638 def newer(self, nodes):
612 m = {}
639 m = {}
613 nl = []
640 nl = []
614 cl = self.changelog
641 cl = self.changelog
615 t = l = cl.count()
642 t = l = cl.count()
616 for n in nodes:
643 for n in nodes:
617 l = min(l, cl.rev(n))
644 l = min(l, cl.rev(n))
618 for p in cl.parents(n):
645 for p in cl.parents(n):
619 m[p] = 1
646 m[p] = 1
620
647
621 for i in xrange(l, t):
648 for i in xrange(l, t):
622 n = cl.node(i)
649 n = cl.node(i)
623 for p in cl.parents(n):
650 for p in cl.parents(n):
624 if p in m and n not in m:
651 if p in m and n not in m:
625 m[n] = 1
652 m[n] = 1
626 nl.append(n)
653 nl.append(n)
627
654
628 return nl
655 return nl
629
656
630 def getchangegroup(self, remote):
657 def getchangegroup(self, remote):
631 tip = remote.branches([])[0]
658 tip = remote.branches([])[0]
632 m = self.changelog.nodemap
659 m = self.changelog.nodemap
633 unknown = [tip]
660 unknown = [tip]
634 search = []
661 search = []
635 fetch = []
662 fetch = []
636
663
637 if tip[0] in m:
664 if tip[0] in m:
638 return None
665 return None
639
666
640 while unknown:
667 while unknown:
641 n = unknown.pop(0)
668 n = unknown.pop(0)
642 if n == nullid: break
669 if n == nullid: break
643 if n[1] and n[1] in m: # do we know the base?
670 if n[1] and n[1] in m: # do we know the base?
644 search.append(n) # schedule branch range for scanning
671 search.append(n) # schedule branch range for scanning
645 else:
672 else:
646 for b in remote.branches([n[2], n[3]]):
673 for b in remote.branches([n[2], n[3]]):
647 if b[0] in m:
674 if b[0] in m:
648 if n[1] not in fetch:
675 if n[1] not in fetch:
649 fetch.append(n[1]) # earliest unknown
676 fetch.append(n[1]) # earliest unknown
650 else:
677 else:
651 unknown.append(b)
678 unknown.append(b)
652
679
653 while search:
680 while search:
654 n = search.pop(0)
681 n = search.pop(0)
655 l = remote.between([(n[0], n[1])])[0]
682 l = remote.between([(n[0], n[1])])[0]
656 p = n[0]
683 p = n[0]
657 f = 1
684 f = 1
658 for i in l + [n[1]]:
685 for i in l + [n[1]]:
659 if i in m:
686 if i in m:
660 if f <= 4:
687 if f <= 4:
661 fetch.append(p)
688 fetch.append(p)
662 else:
689 else:
663 search.append((p, i))
690 search.append((p, i))
664 break
691 break
665 p, f = i, f * 2
692 p, f = i, f * 2
666
693
667 for f in fetch:
694 for f in fetch:
668 if f in m:
695 if f in m:
669 raise "already have", hex(f[:4])
696 raise "already have", hex(f[:4])
670
697
671 return remote.changegroup(fetch)
698 return remote.changegroup(fetch)
672
699
673 def changegroup(self, basenodes):
700 def changegroup(self, basenodes):
674 nodes = self.newer(basenodes)
701 nodes = self.newer(basenodes)
675
702
676 # construct the link map
703 # construct the link map
677 linkmap = {}
704 linkmap = {}
678 for n in nodes:
705 for n in nodes:
679 linkmap[self.changelog.rev(n)] = n
706 linkmap[self.changelog.rev(n)] = n
680
707
681 # construct a list of all changed files
708 # construct a list of all changed files
682 changed = {}
709 changed = {}
683 for n in nodes:
710 for n in nodes:
684 c = self.changelog.read(n)
711 c = self.changelog.read(n)
685 for f in c[3]:
712 for f in c[3]:
686 changed[f] = 1
713 changed[f] = 1
687 changed = changed.keys()
714 changed = changed.keys()
688 changed.sort()
715 changed.sort()
689
716
690 # the changegroup is changesets + manifests + all file revs
717 # the changegroup is changesets + manifests + all file revs
691 revs = [ self.changelog.rev(n) for n in nodes ]
718 revs = [ self.changelog.rev(n) for n in nodes ]
692
719
693 yield self.changelog.group(linkmap)
720 yield self.changelog.group(linkmap)
694 yield self.manifest.group(linkmap)
721 yield self.manifest.group(linkmap)
695
722
696 for f in changed:
723 for f in changed:
697 g = self.file(f).group(linkmap)
724 g = self.file(f).group(linkmap)
698 if not g: raise "couldn't find change to %s" % f
725 if not g: raise "couldn't find change to %s" % f
699 l = struct.pack(">l", len(f))
726 l = struct.pack(">l", len(f))
700 yield "".join([l, f, g])
727 yield "".join([l, f, g])
701
728
702 def addchangegroup(self, generator):
729 def addchangegroup(self, generator):
703 class genread:
730 class genread:
704 def __init__(self, generator):
731 def __init__(self, generator):
705 self.g = generator
732 self.g = generator
706 self.buf = ""
733 self.buf = ""
707 def read(self, l):
734 def read(self, l):
708 while l > len(self.buf):
735 while l > len(self.buf):
709 try:
736 try:
710 self.buf += self.g.next()
737 self.buf += self.g.next()
711 except StopIteration:
738 except StopIteration:
712 break
739 break
713 d, self.buf = self.buf[:l], self.buf[l:]
740 d, self.buf = self.buf[:l], self.buf[l:]
714 return d
741 return d
715
742
716 if not generator: return
743 if not generator: return
717 source = genread(generator)
744 source = genread(generator)
718
745
719 def getchunk(add = 0):
746 def getchunk(add = 0):
720 d = source.read(4)
747 d = source.read(4)
721 if not d: return ""
748 if not d: return ""
722 l = struct.unpack(">l", d)[0]
749 l = struct.unpack(">l", d)[0]
723 return source.read(l - 4 + add)
750 return source.read(l - 4 + add)
724
751
725 tr = self.transaction()
752 tr = self.transaction()
726 simple = True
753 simple = True
727
754
728 print "merging changesets"
755 print "merging changesets"
729 # pull off the changeset group
756 # pull off the changeset group
730 csg = getchunk()
757 csg = getchunk()
731 co = self.changelog.tip()
758 co = self.changelog.tip()
732 cn = self.changelog.addgroup(csg, lambda x: self.changelog.count(), tr)
759 cn = self.changelog.addgroup(csg, lambda x: self.changelog.count(), tr)
733
760
734 print "merging manifests"
761 print "merging manifests"
735 # pull off the manifest group
762 # pull off the manifest group
736 mfg = getchunk()
763 mfg = getchunk()
737 mo = self.manifest.tip()
764 mo = self.manifest.tip()
738 mm = self.manifest.addgroup(mfg, lambda x: self.changelog.rev(x), tr)
765 mm = self.manifest.addgroup(mfg, lambda x: self.changelog.rev(x), tr)
739
766
740 # do we need a resolve?
767 # do we need a resolve?
741 if self.changelog.ancestor(co, cn) != co:
768 if self.changelog.ancestor(co, cn) != co:
742 simple = False
769 simple = False
743 resolverev = self.changelog.count()
770 resolverev = self.changelog.count()
744
771
745 # process the files
772 # process the files
746 print "merging files"
773 print "merging files"
747 new = {}
774 new = {}
748 while 1:
775 while 1:
749 f = getchunk(4)
776 f = getchunk(4)
750 if not f: break
777 if not f: break
751 fg = getchunk()
778 fg = getchunk()
752
779
753 fl = self.file(f)
780 fl = self.file(f)
754 o = fl.tip()
781 o = fl.tip()
755 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr)
782 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr)
756 if not simple:
783 if not simple:
757 nn = fl.resolvedag(o, n, tr, resolverev)
784 nn = fl.resolvedag(o, n, tr, resolverev)
758 if nn: new[f] = nn
785 if nn: new[f] = nn
759
786
760 # For simple merges, we don't need to resolve manifests or changesets
787 # For simple merges, we don't need to resolve manifests or changesets
761 if simple:
788 if simple:
762 tr.close()
789 tr.close()
763 return
790 return
764
791
765 # resolve the manifest to point to all the merged files
792 # resolve the manifest to point to all the merged files
766 self.ui.status("resolving manifests\n")
793 self.ui.status("resolving manifests\n")
767 ma = self.manifest.ancestor(mm, mo)
794 ma = self.manifest.ancestor(mm, mo)
768 mmap = self.manifest.read(mm) # mine
795 mmap = self.manifest.read(mm) # mine
769 omap = self.manifest.read(mo) # other
796 omap = self.manifest.read(mo) # other
770 amap = self.manifest.read(ma) # ancestor
797 amap = self.manifest.read(ma) # ancestor
771 nmap = {}
798 nmap = {}
772
799
773 for f, mid in mmap.iteritems():
800 for f, mid in mmap.iteritems():
774 if f in omap:
801 if f in omap:
775 if mid != omap[f]:
802 if mid != omap[f]:
776 nmap[f] = new.get(f, mid) # use merged version
803 nmap[f] = new.get(f, mid) # use merged version
777 else:
804 else:
778 nmap[f] = new.get(f, mid) # they're the same
805 nmap[f] = new.get(f, mid) # they're the same
779 del omap[f]
806 del omap[f]
780 elif f in amap:
807 elif f in amap:
781 if mid != amap[f]:
808 if mid != amap[f]:
782 pass # we should prompt here
809 pass # we should prompt here
783 else:
810 else:
784 pass # other deleted it
811 pass # other deleted it
785 else:
812 else:
786 nmap[f] = new.get(f, mid) # we created it
813 nmap[f] = new.get(f, mid) # we created it
787
814
788 del mmap
815 del mmap
789
816
790 for f, oid in omap.iteritems():
817 for f, oid in omap.iteritems():
791 if f in amap:
818 if f in amap:
792 if oid != amap[f]:
819 if oid != amap[f]:
793 pass # this is the nasty case, we should prompt
820 pass # this is the nasty case, we should prompt
794 else:
821 else:
795 pass # probably safe
822 pass # probably safe
796 else:
823 else:
797 nmap[f] = new.get(f, oid) # remote created it
824 nmap[f] = new.get(f, oid) # remote created it
798
825
799 del omap
826 del omap
800 del amap
827 del amap
801
828
802 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
829 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
803
830
804 # Now all files and manifests are merged, we add the changed files
831 # Now all files and manifests are merged, we add the changed files
805 # and manifest id to the changelog
832 # and manifest id to the changelog
806 self.ui.status("committing merge changeset\n")
833 self.ui.status("committing merge changeset\n")
807 new = new.keys()
834 new = new.keys()
808 new.sort()
835 new.sort()
809 if co == cn: cn = -1
836 if co == cn: cn = -1
810
837
811 edittext = "\nHG: merge resolve\n" + \
838 edittext = "\nHG: merge resolve\n" + \
812 "".join(["HG: changed %s\n" % f for f in new])
839 "".join(["HG: changed %s\n" % f for f in new])
813 edittext = self.ui.edit(edittext)
840 edittext = self.ui.edit(edittext)
814 n = self.changelog.add(node, new, edittext, tr, co, cn)
841 n = self.changelog.add(node, new, edittext, tr, co, cn)
815
842
816 tr.close()
843 tr.close()
817
844
818 class remoterepository:
845 class remoterepository:
819 def __init__(self, ui, path):
846 def __init__(self, ui, path):
820 self.url = path.replace("hg://", "http://", 1)
847 self.url = path.replace("hg://", "http://", 1)
821 self.ui = ui
848 self.ui = ui
822
849
823 def do_cmd(self, cmd, **args):
850 def do_cmd(self, cmd, **args):
824 q = {"cmd": cmd}
851 q = {"cmd": cmd}
825 q.update(args)
852 q.update(args)
826 qs = urllib.urlencode(q)
853 qs = urllib.urlencode(q)
827 cu = "%s?%s" % (self.url, qs)
854 cu = "%s?%s" % (self.url, qs)
828 return urllib.urlopen(cu)
855 return urllib.urlopen(cu)
829
856
830 def branches(self, nodes):
857 def branches(self, nodes):
831 n = " ".join(map(hex, nodes))
858 n = " ".join(map(hex, nodes))
832 d = self.do_cmd("branches", nodes=n).read()
859 d = self.do_cmd("branches", nodes=n).read()
833 br = [ map(bin, b.split(" ")) for b in d.splitlines() ]
860 br = [ map(bin, b.split(" ")) for b in d.splitlines() ]
834 return br
861 return br
835
862
836 def between(self, pairs):
863 def between(self, pairs):
837 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
864 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
838 d = self.do_cmd("between", pairs=n).read()
865 d = self.do_cmd("between", pairs=n).read()
839 p = [ map(bin, l.split(" ")) for l in d.splitlines() ]
866 p = [ map(bin, l.split(" ")) for l in d.splitlines() ]
840 return p
867 return p
841
868
842 def changegroup(self, nodes):
869 def changegroup(self, nodes):
843 n = " ".join(map(hex, nodes))
870 n = " ".join(map(hex, nodes))
844 zd = zlib.decompressobj()
871 zd = zlib.decompressobj()
845 f = self.do_cmd("changegroup", roots=n)
872 f = self.do_cmd("changegroup", roots=n)
846 while 1:
873 while 1:
847 d = f.read(4096)
874 d = f.read(4096)
848 if not d:
875 if not d:
849 yield zd.flush()
876 yield zd.flush()
850 break
877 break
851 yield zd.decompress(d)
878 yield zd.decompress(d)
852
879
853 def repository(ui, path=None, create=0):
880 def repository(ui, path=None, create=0):
854 if path and path[:5] == "hg://":
881 if path and path[:5] == "hg://":
855 return remoterepository(ui, path)
882 return remoterepository(ui, path)
856 else:
883 else:
857 return localrepository(ui, path, create)
884 return localrepository(ui, path, create)
858
885
859 class ui:
886 class ui:
860 def __init__(self, verbose=False, debug=False):
887 def __init__(self, verbose=False, debug=False):
861 self.verbose = verbose
888 self.verbose = verbose
862 def write(self, *args):
889 def write(self, *args):
863 for a in args:
890 for a in args:
864 sys.stdout.write(str(a))
891 sys.stdout.write(str(a))
865 def prompt(self, msg, pat):
892 def prompt(self, msg, pat):
866 while 1:
893 while 1:
867 sys.stdout.write(msg)
894 sys.stdout.write(msg)
868 r = sys.stdin.readline()[:-1]
895 r = sys.stdin.readline()[:-1]
869 if re.match(pat, r):
896 if re.match(pat, r):
870 return r
897 return r
871 def status(self, *msg):
898 def status(self, *msg):
872 self.write(*msg)
899 self.write(*msg)
873 def warn(self, msg):
900 def warn(self, msg):
874 self.write(*msg)
901 self.write(*msg)
875 def note(self, msg):
902 def note(self, msg):
876 if self.verbose: self.write(*msg)
903 if self.verbose: self.write(*msg)
877 def debug(self, msg):
904 def debug(self, msg):
878 if self.debug: self.write(*msg)
905 if self.debug: self.write(*msg)
879 def edit(self, text):
906 def edit(self, text):
880 (fd, name) = tempfile.mkstemp("hg")
907 (fd, name) = tempfile.mkstemp("hg")
881 f = os.fdopen(fd, "w")
908 f = os.fdopen(fd, "w")
882 f.write(text)
909 f.write(text)
883 f.close()
910 f.close()
884
911
885 editor = os.environ.get("EDITOR", "vi")
912 editor = os.environ.get("EDITOR", "vi")
886 r = os.system("%s %s" % (editor, name))
913 r = os.system("%s %s" % (editor, name))
887 if r:
914 if r:
888 raise "Edit failed!"
915 raise "Edit failed!"
889
916
890 t = open(name).read()
917 t = open(name).read()
891 t = re.sub("(?m)^HG:.*\n", "", t)
918 t = re.sub("(?m)^HG:.*\n", "", t)
892
919
893 return t
920 return t
894
921
895
922
896 class httprangereader:
923 class httprangereader:
897 def __init__(self, url):
924 def __init__(self, url):
898 self.url = url
925 self.url = url
899 self.pos = 0
926 self.pos = 0
900 def seek(self, pos):
927 def seek(self, pos):
901 self.pos = pos
928 self.pos = pos
902 def read(self, bytes=None):
929 def read(self, bytes=None):
903 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
930 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
904 urllib2.install_opener(opener)
931 urllib2.install_opener(opener)
905 req = urllib2.Request(self.url)
932 req = urllib2.Request(self.url)
906 end = ''
933 end = ''
907 if bytes: end = self.pos + bytes
934 if bytes: end = self.pos + bytes
908 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
935 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
909 f = urllib2.urlopen(req)
936 f = urllib2.urlopen(req)
910 return f.read()
937 return f.read()
General Comments 0
You need to be logged in to leave comments. Login now