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