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