Mercurial > hg > mercurial-crew
comparison mercurial/dirstate.py @ 1089:142b5d5ec9cc
Break apart hg.py
- move the various parts of hg.py into their own files
- create node.py to store node manipulation functions
author | mpm@selenic.com |
---|---|
date | Sat, 27 Aug 2005 14:21:25 -0700 |
parents | mercurial/hg.py@05dc7aba22eb |
children | 221b5252864c |
comparison
equal
deleted
inserted
replaced
1088:39b916b1d8e4 | 1089:142b5d5ec9cc |
---|---|
1 """ | |
2 dirstate.py - working directory tracking for mercurial | |
3 | |
4 Copyright 2005 Matt Mackall <mpm@selenic.com> | |
5 | |
6 This software may be used and distributed according to the terms | |
7 of the GNU General Public License, incorporated herein by reference. | |
8 """ | |
9 | |
10 import sys, struct, os | |
11 from revlog import * | |
12 from demandload import * | |
13 demandload(globals(), "time bisect stat util") | |
14 | |
15 class dirstate: | |
16 def __init__(self, opener, ui, root): | |
17 self.opener = opener | |
18 self.root = root | |
19 self.dirty = 0 | |
20 self.ui = ui | |
21 self.map = None | |
22 self.pl = None | |
23 self.copies = {} | |
24 self.ignorefunc = None | |
25 | |
26 def wjoin(self, f): | |
27 return os.path.join(self.root, f) | |
28 | |
29 def getcwd(self): | |
30 cwd = os.getcwd() | |
31 if cwd == self.root: return '' | |
32 return cwd[len(self.root) + 1:] | |
33 | |
34 def ignore(self, f): | |
35 if not self.ignorefunc: | |
36 bigpat = [] | |
37 try: | |
38 l = file(self.wjoin(".hgignore")) | |
39 for pat in l: | |
40 p = pat.rstrip() | |
41 if p: | |
42 try: | |
43 re.compile(p) | |
44 except: | |
45 self.ui.warn("ignoring invalid ignore" | |
46 + " regular expression '%s'\n" % p) | |
47 else: | |
48 bigpat.append(p) | |
49 except IOError: pass | |
50 | |
51 if bigpat: | |
52 s = "(?:%s)" % (")|(?:".join(bigpat)) | |
53 r = re.compile(s) | |
54 self.ignorefunc = r.search | |
55 else: | |
56 self.ignorefunc = util.never | |
57 | |
58 return self.ignorefunc(f) | |
59 | |
60 def __del__(self): | |
61 if self.dirty: | |
62 self.write() | |
63 | |
64 def __getitem__(self, key): | |
65 try: | |
66 return self.map[key] | |
67 except TypeError: | |
68 self.read() | |
69 return self[key] | |
70 | |
71 def __contains__(self, key): | |
72 if not self.map: self.read() | |
73 return key in self.map | |
74 | |
75 def parents(self): | |
76 if not self.pl: | |
77 self.read() | |
78 return self.pl | |
79 | |
80 def markdirty(self): | |
81 if not self.dirty: | |
82 self.dirty = 1 | |
83 | |
84 def setparents(self, p1, p2=nullid): | |
85 self.markdirty() | |
86 self.pl = p1, p2 | |
87 | |
88 def state(self, key): | |
89 try: | |
90 return self[key][0] | |
91 except KeyError: | |
92 return "?" | |
93 | |
94 def read(self): | |
95 if self.map is not None: return self.map | |
96 | |
97 self.map = {} | |
98 self.pl = [nullid, nullid] | |
99 try: | |
100 st = self.opener("dirstate").read() | |
101 if not st: return | |
102 except: return | |
103 | |
104 self.pl = [st[:20], st[20: 40]] | |
105 | |
106 pos = 40 | |
107 while pos < len(st): | |
108 e = struct.unpack(">cllll", st[pos:pos+17]) | |
109 l = e[4] | |
110 pos += 17 | |
111 f = st[pos:pos + l] | |
112 if '\0' in f: | |
113 f, c = f.split('\0') | |
114 self.copies[f] = c | |
115 self.map[f] = e[:4] | |
116 pos += l | |
117 | |
118 def copy(self, source, dest): | |
119 self.read() | |
120 self.markdirty() | |
121 self.copies[dest] = source | |
122 | |
123 def copied(self, file): | |
124 return self.copies.get(file, None) | |
125 | |
126 def update(self, files, state, **kw): | |
127 ''' current states: | |
128 n normal | |
129 m needs merging | |
130 r marked for removal | |
131 a marked for addition''' | |
132 | |
133 if not files: return | |
134 self.read() | |
135 self.markdirty() | |
136 for f in files: | |
137 if state == "r": | |
138 self.map[f] = ('r', 0, 0, 0) | |
139 else: | |
140 s = os.stat(os.path.join(self.root, f)) | |
141 st_size = kw.get('st_size', s.st_size) | |
142 st_mtime = kw.get('st_mtime', s.st_mtime) | |
143 self.map[f] = (state, s.st_mode, st_size, st_mtime) | |
144 | |
145 def forget(self, files): | |
146 if not files: return | |
147 self.read() | |
148 self.markdirty() | |
149 for f in files: | |
150 try: | |
151 del self.map[f] | |
152 except KeyError: | |
153 self.ui.warn("not in dirstate: %s!\n" % f) | |
154 pass | |
155 | |
156 def clear(self): | |
157 self.map = {} | |
158 self.markdirty() | |
159 | |
160 def write(self): | |
161 st = self.opener("dirstate", "w") | |
162 st.write("".join(self.pl)) | |
163 for f, e in self.map.items(): | |
164 c = self.copied(f) | |
165 if c: | |
166 f = f + "\0" + c | |
167 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f)) | |
168 st.write(e + f) | |
169 self.dirty = 0 | |
170 | |
171 def filterfiles(self, files): | |
172 ret = {} | |
173 unknown = [] | |
174 | |
175 for x in files: | |
176 if x is '.': | |
177 return self.map.copy() | |
178 if x not in self.map: | |
179 unknown.append(x) | |
180 else: | |
181 ret[x] = self.map[x] | |
182 | |
183 if not unknown: | |
184 return ret | |
185 | |
186 b = self.map.keys() | |
187 b.sort() | |
188 blen = len(b) | |
189 | |
190 for x in unknown: | |
191 bs = bisect.bisect(b, x) | |
192 if bs != 0 and b[bs-1] == x: | |
193 ret[x] = self.map[x] | |
194 continue | |
195 while bs < blen: | |
196 s = b[bs] | |
197 if len(s) > len(x) and s.startswith(x) and s[len(x)] == '/': | |
198 ret[s] = self.map[s] | |
199 else: | |
200 break | |
201 bs += 1 | |
202 return ret | |
203 | |
204 def walk(self, files=None, match=util.always, dc=None): | |
205 self.read() | |
206 | |
207 # walk all files by default | |
208 if not files: | |
209 files = [self.root] | |
210 if not dc: | |
211 dc = self.map.copy() | |
212 elif not dc: | |
213 dc = self.filterfiles(files) | |
214 | |
215 known = {'.hg': 1} | |
216 def seen(fn): | |
217 if fn in known: return True | |
218 known[fn] = 1 | |
219 def traverse(): | |
220 for ff in util.unique(files): | |
221 f = os.path.join(self.root, ff) | |
222 try: | |
223 st = os.stat(f) | |
224 except OSError, inst: | |
225 if ff not in dc: self.ui.warn('%s: %s\n' % ( | |
226 util.pathto(self.getcwd(), ff), | |
227 inst.strerror)) | |
228 continue | |
229 if stat.S_ISDIR(st.st_mode): | |
230 for dir, subdirs, fl in os.walk(f): | |
231 d = dir[len(self.root) + 1:] | |
232 nd = util.normpath(d) | |
233 if nd == '.': nd = '' | |
234 if seen(nd): | |
235 subdirs[:] = [] | |
236 continue | |
237 for sd in subdirs: | |
238 ds = os.path.join(nd, sd +'/') | |
239 if self.ignore(ds) or not match(ds): | |
240 subdirs.remove(sd) | |
241 subdirs.sort() | |
242 fl.sort() | |
243 for fn in fl: | |
244 fn = util.pconvert(os.path.join(d, fn)) | |
245 yield 'f', fn | |
246 elif stat.S_ISREG(st.st_mode): | |
247 yield 'f', ff | |
248 else: | |
249 kind = 'unknown' | |
250 if stat.S_ISCHR(st.st_mode): kind = 'character device' | |
251 elif stat.S_ISBLK(st.st_mode): kind = 'block device' | |
252 elif stat.S_ISFIFO(st.st_mode): kind = 'fifo' | |
253 elif stat.S_ISLNK(st.st_mode): kind = 'symbolic link' | |
254 elif stat.S_ISSOCK(st.st_mode): kind = 'socket' | |
255 self.ui.warn('%s: unsupported file type (type is %s)\n' % ( | |
256 util.pathto(self.getcwd(), ff), | |
257 kind)) | |
258 | |
259 ks = dc.keys() | |
260 ks.sort() | |
261 for k in ks: | |
262 yield 'm', k | |
263 | |
264 # yield only files that match: all in dirstate, others only if | |
265 # not in .hgignore | |
266 | |
267 for src, fn in util.unique(traverse()): | |
268 fn = util.normpath(fn) | |
269 if seen(fn): continue | |
270 if fn not in dc and self.ignore(fn): | |
271 continue | |
272 if match(fn): | |
273 yield src, fn | |
274 | |
275 def changes(self, files=None, match=util.always): | |
276 self.read() | |
277 if not files: | |
278 dc = self.map.copy() | |
279 else: | |
280 dc = self.filterfiles(files) | |
281 lookup, modified, added, unknown = [], [], [], [] | |
282 removed, deleted = [], [] | |
283 | |
284 for src, fn in self.walk(files, match, dc=dc): | |
285 try: | |
286 s = os.stat(os.path.join(self.root, fn)) | |
287 except OSError: | |
288 continue | |
289 if not stat.S_ISREG(s.st_mode): | |
290 continue | |
291 c = dc.get(fn) | |
292 if c: | |
293 del dc[fn] | |
294 if c[0] == 'm': | |
295 modified.append(fn) | |
296 elif c[0] == 'a': | |
297 added.append(fn) | |
298 elif c[0] == 'r': | |
299 unknown.append(fn) | |
300 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100: | |
301 modified.append(fn) | |
302 elif c[3] != s.st_mtime: | |
303 lookup.append(fn) | |
304 else: | |
305 unknown.append(fn) | |
306 | |
307 for fn, c in [(fn, c) for fn, c in dc.items() if match(fn)]: | |
308 if c[0] == 'r': | |
309 removed.append(fn) | |
310 else: | |
311 deleted.append(fn) | |
312 return (lookup, modified, added, removed + deleted, unknown) |