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