|
@@
-1,400
+1,411
b''
|
|
1
|
"""
|
|
1
|
"""
|
|
2
|
dirstate.py - working directory tracking for mercurial
|
|
2
|
dirstate.py - working directory tracking for mercurial
|
|
3
|
|
|
3
|
|
|
4
|
Copyright 2005 Matt Mackall <mpm@selenic.com>
|
|
4
|
Copyright 2005 Matt Mackall <mpm@selenic.com>
|
|
5
|
|
|
5
|
|
|
6
|
This software may be used and distributed according to the terms
|
|
6
|
This software may be used and distributed according to the terms
|
|
7
|
of the GNU General Public License, incorporated herein by reference.
|
|
7
|
of the GNU General Public License, incorporated herein by reference.
|
|
8
|
"""
|
|
8
|
"""
|
|
9
|
|
|
9
|
|
|
10
|
import struct, os
|
|
10
|
import struct, os
|
|
11
|
from node import *
|
|
11
|
from node import *
|
|
12
|
from i18n import gettext as _
|
|
12
|
from i18n import gettext as _
|
|
13
|
from demandload import *
|
|
13
|
from demandload import *
|
|
14
|
demandload(globals(), "time bisect stat util re errno")
|
|
14
|
demandload(globals(), "time bisect stat util re errno")
|
|
15
|
|
|
15
|
|
|
16
|
class dirstate:
|
|
16
|
class dirstate:
|
|
17
|
def __init__(self, opener, ui, root):
|
|
17
|
def __init__(self, opener, ui, root):
|
|
18
|
self.opener = opener
|
|
18
|
self.opener = opener
|
|
19
|
self.root = root
|
|
19
|
self.root = root
|
|
20
|
self.dirty = 0
|
|
20
|
self.dirty = 0
|
|
21
|
self.ui = ui
|
|
21
|
self.ui = ui
|
|
22
|
self.map = None
|
|
22
|
self.map = None
|
|
23
|
self.pl = None
|
|
23
|
self.pl = None
|
|
24
|
self.copies = {}
|
|
24
|
self.copies = {}
|
|
25
|
self.ignorefunc = None
|
|
25
|
self.ignorefunc = None
|
|
26
|
self.blockignore = False
|
|
26
|
self.blockignore = False
|
|
27
|
|
|
27
|
|
|
28
|
def wjoin(self, f):
|
|
28
|
def wjoin(self, f):
|
|
29
|
return os.path.join(self.root, f)
|
|
29
|
return os.path.join(self.root, f)
|
|
30
|
|
|
30
|
|
|
31
|
def getcwd(self):
|
|
31
|
def getcwd(self):
|
|
32
|
cwd = os.getcwd()
|
|
32
|
cwd = os.getcwd()
|
|
33
|
if cwd == self.root: return ''
|
|
33
|
if cwd == self.root: return ''
|
|
34
|
return cwd[len(self.root) + 1:]
|
|
34
|
return cwd[len(self.root) + 1:]
|
|
35
|
|
|
35
|
|
|
36
|
def hgignore(self):
|
|
36
|
def hgignore(self):
|
|
37
|
'''return the contents of .hgignore as a list of patterns.
|
|
37
|
'''return the contents of .hgignore as a list of patterns.
|
|
38
|
|
|
38
|
|
|
39
|
trailing white space is dropped.
|
|
39
|
trailing white space is dropped.
|
|
40
|
the escape character is backslash.
|
|
40
|
the escape character is backslash.
|
|
41
|
comments start with #.
|
|
41
|
comments start with #.
|
|
42
|
empty lines are skipped.
|
|
42
|
empty lines are skipped.
|
|
43
|
|
|
43
|
|
|
44
|
lines can be of the following formats:
|
|
44
|
lines can be of the following formats:
|
|
45
|
|
|
45
|
|
|
46
|
syntax: regexp # defaults following lines to non-rooted regexps
|
|
46
|
syntax: regexp # defaults following lines to non-rooted regexps
|
|
47
|
syntax: glob # defaults following lines to non-rooted globs
|
|
47
|
syntax: glob # defaults following lines to non-rooted globs
|
|
48
|
re:pattern # non-rooted regular expression
|
|
48
|
re:pattern # non-rooted regular expression
|
|
49
|
glob:pattern # non-rooted glob
|
|
49
|
glob:pattern # non-rooted glob
|
|
50
|
pattern # pattern of the current default type'''
|
|
50
|
pattern # pattern of the current default type'''
|
|
51
|
syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
|
|
51
|
syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
|
|
52
|
def parselines(fp):
|
|
52
|
def parselines(fp):
|
|
53
|
for line in fp:
|
|
53
|
for line in fp:
|
|
54
|
escape = False
|
|
54
|
escape = False
|
|
55
|
for i in xrange(len(line)):
|
|
55
|
for i in xrange(len(line)):
|
|
56
|
if escape: escape = False
|
|
56
|
if escape: escape = False
|
|
57
|
elif line[i] == '\\': escape = True
|
|
57
|
elif line[i] == '\\': escape = True
|
|
58
|
elif line[i] == '#': break
|
|
58
|
elif line[i] == '#': break
|
|
59
|
line = line[:i].rstrip()
|
|
59
|
line = line[:i].rstrip()
|
|
60
|
if line: yield line
|
|
60
|
if line: yield line
|
|
61
|
pats = []
|
|
61
|
pats = []
|
|
62
|
try:
|
|
62
|
try:
|
|
63
|
fp = open(self.wjoin('.hgignore'))
|
|
63
|
fp = open(self.wjoin('.hgignore'))
|
|
64
|
syntax = 'relre:'
|
|
64
|
syntax = 'relre:'
|
|
65
|
for line in parselines(fp):
|
|
65
|
for line in parselines(fp):
|
|
66
|
if line.startswith('syntax:'):
|
|
66
|
if line.startswith('syntax:'):
|
|
67
|
s = line[7:].strip()
|
|
67
|
s = line[7:].strip()
|
|
68
|
try:
|
|
68
|
try:
|
|
69
|
syntax = syntaxes[s]
|
|
69
|
syntax = syntaxes[s]
|
|
70
|
except KeyError:
|
|
70
|
except KeyError:
|
|
71
|
self.ui.warn(_("ignoring invalid syntax '%s'\n") % s)
|
|
71
|
self.ui.warn(_("ignoring invalid syntax '%s'\n") % s)
|
|
72
|
continue
|
|
72
|
continue
|
|
73
|
pat = syntax + line
|
|
73
|
pat = syntax + line
|
|
74
|
for s in syntaxes.values():
|
|
74
|
for s in syntaxes.values():
|
|
75
|
if line.startswith(s):
|
|
75
|
if line.startswith(s):
|
|
76
|
pat = line
|
|
76
|
pat = line
|
|
77
|
break
|
|
77
|
break
|
|
78
|
pats.append(pat)
|
|
78
|
pats.append(pat)
|
|
79
|
except IOError: pass
|
|
79
|
except IOError: pass
|
|
80
|
return pats
|
|
80
|
return pats
|
|
81
|
|
|
81
|
|
|
82
|
def ignore(self, fn):
|
|
82
|
def ignore(self, fn):
|
|
83
|
'''default match function used by dirstate and localrepository.
|
|
83
|
'''default match function used by dirstate and localrepository.
|
|
84
|
this honours the .hgignore file, and nothing more.'''
|
|
84
|
this honours the .hgignore file, and nothing more.'''
|
|
85
|
if self.blockignore:
|
|
85
|
if self.blockignore:
|
|
86
|
return False
|
|
86
|
return False
|
|
87
|
if not self.ignorefunc:
|
|
87
|
if not self.ignorefunc:
|
|
88
|
ignore = self.hgignore()
|
|
88
|
ignore = self.hgignore()
|
|
89
|
if ignore:
|
|
89
|
if ignore:
|
|
90
|
files, self.ignorefunc, anypats = util.matcher(self.root,
|
|
90
|
files, self.ignorefunc, anypats = util.matcher(self.root,
|
|
91
|
inc=ignore)
|
|
91
|
inc=ignore)
|
|
92
|
else:
|
|
92
|
else:
|
|
93
|
self.ignorefunc = util.never
|
|
93
|
self.ignorefunc = util.never
|
|
94
|
return self.ignorefunc(fn)
|
|
94
|
return self.ignorefunc(fn)
|
|
95
|
|
|
95
|
|
|
96
|
def __del__(self):
|
|
96
|
def __del__(self):
|
|
97
|
if self.dirty:
|
|
97
|
if self.dirty:
|
|
98
|
self.write()
|
|
98
|
self.write()
|
|
99
|
|
|
99
|
|
|
100
|
def __getitem__(self, key):
|
|
100
|
def __getitem__(self, key):
|
|
101
|
try:
|
|
101
|
try:
|
|
102
|
return self.map[key]
|
|
102
|
return self.map[key]
|
|
103
|
except TypeError:
|
|
103
|
except TypeError:
|
|
104
|
self.read()
|
|
104
|
self.read()
|
|
105
|
return self[key]
|
|
105
|
return self[key]
|
|
106
|
|
|
106
|
|
|
107
|
def __contains__(self, key):
|
|
107
|
def __contains__(self, key):
|
|
108
|
if not self.map: self.read()
|
|
108
|
if not self.map: self.read()
|
|
109
|
return key in self.map
|
|
109
|
return key in self.map
|
|
110
|
|
|
110
|
|
|
111
|
def parents(self):
|
|
111
|
def parents(self):
|
|
112
|
if not self.pl:
|
|
112
|
if not self.pl:
|
|
113
|
self.read()
|
|
113
|
self.read()
|
|
114
|
return self.pl
|
|
114
|
return self.pl
|
|
115
|
|
|
115
|
|
|
116
|
def markdirty(self):
|
|
116
|
def markdirty(self):
|
|
117
|
if not self.dirty:
|
|
117
|
if not self.dirty:
|
|
118
|
self.dirty = 1
|
|
118
|
self.dirty = 1
|
|
119
|
|
|
119
|
|
|
120
|
def setparents(self, p1, p2=nullid):
|
|
120
|
def setparents(self, p1, p2=nullid):
|
|
121
|
if not self.pl:
|
|
121
|
if not self.pl:
|
|
122
|
self.read()
|
|
122
|
self.read()
|
|
123
|
self.markdirty()
|
|
123
|
self.markdirty()
|
|
124
|
self.pl = p1, p2
|
|
124
|
self.pl = p1, p2
|
|
125
|
|
|
125
|
|
|
126
|
def state(self, key):
|
|
126
|
def state(self, key):
|
|
127
|
try:
|
|
127
|
try:
|
|
128
|
return self[key][0]
|
|
128
|
return self[key][0]
|
|
129
|
except KeyError:
|
|
129
|
except KeyError:
|
|
130
|
return "?"
|
|
130
|
return "?"
|
|
131
|
|
|
131
|
|
|
132
|
def read(self):
|
|
132
|
def read(self):
|
|
133
|
if self.map is not None: return self.map
|
|
133
|
if self.map is not None: return self.map
|
|
134
|
|
|
134
|
|
|
135
|
self.map = {}
|
|
135
|
self.map = {}
|
|
136
|
self.pl = [nullid, nullid]
|
|
136
|
self.pl = [nullid, nullid]
|
|
137
|
try:
|
|
137
|
try:
|
|
138
|
st = self.opener("dirstate").read()
|
|
138
|
st = self.opener("dirstate").read()
|
|
139
|
if not st: return
|
|
139
|
if not st: return
|
|
140
|
except: return
|
|
140
|
except: return
|
|
141
|
|
|
141
|
|
|
142
|
self.pl = [st[:20], st[20: 40]]
|
|
142
|
self.pl = [st[:20], st[20: 40]]
|
|
143
|
|
|
143
|
|
|
144
|
pos = 40
|
|
144
|
pos = 40
|
|
145
|
while pos < len(st):
|
|
145
|
while pos < len(st):
|
|
146
|
e = struct.unpack(">cllll", st[pos:pos+17])
|
|
146
|
e = struct.unpack(">cllll", st[pos:pos+17])
|
|
147
|
l = e[4]
|
|
147
|
l = e[4]
|
|
148
|
pos += 17
|
|
148
|
pos += 17
|
|
149
|
f = st[pos:pos + l]
|
|
149
|
f = st[pos:pos + l]
|
|
150
|
if '\0' in f:
|
|
150
|
if '\0' in f:
|
|
151
|
f, c = f.split('\0')
|
|
151
|
f, c = f.split('\0')
|
|
152
|
self.copies[f] = c
|
|
152
|
self.copies[f] = c
|
|
153
|
self.map[f] = e[:4]
|
|
153
|
self.map[f] = e[:4]
|
|
154
|
pos += l
|
|
154
|
pos += l
|
|
155
|
|
|
155
|
|
|
156
|
def copy(self, source, dest):
|
|
156
|
def copy(self, source, dest):
|
|
157
|
self.read()
|
|
157
|
self.read()
|
|
158
|
self.markdirty()
|
|
158
|
self.markdirty()
|
|
159
|
self.copies[dest] = source
|
|
159
|
self.copies[dest] = source
|
|
160
|
|
|
160
|
|
|
161
|
def copied(self, file):
|
|
161
|
def copied(self, file):
|
|
162
|
return self.copies.get(file, None)
|
|
162
|
return self.copies.get(file, None)
|
|
163
|
|
|
163
|
|
|
164
|
def update(self, files, state, **kw):
|
|
164
|
def update(self, files, state, **kw):
|
|
165
|
''' current states:
|
|
165
|
''' current states:
|
|
166
|
n normal
|
|
166
|
n normal
|
|
167
|
m needs merging
|
|
167
|
m needs merging
|
|
168
|
r marked for removal
|
|
168
|
r marked for removal
|
|
169
|
a marked for addition'''
|
|
169
|
a marked for addition'''
|
|
170
|
|
|
170
|
|
|
171
|
if not files: return
|
|
171
|
if not files: return
|
|
172
|
self.read()
|
|
172
|
self.read()
|
|
173
|
self.markdirty()
|
|
173
|
self.markdirty()
|
|
174
|
for f in files:
|
|
174
|
for f in files:
|
|
175
|
if state == "r":
|
|
175
|
if state == "r":
|
|
176
|
self.map[f] = ('r', 0, 0, 0)
|
|
176
|
self.map[f] = ('r', 0, 0, 0)
|
|
177
|
else:
|
|
177
|
else:
|
|
178
|
s = os.lstat(os.path.join(self.root, f))
|
|
178
|
s = os.lstat(os.path.join(self.root, f))
|
|
179
|
st_size = kw.get('st_size', s.st_size)
|
|
179
|
st_size = kw.get('st_size', s.st_size)
|
|
180
|
st_mtime = kw.get('st_mtime', s.st_mtime)
|
|
180
|
st_mtime = kw.get('st_mtime', s.st_mtime)
|
|
181
|
self.map[f] = (state, s.st_mode, st_size, st_mtime)
|
|
181
|
self.map[f] = (state, s.st_mode, st_size, st_mtime)
|
|
182
|
if self.copies.has_key(f):
|
|
182
|
if self.copies.has_key(f):
|
|
183
|
del self.copies[f]
|
|
183
|
del self.copies[f]
|
|
184
|
|
|
184
|
|
|
185
|
def forget(self, files):
|
|
185
|
def forget(self, files):
|
|
186
|
if not files: return
|
|
186
|
if not files: return
|
|
187
|
self.read()
|
|
187
|
self.read()
|
|
188
|
self.markdirty()
|
|
188
|
self.markdirty()
|
|
189
|
for f in files:
|
|
189
|
for f in files:
|
|
190
|
try:
|
|
190
|
try:
|
|
191
|
del self.map[f]
|
|
191
|
del self.map[f]
|
|
192
|
except KeyError:
|
|
192
|
except KeyError:
|
|
193
|
self.ui.warn(_("not in dirstate: %s!\n") % f)
|
|
193
|
self.ui.warn(_("not in dirstate: %s!\n") % f)
|
|
194
|
pass
|
|
194
|
pass
|
|
195
|
|
|
195
|
|
|
196
|
def clear(self):
|
|
196
|
def clear(self):
|
|
197
|
self.map = {}
|
|
197
|
self.map = {}
|
|
198
|
self.markdirty()
|
|
198
|
self.markdirty()
|
|
199
|
|
|
199
|
|
|
200
|
def write(self):
|
|
200
|
def write(self):
|
|
201
|
st = self.opener("dirstate", "w")
|
|
201
|
st = self.opener("dirstate", "w")
|
|
202
|
st.write("".join(self.pl))
|
|
202
|
st.write("".join(self.pl))
|
|
203
|
for f, e in self.map.items():
|
|
203
|
for f, e in self.map.items():
|
|
204
|
c = self.copied(f)
|
|
204
|
c = self.copied(f)
|
|
205
|
if c:
|
|
205
|
if c:
|
|
206
|
f = f + "\0" + c
|
|
206
|
f = f + "\0" + c
|
|
207
|
e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
|
|
207
|
e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
|
|
208
|
st.write(e + f)
|
|
208
|
st.write(e + f)
|
|
209
|
self.dirty = 0
|
|
209
|
self.dirty = 0
|
|
210
|
|
|
210
|
|
|
211
|
def filterfiles(self, files):
|
|
211
|
def filterfiles(self, files):
|
|
212
|
ret = {}
|
|
212
|
ret = {}
|
|
213
|
unknown = []
|
|
213
|
unknown = []
|
|
214
|
|
|
214
|
|
|
215
|
for x in files:
|
|
215
|
for x in files:
|
|
216
|
if x is '.':
|
|
216
|
if x is '.':
|
|
217
|
return self.map.copy()
|
|
217
|
return self.map.copy()
|
|
218
|
if x not in self.map:
|
|
218
|
if x not in self.map:
|
|
219
|
unknown.append(x)
|
|
219
|
unknown.append(x)
|
|
220
|
else:
|
|
220
|
else:
|
|
221
|
ret[x] = self.map[x]
|
|
221
|
ret[x] = self.map[x]
|
|
222
|
|
|
222
|
|
|
223
|
if not unknown:
|
|
223
|
if not unknown:
|
|
224
|
return ret
|
|
224
|
return ret
|
|
225
|
|
|
225
|
|
|
226
|
b = self.map.keys()
|
|
226
|
b = self.map.keys()
|
|
227
|
b.sort()
|
|
227
|
b.sort()
|
|
228
|
blen = len(b)
|
|
228
|
blen = len(b)
|
|
229
|
|
|
229
|
|
|
230
|
for x in unknown:
|
|
230
|
for x in unknown:
|
|
231
|
bs = bisect.bisect(b, x)
|
|
231
|
bs = bisect.bisect(b, x)
|
|
232
|
if bs != 0 and b[bs-1] == x:
|
|
232
|
if bs != 0 and b[bs-1] == x:
|
|
233
|
ret[x] = self.map[x]
|
|
233
|
ret[x] = self.map[x]
|
|
234
|
continue
|
|
234
|
continue
|
|
235
|
while bs < blen:
|
|
235
|
while bs < blen:
|
|
236
|
s = b[bs]
|
|
236
|
s = b[bs]
|
|
237
|
if len(s) > len(x) and s.startswith(x) and s[len(x)] == '/':
|
|
237
|
if len(s) > len(x) and s.startswith(x) and s[len(x)] == '/':
|
|
238
|
ret[s] = self.map[s]
|
|
238
|
ret[s] = self.map[s]
|
|
239
|
else:
|
|
239
|
else:
|
|
240
|
break
|
|
240
|
break
|
|
241
|
bs += 1
|
|
241
|
bs += 1
|
|
242
|
return ret
|
|
242
|
return ret
|
|
243
|
|
|
243
|
|
|
|
|
|
244
|
def supported_type(self, f, st, verbose=True):
|
|
|
|
|
245
|
if stat.S_ISREG(st.st_mode):
|
|
|
|
|
246
|
return True
|
|
|
|
|
247
|
if verbose:
|
|
|
|
|
248
|
kind = 'unknown'
|
|
|
|
|
249
|
if stat.S_ISCHR(st.st_mode): kind = _('character device')
|
|
|
|
|
250
|
elif stat.S_ISBLK(st.st_mode): kind = _('block device')
|
|
|
|
|
251
|
elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
|
|
|
|
|
252
|
elif stat.S_ISLNK(st.st_mode): kind = _('symbolic link')
|
|
|
|
|
253
|
elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
|
|
|
|
|
254
|
elif stat.S_ISDIR(st.st_mode): kind = _('directory')
|
|
|
|
|
255
|
self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
|
|
|
|
|
256
|
util.pathto(self.getcwd(), f),
|
|
|
|
|
257
|
kind))
|
|
|
|
|
258
|
return False
|
|
|
|
|
259
|
|
|
244
|
def statwalk(self, files=None, match=util.always, dc=None):
|
|
260
|
def statwalk(self, files=None, match=util.always, dc=None):
|
|
245
|
self.read()
|
|
261
|
self.read()
|
|
246
|
|
|
262
|
|
|
247
|
# walk all files by default
|
|
263
|
# walk all files by default
|
|
248
|
if not files:
|
|
264
|
if not files:
|
|
249
|
files = [self.root]
|
|
265
|
files = [self.root]
|
|
250
|
if not dc:
|
|
266
|
if not dc:
|
|
251
|
dc = self.map.copy()
|
|
267
|
dc = self.map.copy()
|
|
252
|
elif not dc:
|
|
268
|
elif not dc:
|
|
253
|
dc = self.filterfiles(files)
|
|
269
|
dc = self.filterfiles(files)
|
|
254
|
|
|
270
|
|
|
255
|
def statmatch(file, stat):
|
|
271
|
def statmatch(file, stat):
|
|
256
|
file = util.pconvert(file)
|
|
272
|
file = util.pconvert(file)
|
|
257
|
if file not in dc and self.ignore(file):
|
|
273
|
if file not in dc and self.ignore(file):
|
|
258
|
return False
|
|
274
|
return False
|
|
259
|
return match(file)
|
|
275
|
return match(file)
|
|
260
|
|
|
276
|
|
|
261
|
return self.walkhelper(files=files, statmatch=statmatch, dc=dc)
|
|
277
|
return self.walkhelper(files=files, statmatch=statmatch, dc=dc)
|
|
262
|
|
|
278
|
|
|
263
|
def walk(self, files=None, match=util.always, dc=None):
|
|
279
|
def walk(self, files=None, match=util.always, dc=None):
|
|
264
|
# filter out the stat
|
|
280
|
# filter out the stat
|
|
265
|
for src, f, st in self.statwalk(files, match, dc):
|
|
281
|
for src, f, st in self.statwalk(files, match, dc):
|
|
266
|
yield src, f
|
|
282
|
yield src, f
|
|
267
|
|
|
283
|
|
|
268
|
# walk recursively through the directory tree, finding all files
|
|
284
|
# walk recursively through the directory tree, finding all files
|
|
269
|
# matched by the statmatch function
|
|
285
|
# matched by the statmatch function
|
|
270
|
#
|
|
286
|
#
|
|
271
|
# results are yielded in a tuple (src, filename, st), where src
|
|
287
|
# results are yielded in a tuple (src, filename, st), where src
|
|
272
|
# is one of:
|
|
288
|
# is one of:
|
|
273
|
# 'f' the file was found in the directory tree
|
|
289
|
# 'f' the file was found in the directory tree
|
|
274
|
# 'm' the file was only in the dirstate and not in the tree
|
|
290
|
# 'm' the file was only in the dirstate and not in the tree
|
|
275
|
# and st is the stat result if the file was found in the directory.
|
|
291
|
# and st is the stat result if the file was found in the directory.
|
|
276
|
#
|
|
292
|
#
|
|
277
|
# dc is an optional arg for the current dirstate. dc is not modified
|
|
293
|
# dc is an optional arg for the current dirstate. dc is not modified
|
|
278
|
# directly by this function, but might be modified by your statmatch call.
|
|
294
|
# directly by this function, but might be modified by your statmatch call.
|
|
279
|
#
|
|
295
|
#
|
|
280
|
def walkhelper(self, files, statmatch, dc):
|
|
296
|
def walkhelper(self, files, statmatch, dc):
|
|
281
|
def supported_type(f, st):
|
|
|
|
|
282
|
if stat.S_ISREG(st.st_mode):
|
|
|
|
|
283
|
return True
|
|
|
|
|
284
|
else:
|
|
|
|
|
285
|
kind = 'unknown'
|
|
|
|
|
286
|
if stat.S_ISCHR(st.st_mode): kind = _('character device')
|
|
|
|
|
287
|
elif stat.S_ISBLK(st.st_mode): kind = _('block device')
|
|
|
|
|
288
|
elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
|
|
|
|
|
289
|
elif stat.S_ISLNK(st.st_mode): kind = _('symbolic link')
|
|
|
|
|
290
|
elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
|
|
|
|
|
291
|
elif stat.S_ISDIR(st.st_mode): kind = _('directory')
|
|
|
|
|
292
|
self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
|
|
|
|
|
293
|
util.pathto(self.getcwd(), f),
|
|
|
|
|
294
|
kind))
|
|
|
|
|
295
|
return False
|
|
|
|
|
296
|
|
|
|
|
|
297
|
# recursion free walker, faster than os.walk.
|
|
297
|
# recursion free walker, faster than os.walk.
|
|
298
|
def findfiles(s):
|
|
298
|
def findfiles(s):
|
|
299
|
retfiles = []
|
|
299
|
retfiles = []
|
|
300
|
work = [s]
|
|
300
|
work = [s]
|
|
301
|
while work:
|
|
301
|
while work:
|
|
302
|
top = work.pop()
|
|
302
|
top = work.pop()
|
|
303
|
names = os.listdir(top)
|
|
303
|
names = os.listdir(top)
|
|
304
|
names.sort()
|
|
304
|
names.sort()
|
|
305
|
# nd is the top of the repository dir tree
|
|
305
|
# nd is the top of the repository dir tree
|
|
306
|
nd = util.normpath(top[len(self.root) + 1:])
|
|
306
|
nd = util.normpath(top[len(self.root) + 1:])
|
|
307
|
if nd == '.': nd = ''
|
|
307
|
if nd == '.': nd = ''
|
|
308
|
for f in names:
|
|
308
|
for f in names:
|
|
309
|
np = os.path.join(nd, f)
|
|
309
|
np = os.path.join(nd, f)
|
|
310
|
if seen(np):
|
|
310
|
if seen(np):
|
|
311
|
continue
|
|
311
|
continue
|
|
312
|
p = os.path.join(top, f)
|
|
312
|
p = os.path.join(top, f)
|
|
313
|
# don't trip over symlinks
|
|
313
|
# don't trip over symlinks
|
|
314
|
st = os.lstat(p)
|
|
314
|
st = os.lstat(p)
|
|
315
|
if stat.S_ISDIR(st.st_mode):
|
|
315
|
if stat.S_ISDIR(st.st_mode):
|
|
316
|
ds = os.path.join(nd, f +'/')
|
|
316
|
ds = os.path.join(nd, f +'/')
|
|
317
|
if statmatch(ds, st):
|
|
317
|
if statmatch(ds, st):
|
|
318
|
work.append(p)
|
|
318
|
work.append(p)
|
|
319
|
elif statmatch(np, st) and supported_type(np, st):
|
|
319
|
if statmatch(np, st) and np in dc:
|
|
320
|
yield util.pconvert(np), st
|
|
320
|
yield 'm', util.pconvert(np), st
|
|
321
|
|
|
321
|
elif statmatch(np, st):
|
|
|
|
|
322
|
if self.supported_type(np, st):
|
|
|
|
|
323
|
yield 'f', util.pconvert(np), st
|
|
|
|
|
324
|
elif np in dc:
|
|
|
|
|
325
|
yield 'm', util.pconvert(np), st
|
|
322
|
|
|
326
|
|
|
323
|
known = {'.hg': 1}
|
|
327
|
known = {'.hg': 1}
|
|
324
|
def seen(fn):
|
|
328
|
def seen(fn):
|
|
325
|
if fn in known: return True
|
|
329
|
if fn in known: return True
|
|
326
|
known[fn] = 1
|
|
330
|
known[fn] = 1
|
|
327
|
|
|
331
|
|
|
328
|
# step one, find all files that match our criteria
|
|
332
|
# step one, find all files that match our criteria
|
|
329
|
files.sort()
|
|
333
|
files.sort()
|
|
330
|
for ff in util.unique(files):
|
|
334
|
for ff in util.unique(files):
|
|
331
|
f = os.path.join(self.root, ff)
|
|
335
|
f = os.path.join(self.root, ff)
|
|
332
|
try:
|
|
336
|
try:
|
|
333
|
st = os.lstat(f)
|
|
337
|
st = os.lstat(f)
|
|
334
|
except OSError, inst:
|
|
338
|
except OSError, inst:
|
|
335
|
if ff not in dc: self.ui.warn('%s: %s\n' % (
|
|
339
|
if ff not in dc: self.ui.warn('%s: %s\n' % (
|
|
336
|
util.pathto(self.getcwd(), ff),
|
|
340
|
util.pathto(self.getcwd(), ff),
|
|
337
|
inst.strerror))
|
|
341
|
inst.strerror))
|
|
338
|
continue
|
|
342
|
continue
|
|
339
|
if stat.S_ISDIR(st.st_mode):
|
|
343
|
if stat.S_ISDIR(st.st_mode):
|
|
340
|
cmp0 = (lambda x, y: cmp(x[0], y[0]))
|
|
344
|
cmp1 = (lambda x, y: cmp(x[1], y[1]))
|
|
341
|
sorted = [ x for x in findfiles(f) ]
|
|
345
|
sorted = [ x for x in findfiles(f) ]
|
|
342
|
sorted.sort(cmp0)
|
|
346
|
sorted.sort(cmp1)
|
|
343
|
for fl, stl in sorted:
|
|
347
|
for e in sorted:
|
|
344
|
yield 'f', fl, stl
|
|
348
|
yield e
|
|
345
|
else:
|
|
349
|
else:
|
|
346
|
ff = util.normpath(ff)
|
|
350
|
ff = util.normpath(ff)
|
|
347
|
if seen(ff):
|
|
351
|
if seen(ff):
|
|
348
|
continue
|
|
352
|
continue
|
|
349
|
found = False
|
|
|
|
|
350
|
self.blockignore = True
|
|
353
|
self.blockignore = True
|
|
351
|
if statmatch(ff, st) and supported_type(ff, st):
|
|
354
|
if statmatch(ff, st):
|
|
352
|
found = True
|
|
355
|
if self.supported_type(ff, st):
|
|
|
|
|
356
|
yield 'f', ff, st
|
|
|
|
|
357
|
elif ff in dc:
|
|
|
|
|
358
|
yield 'm', ff, st
|
|
353
|
self.blockignore = False
|
|
359
|
self.blockignore = False
|
|
354
|
if found:
|
|
|
|
|
355
|
yield 'f', ff, st
|
|
|
|
|
356
|
|
|
360
|
|
|
357
|
# step two run through anything left in the dc hash and yield
|
|
361
|
# step two run through anything left in the dc hash and yield
|
|
358
|
# if we haven't already seen it
|
|
362
|
# if we haven't already seen it
|
|
359
|
ks = dc.keys()
|
|
363
|
ks = dc.keys()
|
|
360
|
ks.sort()
|
|
364
|
ks.sort()
|
|
361
|
for k in ks:
|
|
365
|
for k in ks:
|
|
362
|
if not seen(k) and (statmatch(k, None)):
|
|
366
|
if not seen(k) and (statmatch(k, None)):
|
|
363
|
yield 'm', k, None
|
|
367
|
yield 'm', k, None
|
|
364
|
|
|
368
|
|
|
365
|
def changes(self, files=None, match=util.always):
|
|
369
|
def changes(self, files=None, match=util.always):
|
|
366
|
lookup, modified, added, unknown = [], [], [], []
|
|
370
|
lookup, modified, added, unknown = [], [], [], []
|
|
367
|
removed, deleted = [], []
|
|
371
|
removed, deleted = [], []
|
|
368
|
|
|
372
|
|
|
369
|
for src, fn, st in self.statwalk(files, match):
|
|
373
|
for src, fn, st in self.statwalk(files, match):
|
|
370
|
try:
|
|
374
|
try:
|
|
371
|
type, mode, size, time = self[fn]
|
|
375
|
type, mode, size, time = self[fn]
|
|
372
|
except KeyError:
|
|
376
|
except KeyError:
|
|
373
|
unknown.append(fn)
|
|
377
|
unknown.append(fn)
|
|
374
|
continue
|
|
378
|
continue
|
|
375
|
if src == 'm':
|
|
379
|
if src == 'm':
|
|
|
|
|
380
|
nonexistent = True
|
|
|
|
|
381
|
if not st:
|
|
376
|
try:
|
|
382
|
try:
|
|
377
|
st = os.stat(fn)
|
|
383
|
st = os.lstat(fn)
|
|
378
|
except OSError, inst:
|
|
384
|
except OSError, inst:
|
|
|
|
|
385
|
if inst.errno != errno.ENOENT:
|
|
|
|
|
386
|
raise
|
|
|
|
|
387
|
st = None
|
|
|
|
|
388
|
# We need to re-check that it is a valid file
|
|
|
|
|
389
|
if st and self.supported_type(fn, st):
|
|
|
|
|
390
|
nonexistent = False
|
|
379
|
# XXX: what to do with file no longer present in the fs
|
|
391
|
# XXX: what to do with file no longer present in the fs
|
|
380
|
# who are not removed in the dirstate ?
|
|
392
|
# who are not removed in the dirstate ?
|
|
381
|
if inst.errno != errno.ENOENT:
|
|
393
|
if nonexistent:
|
|
382
|
raise
|
|
|
|
|
383
|
deleted.append(fn)
|
|
394
|
deleted.append(fn)
|
|
384
|
continue
|
|
395
|
continue
|
|
385
|
# check the common case first
|
|
396
|
# check the common case first
|
|
386
|
if type == 'n':
|
|
397
|
if type == 'n':
|
|
387
|
if not st:
|
|
398
|
if not st:
|
|
388
|
st = os.stat(fn)
|
|
399
|
st = os.stat(fn)
|
|
389
|
if size != st.st_size or (mode ^ st.st_mode) & 0100:
|
|
400
|
if size != st.st_size or (mode ^ st.st_mode) & 0100:
|
|
390
|
modified.append(fn)
|
|
401
|
modified.append(fn)
|
|
391
|
elif time != st.st_mtime:
|
|
402
|
elif time != st.st_mtime:
|
|
392
|
lookup.append(fn)
|
|
403
|
lookup.append(fn)
|
|
393
|
elif type == 'm':
|
|
404
|
elif type == 'm':
|
|
394
|
modified.append(fn)
|
|
405
|
modified.append(fn)
|
|
395
|
elif type == 'a':
|
|
406
|
elif type == 'a':
|
|
396
|
added.append(fn)
|
|
407
|
added.append(fn)
|
|
397
|
elif type == 'r':
|
|
408
|
elif type == 'r':
|
|
398
|
removed.append(fn)
|
|
409
|
removed.append(fn)
|
|
399
|
|
|
410
|
|
|
400
|
return (lookup, modified, added, removed + deleted, unknown)
|
|
411
|
return (lookup, modified, added, removed + deleted, unknown)
|