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