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