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