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