##// END OF EJS Templates
dirstate-entry: add `modified` property...
Raphaël Gomès -
r50715:3eac9250 default
parent child Browse files
Show More
@@ -1,1261 +1,1261 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Olivia Mackall <olivia@selenic.com> and others
4 Copyright 2008 Olivia Mackall <olivia@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #define PY_SSIZE_T_CLEAN
10 #define PY_SSIZE_T_CLEAN
11 #include <Python.h>
11 #include <Python.h>
12 #include <ctype.h>
12 #include <ctype.h>
13 #include <stddef.h>
13 #include <stddef.h>
14 #include <string.h>
14 #include <string.h>
15
15
16 #include "bitmanipulation.h"
16 #include "bitmanipulation.h"
17 #include "charencode.h"
17 #include "charencode.h"
18 #include "util.h"
18 #include "util.h"
19
19
20 static const char *const versionerrortext = "Python minor version mismatch";
20 static const char *const versionerrortext = "Python minor version mismatch";
21
21
22 static const int dirstate_v1_from_p2 = -2;
22 static const int dirstate_v1_from_p2 = -2;
23 static const int dirstate_v1_nonnormal = -1;
23 static const int dirstate_v1_nonnormal = -1;
24 static const int ambiguous_time = -1;
24 static const int ambiguous_time = -1;
25
25
26 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
26 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
27 {
27 {
28 Py_ssize_t expected_size;
28 Py_ssize_t expected_size;
29
29
30 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
30 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
31 return NULL;
31 return NULL;
32 }
32 }
33
33
34 return _dict_new_presized(expected_size);
34 return _dict_new_presized(expected_size);
35 }
35 }
36
36
37 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
37 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
38 PyObject *kwds)
38 PyObject *kwds)
39 {
39 {
40 /* We do all the initialization here and not a tp_init function because
40 /* We do all the initialization here and not a tp_init function because
41 * dirstate_item is immutable. */
41 * dirstate_item is immutable. */
42 dirstateItemObject *t;
42 dirstateItemObject *t;
43 int wc_tracked;
43 int wc_tracked;
44 int p1_tracked;
44 int p1_tracked;
45 int p2_info;
45 int p2_info;
46 int has_meaningful_data;
46 int has_meaningful_data;
47 int has_meaningful_mtime;
47 int has_meaningful_mtime;
48 int mtime_second_ambiguous;
48 int mtime_second_ambiguous;
49 int mode;
49 int mode;
50 int size;
50 int size;
51 int mtime_s;
51 int mtime_s;
52 int mtime_ns;
52 int mtime_ns;
53 PyObject *parentfiledata;
53 PyObject *parentfiledata;
54 PyObject *mtime;
54 PyObject *mtime;
55 PyObject *fallback_exec;
55 PyObject *fallback_exec;
56 PyObject *fallback_symlink;
56 PyObject *fallback_symlink;
57 static char *keywords_name[] = {
57 static char *keywords_name[] = {
58 "wc_tracked", "p1_tracked", "p2_info",
58 "wc_tracked", "p1_tracked", "p2_info",
59 "has_meaningful_data", "has_meaningful_mtime", "parentfiledata",
59 "has_meaningful_data", "has_meaningful_mtime", "parentfiledata",
60 "fallback_exec", "fallback_symlink", NULL,
60 "fallback_exec", "fallback_symlink", NULL,
61 };
61 };
62 wc_tracked = 0;
62 wc_tracked = 0;
63 p1_tracked = 0;
63 p1_tracked = 0;
64 p2_info = 0;
64 p2_info = 0;
65 has_meaningful_mtime = 1;
65 has_meaningful_mtime = 1;
66 has_meaningful_data = 1;
66 has_meaningful_data = 1;
67 mtime_second_ambiguous = 0;
67 mtime_second_ambiguous = 0;
68 parentfiledata = Py_None;
68 parentfiledata = Py_None;
69 fallback_exec = Py_None;
69 fallback_exec = Py_None;
70 fallback_symlink = Py_None;
70 fallback_symlink = Py_None;
71 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiiiOOO", keywords_name,
71 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiiiOOO", keywords_name,
72 &wc_tracked, &p1_tracked, &p2_info,
72 &wc_tracked, &p1_tracked, &p2_info,
73 &has_meaningful_data,
73 &has_meaningful_data,
74 &has_meaningful_mtime, &parentfiledata,
74 &has_meaningful_mtime, &parentfiledata,
75 &fallback_exec, &fallback_symlink)) {
75 &fallback_exec, &fallback_symlink)) {
76 return NULL;
76 return NULL;
77 }
77 }
78 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
78 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
79 if (!t) {
79 if (!t) {
80 return NULL;
80 return NULL;
81 }
81 }
82
82
83 t->flags = 0;
83 t->flags = 0;
84 if (wc_tracked) {
84 if (wc_tracked) {
85 t->flags |= dirstate_flag_wc_tracked;
85 t->flags |= dirstate_flag_wc_tracked;
86 }
86 }
87 if (p1_tracked) {
87 if (p1_tracked) {
88 t->flags |= dirstate_flag_p1_tracked;
88 t->flags |= dirstate_flag_p1_tracked;
89 }
89 }
90 if (p2_info) {
90 if (p2_info) {
91 t->flags |= dirstate_flag_p2_info;
91 t->flags |= dirstate_flag_p2_info;
92 }
92 }
93
93
94 if (fallback_exec != Py_None) {
94 if (fallback_exec != Py_None) {
95 t->flags |= dirstate_flag_has_fallback_exec;
95 t->flags |= dirstate_flag_has_fallback_exec;
96 if (PyObject_IsTrue(fallback_exec)) {
96 if (PyObject_IsTrue(fallback_exec)) {
97 t->flags |= dirstate_flag_fallback_exec;
97 t->flags |= dirstate_flag_fallback_exec;
98 }
98 }
99 }
99 }
100 if (fallback_symlink != Py_None) {
100 if (fallback_symlink != Py_None) {
101 t->flags |= dirstate_flag_has_fallback_symlink;
101 t->flags |= dirstate_flag_has_fallback_symlink;
102 if (PyObject_IsTrue(fallback_symlink)) {
102 if (PyObject_IsTrue(fallback_symlink)) {
103 t->flags |= dirstate_flag_fallback_symlink;
103 t->flags |= dirstate_flag_fallback_symlink;
104 }
104 }
105 }
105 }
106
106
107 if (parentfiledata != Py_None) {
107 if (parentfiledata != Py_None) {
108 if (!PyArg_ParseTuple(parentfiledata, "iiO", &mode, &size,
108 if (!PyArg_ParseTuple(parentfiledata, "iiO", &mode, &size,
109 &mtime)) {
109 &mtime)) {
110 return NULL;
110 return NULL;
111 }
111 }
112 if (mtime != Py_None) {
112 if (mtime != Py_None) {
113 if (!PyArg_ParseTuple(mtime, "iii", &mtime_s, &mtime_ns,
113 if (!PyArg_ParseTuple(mtime, "iii", &mtime_s, &mtime_ns,
114 &mtime_second_ambiguous)) {
114 &mtime_second_ambiguous)) {
115 return NULL;
115 return NULL;
116 }
116 }
117 } else {
117 } else {
118 has_meaningful_mtime = 0;
118 has_meaningful_mtime = 0;
119 }
119 }
120 } else {
120 } else {
121 has_meaningful_data = 0;
121 has_meaningful_data = 0;
122 has_meaningful_mtime = 0;
122 has_meaningful_mtime = 0;
123 }
123 }
124 if (has_meaningful_data) {
124 if (has_meaningful_data) {
125 t->flags |= dirstate_flag_has_meaningful_data;
125 t->flags |= dirstate_flag_has_meaningful_data;
126 t->mode = mode;
126 t->mode = mode;
127 t->size = size;
127 t->size = size;
128 if (mtime_second_ambiguous) {
128 if (mtime_second_ambiguous) {
129 t->flags |= dirstate_flag_mtime_second_ambiguous;
129 t->flags |= dirstate_flag_mtime_second_ambiguous;
130 }
130 }
131 } else {
131 } else {
132 t->mode = 0;
132 t->mode = 0;
133 t->size = 0;
133 t->size = 0;
134 }
134 }
135 if (has_meaningful_mtime) {
135 if (has_meaningful_mtime) {
136 t->flags |= dirstate_flag_has_mtime;
136 t->flags |= dirstate_flag_has_mtime;
137 t->mtime_s = mtime_s;
137 t->mtime_s = mtime_s;
138 t->mtime_ns = mtime_ns;
138 t->mtime_ns = mtime_ns;
139 } else {
139 } else {
140 t->mtime_s = 0;
140 t->mtime_s = 0;
141 t->mtime_ns = 0;
141 t->mtime_ns = 0;
142 }
142 }
143 return (PyObject *)t;
143 return (PyObject *)t;
144 }
144 }
145
145
146 static void dirstate_item_dealloc(PyObject *o)
146 static void dirstate_item_dealloc(PyObject *o)
147 {
147 {
148 PyObject_Del(o);
148 PyObject_Del(o);
149 }
149 }
150
150
151 static inline bool dirstate_item_c_tracked(dirstateItemObject *self)
151 static inline bool dirstate_item_c_tracked(dirstateItemObject *self)
152 {
152 {
153 return (self->flags & dirstate_flag_wc_tracked);
153 return (self->flags & dirstate_flag_wc_tracked);
154 }
154 }
155
155
156 static inline bool dirstate_item_c_any_tracked(dirstateItemObject *self)
156 static inline bool dirstate_item_c_any_tracked(dirstateItemObject *self)
157 {
157 {
158 const int mask = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
158 const int mask = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
159 dirstate_flag_p2_info;
159 dirstate_flag_p2_info;
160 return (self->flags & mask);
160 return (self->flags & mask);
161 }
161 }
162
162
163 static inline bool dirstate_item_c_added(dirstateItemObject *self)
163 static inline bool dirstate_item_c_added(dirstateItemObject *self)
164 {
164 {
165 const int mask = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
165 const int mask = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
166 dirstate_flag_p2_info);
166 dirstate_flag_p2_info);
167 const int target = dirstate_flag_wc_tracked;
167 const int target = dirstate_flag_wc_tracked;
168 return (self->flags & mask) == target;
168 return (self->flags & mask) == target;
169 }
169 }
170
170
171 static inline bool dirstate_item_c_removed(dirstateItemObject *self)
171 static inline bool dirstate_item_c_removed(dirstateItemObject *self)
172 {
172 {
173 if (self->flags & dirstate_flag_wc_tracked) {
173 if (self->flags & dirstate_flag_wc_tracked) {
174 return false;
174 return false;
175 }
175 }
176 return (self->flags &
176 return (self->flags &
177 (dirstate_flag_p1_tracked | dirstate_flag_p2_info));
177 (dirstate_flag_p1_tracked | dirstate_flag_p2_info));
178 }
178 }
179
179
180 static inline bool dirstate_item_c_merged(dirstateItemObject *self)
180 static inline bool dirstate_item_c_modified(dirstateItemObject *self)
181 {
181 {
182 return ((self->flags & dirstate_flag_wc_tracked) &&
182 return ((self->flags & dirstate_flag_wc_tracked) &&
183 (self->flags & dirstate_flag_p1_tracked) &&
183 (self->flags & dirstate_flag_p1_tracked) &&
184 (self->flags & dirstate_flag_p2_info));
184 (self->flags & dirstate_flag_p2_info));
185 }
185 }
186
186
187 static inline bool dirstate_item_c_from_p2(dirstateItemObject *self)
187 static inline bool dirstate_item_c_from_p2(dirstateItemObject *self)
188 {
188 {
189 return ((self->flags & dirstate_flag_wc_tracked) &&
189 return ((self->flags & dirstate_flag_wc_tracked) &&
190 !(self->flags & dirstate_flag_p1_tracked) &&
190 !(self->flags & dirstate_flag_p1_tracked) &&
191 (self->flags & dirstate_flag_p2_info));
191 (self->flags & dirstate_flag_p2_info));
192 }
192 }
193
193
194 static inline char dirstate_item_c_v1_state(dirstateItemObject *self)
194 static inline char dirstate_item_c_v1_state(dirstateItemObject *self)
195 {
195 {
196 if (dirstate_item_c_removed(self)) {
196 if (dirstate_item_c_removed(self)) {
197 return 'r';
197 return 'r';
198 } else if (dirstate_item_c_merged(self)) {
198 } else if (dirstate_item_c_modified(self)) {
199 return 'm';
199 return 'm';
200 } else if (dirstate_item_c_added(self)) {
200 } else if (dirstate_item_c_added(self)) {
201 return 'a';
201 return 'a';
202 } else {
202 } else {
203 return 'n';
203 return 'n';
204 }
204 }
205 }
205 }
206
206
207 static inline bool dirstate_item_c_has_fallback_exec(dirstateItemObject *self)
207 static inline bool dirstate_item_c_has_fallback_exec(dirstateItemObject *self)
208 {
208 {
209 return (bool)self->flags & dirstate_flag_has_fallback_exec;
209 return (bool)self->flags & dirstate_flag_has_fallback_exec;
210 }
210 }
211
211
212 static inline bool
212 static inline bool
213 dirstate_item_c_has_fallback_symlink(dirstateItemObject *self)
213 dirstate_item_c_has_fallback_symlink(dirstateItemObject *self)
214 {
214 {
215 return (bool)self->flags & dirstate_flag_has_fallback_symlink;
215 return (bool)self->flags & dirstate_flag_has_fallback_symlink;
216 }
216 }
217
217
218 static inline int dirstate_item_c_v1_mode(dirstateItemObject *self)
218 static inline int dirstate_item_c_v1_mode(dirstateItemObject *self)
219 {
219 {
220 if (self->flags & dirstate_flag_has_meaningful_data) {
220 if (self->flags & dirstate_flag_has_meaningful_data) {
221 return self->mode;
221 return self->mode;
222 } else {
222 } else {
223 return 0;
223 return 0;
224 }
224 }
225 }
225 }
226
226
227 static inline int dirstate_item_c_v1_size(dirstateItemObject *self)
227 static inline int dirstate_item_c_v1_size(dirstateItemObject *self)
228 {
228 {
229 if (!(self->flags & dirstate_flag_wc_tracked) &&
229 if (!(self->flags & dirstate_flag_wc_tracked) &&
230 (self->flags & dirstate_flag_p2_info)) {
230 (self->flags & dirstate_flag_p2_info)) {
231 if (self->flags & dirstate_flag_p1_tracked) {
231 if (self->flags & dirstate_flag_p1_tracked) {
232 return dirstate_v1_nonnormal;
232 return dirstate_v1_nonnormal;
233 } else {
233 } else {
234 return dirstate_v1_from_p2;
234 return dirstate_v1_from_p2;
235 }
235 }
236 } else if (dirstate_item_c_removed(self)) {
236 } else if (dirstate_item_c_removed(self)) {
237 return 0;
237 return 0;
238 } else if (self->flags & dirstate_flag_p2_info) {
238 } else if (self->flags & dirstate_flag_p2_info) {
239 return dirstate_v1_from_p2;
239 return dirstate_v1_from_p2;
240 } else if (dirstate_item_c_added(self)) {
240 } else if (dirstate_item_c_added(self)) {
241 return dirstate_v1_nonnormal;
241 return dirstate_v1_nonnormal;
242 } else if (self->flags & dirstate_flag_has_meaningful_data) {
242 } else if (self->flags & dirstate_flag_has_meaningful_data) {
243 return self->size;
243 return self->size;
244 } else {
244 } else {
245 return dirstate_v1_nonnormal;
245 return dirstate_v1_nonnormal;
246 }
246 }
247 }
247 }
248
248
249 static inline int dirstate_item_c_v1_mtime(dirstateItemObject *self)
249 static inline int dirstate_item_c_v1_mtime(dirstateItemObject *self)
250 {
250 {
251 if (dirstate_item_c_removed(self)) {
251 if (dirstate_item_c_removed(self)) {
252 return 0;
252 return 0;
253 } else if (!(self->flags & dirstate_flag_has_mtime) ||
253 } else if (!(self->flags & dirstate_flag_has_mtime) ||
254 !(self->flags & dirstate_flag_p1_tracked) ||
254 !(self->flags & dirstate_flag_p1_tracked) ||
255 !(self->flags & dirstate_flag_wc_tracked) ||
255 !(self->flags & dirstate_flag_wc_tracked) ||
256 (self->flags & dirstate_flag_p2_info) ||
256 (self->flags & dirstate_flag_p2_info) ||
257 (self->flags & dirstate_flag_mtime_second_ambiguous)) {
257 (self->flags & dirstate_flag_mtime_second_ambiguous)) {
258 return ambiguous_time;
258 return ambiguous_time;
259 } else {
259 } else {
260 return self->mtime_s;
260 return self->mtime_s;
261 }
261 }
262 }
262 }
263
263
264 static PyObject *dirstate_item_v2_data(dirstateItemObject *self)
264 static PyObject *dirstate_item_v2_data(dirstateItemObject *self)
265 {
265 {
266 int flags = self->flags;
266 int flags = self->flags;
267 int mode = dirstate_item_c_v1_mode(self);
267 int mode = dirstate_item_c_v1_mode(self);
268 #ifdef S_IXUSR
268 #ifdef S_IXUSR
269 /* This is for platforms with an exec bit */
269 /* This is for platforms with an exec bit */
270 if ((mode & S_IXUSR) != 0) {
270 if ((mode & S_IXUSR) != 0) {
271 flags |= dirstate_flag_mode_exec_perm;
271 flags |= dirstate_flag_mode_exec_perm;
272 } else {
272 } else {
273 flags &= ~dirstate_flag_mode_exec_perm;
273 flags &= ~dirstate_flag_mode_exec_perm;
274 }
274 }
275 #else
275 #else
276 flags &= ~dirstate_flag_mode_exec_perm;
276 flags &= ~dirstate_flag_mode_exec_perm;
277 #endif
277 #endif
278 #ifdef S_ISLNK
278 #ifdef S_ISLNK
279 /* This is for platforms with support for symlinks */
279 /* This is for platforms with support for symlinks */
280 if (S_ISLNK(mode)) {
280 if (S_ISLNK(mode)) {
281 flags |= dirstate_flag_mode_is_symlink;
281 flags |= dirstate_flag_mode_is_symlink;
282 } else {
282 } else {
283 flags &= ~dirstate_flag_mode_is_symlink;
283 flags &= ~dirstate_flag_mode_is_symlink;
284 }
284 }
285 #else
285 #else
286 flags &= ~dirstate_flag_mode_is_symlink;
286 flags &= ~dirstate_flag_mode_is_symlink;
287 #endif
287 #endif
288 return Py_BuildValue("iiii", flags, self->size, self->mtime_s,
288 return Py_BuildValue("iiii", flags, self->size, self->mtime_s,
289 self->mtime_ns);
289 self->mtime_ns);
290 };
290 };
291
291
292 static PyObject *dirstate_item_mtime_likely_equal_to(dirstateItemObject *self,
292 static PyObject *dirstate_item_mtime_likely_equal_to(dirstateItemObject *self,
293 PyObject *other)
293 PyObject *other)
294 {
294 {
295 int other_s;
295 int other_s;
296 int other_ns;
296 int other_ns;
297 int other_second_ambiguous;
297 int other_second_ambiguous;
298 if (!PyArg_ParseTuple(other, "iii", &other_s, &other_ns,
298 if (!PyArg_ParseTuple(other, "iii", &other_s, &other_ns,
299 &other_second_ambiguous)) {
299 &other_second_ambiguous)) {
300 return NULL;
300 return NULL;
301 }
301 }
302 if (!(self->flags & dirstate_flag_has_mtime)) {
302 if (!(self->flags & dirstate_flag_has_mtime)) {
303 Py_RETURN_FALSE;
303 Py_RETURN_FALSE;
304 }
304 }
305 if (self->mtime_s != other_s) {
305 if (self->mtime_s != other_s) {
306 Py_RETURN_FALSE;
306 Py_RETURN_FALSE;
307 }
307 }
308 if (self->mtime_ns == 0 || other_ns == 0) {
308 if (self->mtime_ns == 0 || other_ns == 0) {
309 if (self->flags & dirstate_flag_mtime_second_ambiguous) {
309 if (self->flags & dirstate_flag_mtime_second_ambiguous) {
310 Py_RETURN_FALSE;
310 Py_RETURN_FALSE;
311 } else {
311 } else {
312 Py_RETURN_TRUE;
312 Py_RETURN_TRUE;
313 }
313 }
314 }
314 }
315 if (self->mtime_ns == other_ns) {
315 if (self->mtime_ns == other_ns) {
316 Py_RETURN_TRUE;
316 Py_RETURN_TRUE;
317 } else {
317 } else {
318 Py_RETURN_FALSE;
318 Py_RETURN_FALSE;
319 }
319 }
320 };
320 };
321
321
322 /* This will never change since it's bound to V1
322 /* This will never change since it's bound to V1
323 */
323 */
324 static inline dirstateItemObject *
324 static inline dirstateItemObject *
325 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
325 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
326 {
326 {
327 dirstateItemObject *t =
327 dirstateItemObject *t =
328 PyObject_New(dirstateItemObject, &dirstateItemType);
328 PyObject_New(dirstateItemObject, &dirstateItemType);
329 if (!t) {
329 if (!t) {
330 return NULL;
330 return NULL;
331 }
331 }
332 t->flags = 0;
332 t->flags = 0;
333 t->mode = 0;
333 t->mode = 0;
334 t->size = 0;
334 t->size = 0;
335 t->mtime_s = 0;
335 t->mtime_s = 0;
336 t->mtime_ns = 0;
336 t->mtime_ns = 0;
337
337
338 if (state == 'm') {
338 if (state == 'm') {
339 t->flags = (dirstate_flag_wc_tracked |
339 t->flags = (dirstate_flag_wc_tracked |
340 dirstate_flag_p1_tracked | dirstate_flag_p2_info);
340 dirstate_flag_p1_tracked | dirstate_flag_p2_info);
341 } else if (state == 'a') {
341 } else if (state == 'a') {
342 t->flags = dirstate_flag_wc_tracked;
342 t->flags = dirstate_flag_wc_tracked;
343 } else if (state == 'r') {
343 } else if (state == 'r') {
344 if (size == dirstate_v1_nonnormal) {
344 if (size == dirstate_v1_nonnormal) {
345 t->flags =
345 t->flags =
346 dirstate_flag_p1_tracked | dirstate_flag_p2_info;
346 dirstate_flag_p1_tracked | dirstate_flag_p2_info;
347 } else if (size == dirstate_v1_from_p2) {
347 } else if (size == dirstate_v1_from_p2) {
348 t->flags = dirstate_flag_p2_info;
348 t->flags = dirstate_flag_p2_info;
349 } else {
349 } else {
350 t->flags = dirstate_flag_p1_tracked;
350 t->flags = dirstate_flag_p1_tracked;
351 }
351 }
352 } else if (state == 'n') {
352 } else if (state == 'n') {
353 if (size == dirstate_v1_from_p2) {
353 if (size == dirstate_v1_from_p2) {
354 t->flags =
354 t->flags =
355 dirstate_flag_wc_tracked | dirstate_flag_p2_info;
355 dirstate_flag_wc_tracked | dirstate_flag_p2_info;
356 } else if (size == dirstate_v1_nonnormal) {
356 } else if (size == dirstate_v1_nonnormal) {
357 t->flags =
357 t->flags =
358 dirstate_flag_wc_tracked | dirstate_flag_p1_tracked;
358 dirstate_flag_wc_tracked | dirstate_flag_p1_tracked;
359 } else if (mtime == ambiguous_time) {
359 } else if (mtime == ambiguous_time) {
360 t->flags = (dirstate_flag_wc_tracked |
360 t->flags = (dirstate_flag_wc_tracked |
361 dirstate_flag_p1_tracked |
361 dirstate_flag_p1_tracked |
362 dirstate_flag_has_meaningful_data);
362 dirstate_flag_has_meaningful_data);
363 t->mode = mode;
363 t->mode = mode;
364 t->size = size;
364 t->size = size;
365 } else {
365 } else {
366 t->flags = (dirstate_flag_wc_tracked |
366 t->flags = (dirstate_flag_wc_tracked |
367 dirstate_flag_p1_tracked |
367 dirstate_flag_p1_tracked |
368 dirstate_flag_has_meaningful_data |
368 dirstate_flag_has_meaningful_data |
369 dirstate_flag_has_mtime);
369 dirstate_flag_has_mtime);
370 t->mode = mode;
370 t->mode = mode;
371 t->size = size;
371 t->size = size;
372 t->mtime_s = mtime;
372 t->mtime_s = mtime;
373 }
373 }
374 } else {
374 } else {
375 PyErr_Format(PyExc_RuntimeError,
375 PyErr_Format(PyExc_RuntimeError,
376 "unknown state: `%c` (%d, %d, %d)", state, mode,
376 "unknown state: `%c` (%d, %d, %d)", state, mode,
377 size, mtime);
377 size, mtime);
378 Py_DECREF(t);
378 Py_DECREF(t);
379 return NULL;
379 return NULL;
380 }
380 }
381
381
382 return t;
382 return t;
383 }
383 }
384
384
385 static PyObject *dirstate_item_from_v2_meth(PyTypeObject *subtype,
385 static PyObject *dirstate_item_from_v2_meth(PyTypeObject *subtype,
386 PyObject *args)
386 PyObject *args)
387 {
387 {
388 dirstateItemObject *t =
388 dirstateItemObject *t =
389 PyObject_New(dirstateItemObject, &dirstateItemType);
389 PyObject_New(dirstateItemObject, &dirstateItemType);
390 if (!t) {
390 if (!t) {
391 return NULL;
391 return NULL;
392 }
392 }
393 if (!PyArg_ParseTuple(args, "iiii", &t->flags, &t->size, &t->mtime_s,
393 if (!PyArg_ParseTuple(args, "iiii", &t->flags, &t->size, &t->mtime_s,
394 &t->mtime_ns)) {
394 &t->mtime_ns)) {
395 return NULL;
395 return NULL;
396 }
396 }
397 if (t->flags & dirstate_flag_expected_state_is_modified) {
397 if (t->flags & dirstate_flag_expected_state_is_modified) {
398 t->flags &= ~(dirstate_flag_expected_state_is_modified |
398 t->flags &= ~(dirstate_flag_expected_state_is_modified |
399 dirstate_flag_has_meaningful_data |
399 dirstate_flag_has_meaningful_data |
400 dirstate_flag_has_mtime);
400 dirstate_flag_has_mtime);
401 }
401 }
402 t->mode = 0;
402 t->mode = 0;
403 if (t->flags & dirstate_flag_has_meaningful_data) {
403 if (t->flags & dirstate_flag_has_meaningful_data) {
404 if (t->flags & dirstate_flag_mode_exec_perm) {
404 if (t->flags & dirstate_flag_mode_exec_perm) {
405 t->mode = 0755;
405 t->mode = 0755;
406 } else {
406 } else {
407 t->mode = 0644;
407 t->mode = 0644;
408 }
408 }
409 if (t->flags & dirstate_flag_mode_is_symlink) {
409 if (t->flags & dirstate_flag_mode_is_symlink) {
410 t->mode |= S_IFLNK;
410 t->mode |= S_IFLNK;
411 } else {
411 } else {
412 t->mode |= S_IFREG;
412 t->mode |= S_IFREG;
413 }
413 }
414 }
414 }
415 return (PyObject *)t;
415 return (PyObject *)t;
416 };
416 };
417
417
418 /* This means the next status call will have to actually check its content
418 /* This means the next status call will have to actually check its content
419 to make sure it is correct. */
419 to make sure it is correct. */
420 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
420 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
421 {
421 {
422 self->flags &= ~dirstate_flag_has_mtime;
422 self->flags &= ~dirstate_flag_has_mtime;
423 Py_RETURN_NONE;
423 Py_RETURN_NONE;
424 }
424 }
425
425
426 /* See docstring of the python implementation for details */
426 /* See docstring of the python implementation for details */
427 static PyObject *dirstate_item_set_clean(dirstateItemObject *self,
427 static PyObject *dirstate_item_set_clean(dirstateItemObject *self,
428 PyObject *args)
428 PyObject *args)
429 {
429 {
430 int size, mode, mtime_s, mtime_ns, mtime_second_ambiguous;
430 int size, mode, mtime_s, mtime_ns, mtime_second_ambiguous;
431 PyObject *mtime;
431 PyObject *mtime;
432 mtime_s = 0;
432 mtime_s = 0;
433 mtime_ns = 0;
433 mtime_ns = 0;
434 mtime_second_ambiguous = 0;
434 mtime_second_ambiguous = 0;
435 if (!PyArg_ParseTuple(args, "iiO", &mode, &size, &mtime)) {
435 if (!PyArg_ParseTuple(args, "iiO", &mode, &size, &mtime)) {
436 return NULL;
436 return NULL;
437 }
437 }
438 if (mtime != Py_None) {
438 if (mtime != Py_None) {
439 if (!PyArg_ParseTuple(mtime, "iii", &mtime_s, &mtime_ns,
439 if (!PyArg_ParseTuple(mtime, "iii", &mtime_s, &mtime_ns,
440 &mtime_second_ambiguous)) {
440 &mtime_second_ambiguous)) {
441 return NULL;
441 return NULL;
442 }
442 }
443 } else {
443 } else {
444 self->flags &= ~dirstate_flag_has_mtime;
444 self->flags &= ~dirstate_flag_has_mtime;
445 }
445 }
446 self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
446 self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
447 dirstate_flag_has_meaningful_data |
447 dirstate_flag_has_meaningful_data |
448 dirstate_flag_has_mtime;
448 dirstate_flag_has_mtime;
449 if (mtime_second_ambiguous) {
449 if (mtime_second_ambiguous) {
450 self->flags |= dirstate_flag_mtime_second_ambiguous;
450 self->flags |= dirstate_flag_mtime_second_ambiguous;
451 }
451 }
452 self->mode = mode;
452 self->mode = mode;
453 self->size = size;
453 self->size = size;
454 self->mtime_s = mtime_s;
454 self->mtime_s = mtime_s;
455 self->mtime_ns = mtime_ns;
455 self->mtime_ns = mtime_ns;
456 Py_RETURN_NONE;
456 Py_RETURN_NONE;
457 }
457 }
458
458
459 static PyObject *dirstate_item_set_tracked(dirstateItemObject *self)
459 static PyObject *dirstate_item_set_tracked(dirstateItemObject *self)
460 {
460 {
461 self->flags |= dirstate_flag_wc_tracked;
461 self->flags |= dirstate_flag_wc_tracked;
462 self->flags &= ~dirstate_flag_has_mtime;
462 self->flags &= ~dirstate_flag_has_mtime;
463 Py_RETURN_NONE;
463 Py_RETURN_NONE;
464 }
464 }
465
465
466 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
466 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
467 {
467 {
468 self->flags &= ~dirstate_flag_wc_tracked;
468 self->flags &= ~dirstate_flag_wc_tracked;
469 self->flags &= ~dirstate_flag_has_meaningful_data;
469 self->flags &= ~dirstate_flag_has_meaningful_data;
470 self->flags &= ~dirstate_flag_has_mtime;
470 self->flags &= ~dirstate_flag_has_mtime;
471 self->mode = 0;
471 self->mode = 0;
472 self->size = 0;
472 self->size = 0;
473 self->mtime_s = 0;
473 self->mtime_s = 0;
474 self->mtime_ns = 0;
474 self->mtime_ns = 0;
475 Py_RETURN_NONE;
475 Py_RETURN_NONE;
476 }
476 }
477
477
478 static PyObject *dirstate_item_drop_merge_data(dirstateItemObject *self)
478 static PyObject *dirstate_item_drop_merge_data(dirstateItemObject *self)
479 {
479 {
480 if (self->flags & dirstate_flag_p2_info) {
480 if (self->flags & dirstate_flag_p2_info) {
481 self->flags &= ~(dirstate_flag_p2_info |
481 self->flags &= ~(dirstate_flag_p2_info |
482 dirstate_flag_has_meaningful_data |
482 dirstate_flag_has_meaningful_data |
483 dirstate_flag_has_mtime);
483 dirstate_flag_has_mtime);
484 self->mode = 0;
484 self->mode = 0;
485 self->size = 0;
485 self->size = 0;
486 self->mtime_s = 0;
486 self->mtime_s = 0;
487 self->mtime_ns = 0;
487 self->mtime_ns = 0;
488 }
488 }
489 Py_RETURN_NONE;
489 Py_RETURN_NONE;
490 }
490 }
491 static PyMethodDef dirstate_item_methods[] = {
491 static PyMethodDef dirstate_item_methods[] = {
492 {"v2_data", (PyCFunction)dirstate_item_v2_data, METH_NOARGS,
492 {"v2_data", (PyCFunction)dirstate_item_v2_data, METH_NOARGS,
493 "return data suitable for v2 serialization"},
493 "return data suitable for v2 serialization"},
494 {"mtime_likely_equal_to", (PyCFunction)dirstate_item_mtime_likely_equal_to,
494 {"mtime_likely_equal_to", (PyCFunction)dirstate_item_mtime_likely_equal_to,
495 METH_O, "True if the stored mtime is likely equal to the given mtime"},
495 METH_O, "True if the stored mtime is likely equal to the given mtime"},
496 {"from_v2_data", (PyCFunction)dirstate_item_from_v2_meth,
496 {"from_v2_data", (PyCFunction)dirstate_item_from_v2_meth,
497 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V2 data"},
497 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V2 data"},
498 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
498 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
499 METH_NOARGS, "mark a file as \"possibly dirty\""},
499 METH_NOARGS, "mark a file as \"possibly dirty\""},
500 {"set_clean", (PyCFunction)dirstate_item_set_clean, METH_VARARGS,
500 {"set_clean", (PyCFunction)dirstate_item_set_clean, METH_VARARGS,
501 "mark a file as \"clean\""},
501 "mark a file as \"clean\""},
502 {"set_tracked", (PyCFunction)dirstate_item_set_tracked, METH_NOARGS,
502 {"set_tracked", (PyCFunction)dirstate_item_set_tracked, METH_NOARGS,
503 "mark a file as \"tracked\""},
503 "mark a file as \"tracked\""},
504 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
504 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
505 "mark a file as \"untracked\""},
505 "mark a file as \"untracked\""},
506 {"drop_merge_data", (PyCFunction)dirstate_item_drop_merge_data, METH_NOARGS,
506 {"drop_merge_data", (PyCFunction)dirstate_item_drop_merge_data, METH_NOARGS,
507 "remove all \"merge-only\" from a DirstateItem"},
507 "remove all \"merge-only\" from a DirstateItem"},
508 {NULL} /* Sentinel */
508 {NULL} /* Sentinel */
509 };
509 };
510
510
511 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
511 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
512 {
512 {
513 return PyLong_FromLong(dirstate_item_c_v1_mode(self));
513 return PyLong_FromLong(dirstate_item_c_v1_mode(self));
514 };
514 };
515
515
516 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
516 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
517 {
517 {
518 return PyLong_FromLong(dirstate_item_c_v1_size(self));
518 return PyLong_FromLong(dirstate_item_c_v1_size(self));
519 };
519 };
520
520
521 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
521 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
522 {
522 {
523 return PyLong_FromLong(dirstate_item_c_v1_mtime(self));
523 return PyLong_FromLong(dirstate_item_c_v1_mtime(self));
524 };
524 };
525
525
526 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
526 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
527 {
527 {
528 char state = dirstate_item_c_v1_state(self);
528 char state = dirstate_item_c_v1_state(self);
529 return PyBytes_FromStringAndSize(&state, 1);
529 return PyBytes_FromStringAndSize(&state, 1);
530 };
530 };
531
531
532 static PyObject *dirstate_item_get_has_fallback_exec(dirstateItemObject *self)
532 static PyObject *dirstate_item_get_has_fallback_exec(dirstateItemObject *self)
533 {
533 {
534 if (dirstate_item_c_has_fallback_exec(self)) {
534 if (dirstate_item_c_has_fallback_exec(self)) {
535 Py_RETURN_TRUE;
535 Py_RETURN_TRUE;
536 } else {
536 } else {
537 Py_RETURN_FALSE;
537 Py_RETURN_FALSE;
538 }
538 }
539 };
539 };
540
540
541 static PyObject *dirstate_item_get_fallback_exec(dirstateItemObject *self)
541 static PyObject *dirstate_item_get_fallback_exec(dirstateItemObject *self)
542 {
542 {
543 if (dirstate_item_c_has_fallback_exec(self)) {
543 if (dirstate_item_c_has_fallback_exec(self)) {
544 if (self->flags & dirstate_flag_fallback_exec) {
544 if (self->flags & dirstate_flag_fallback_exec) {
545 Py_RETURN_TRUE;
545 Py_RETURN_TRUE;
546 } else {
546 } else {
547 Py_RETURN_FALSE;
547 Py_RETURN_FALSE;
548 }
548 }
549 } else {
549 } else {
550 Py_RETURN_NONE;
550 Py_RETURN_NONE;
551 }
551 }
552 };
552 };
553
553
554 static int dirstate_item_set_fallback_exec(dirstateItemObject *self,
554 static int dirstate_item_set_fallback_exec(dirstateItemObject *self,
555 PyObject *value)
555 PyObject *value)
556 {
556 {
557 if ((value == Py_None) || (value == NULL)) {
557 if ((value == Py_None) || (value == NULL)) {
558 self->flags &= ~dirstate_flag_has_fallback_exec;
558 self->flags &= ~dirstate_flag_has_fallback_exec;
559 } else {
559 } else {
560 self->flags |= dirstate_flag_has_fallback_exec;
560 self->flags |= dirstate_flag_has_fallback_exec;
561 if (PyObject_IsTrue(value)) {
561 if (PyObject_IsTrue(value)) {
562 self->flags |= dirstate_flag_fallback_exec;
562 self->flags |= dirstate_flag_fallback_exec;
563 } else {
563 } else {
564 self->flags &= ~dirstate_flag_fallback_exec;
564 self->flags &= ~dirstate_flag_fallback_exec;
565 }
565 }
566 }
566 }
567 return 0;
567 return 0;
568 };
568 };
569
569
570 static PyObject *
570 static PyObject *
571 dirstate_item_get_has_fallback_symlink(dirstateItemObject *self)
571 dirstate_item_get_has_fallback_symlink(dirstateItemObject *self)
572 {
572 {
573 if (dirstate_item_c_has_fallback_symlink(self)) {
573 if (dirstate_item_c_has_fallback_symlink(self)) {
574 Py_RETURN_TRUE;
574 Py_RETURN_TRUE;
575 } else {
575 } else {
576 Py_RETURN_FALSE;
576 Py_RETURN_FALSE;
577 }
577 }
578 };
578 };
579
579
580 static PyObject *dirstate_item_get_fallback_symlink(dirstateItemObject *self)
580 static PyObject *dirstate_item_get_fallback_symlink(dirstateItemObject *self)
581 {
581 {
582 if (dirstate_item_c_has_fallback_symlink(self)) {
582 if (dirstate_item_c_has_fallback_symlink(self)) {
583 if (self->flags & dirstate_flag_fallback_symlink) {
583 if (self->flags & dirstate_flag_fallback_symlink) {
584 Py_RETURN_TRUE;
584 Py_RETURN_TRUE;
585 } else {
585 } else {
586 Py_RETURN_FALSE;
586 Py_RETURN_FALSE;
587 }
587 }
588 } else {
588 } else {
589 Py_RETURN_NONE;
589 Py_RETURN_NONE;
590 }
590 }
591 };
591 };
592
592
593 static int dirstate_item_set_fallback_symlink(dirstateItemObject *self,
593 static int dirstate_item_set_fallback_symlink(dirstateItemObject *self,
594 PyObject *value)
594 PyObject *value)
595 {
595 {
596 if ((value == Py_None) || (value == NULL)) {
596 if ((value == Py_None) || (value == NULL)) {
597 self->flags &= ~dirstate_flag_has_fallback_symlink;
597 self->flags &= ~dirstate_flag_has_fallback_symlink;
598 } else {
598 } else {
599 self->flags |= dirstate_flag_has_fallback_symlink;
599 self->flags |= dirstate_flag_has_fallback_symlink;
600 if (PyObject_IsTrue(value)) {
600 if (PyObject_IsTrue(value)) {
601 self->flags |= dirstate_flag_fallback_symlink;
601 self->flags |= dirstate_flag_fallback_symlink;
602 } else {
602 } else {
603 self->flags &= ~dirstate_flag_fallback_symlink;
603 self->flags &= ~dirstate_flag_fallback_symlink;
604 }
604 }
605 }
605 }
606 return 0;
606 return 0;
607 };
607 };
608
608
609 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
609 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
610 {
610 {
611 if (dirstate_item_c_tracked(self)) {
611 if (dirstate_item_c_tracked(self)) {
612 Py_RETURN_TRUE;
612 Py_RETURN_TRUE;
613 } else {
613 } else {
614 Py_RETURN_FALSE;
614 Py_RETURN_FALSE;
615 }
615 }
616 };
616 };
617 static PyObject *dirstate_item_get_p1_tracked(dirstateItemObject *self)
617 static PyObject *dirstate_item_get_p1_tracked(dirstateItemObject *self)
618 {
618 {
619 if (self->flags & dirstate_flag_p1_tracked) {
619 if (self->flags & dirstate_flag_p1_tracked) {
620 Py_RETURN_TRUE;
620 Py_RETURN_TRUE;
621 } else {
621 } else {
622 Py_RETURN_FALSE;
622 Py_RETURN_FALSE;
623 }
623 }
624 };
624 };
625
625
626 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
626 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
627 {
627 {
628 if (dirstate_item_c_added(self)) {
628 if (dirstate_item_c_added(self)) {
629 Py_RETURN_TRUE;
629 Py_RETURN_TRUE;
630 } else {
630 } else {
631 Py_RETURN_FALSE;
631 Py_RETURN_FALSE;
632 }
632 }
633 };
633 };
634
634
635 static PyObject *dirstate_item_get_p2_info(dirstateItemObject *self)
635 static PyObject *dirstate_item_get_p2_info(dirstateItemObject *self)
636 {
636 {
637 if (self->flags & dirstate_flag_wc_tracked &&
637 if (self->flags & dirstate_flag_wc_tracked &&
638 self->flags & dirstate_flag_p2_info) {
638 self->flags & dirstate_flag_p2_info) {
639 Py_RETURN_TRUE;
639 Py_RETURN_TRUE;
640 } else {
640 } else {
641 Py_RETURN_FALSE;
641 Py_RETURN_FALSE;
642 }
642 }
643 };
643 };
644
644
645 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
645 static PyObject *dirstate_item_get_modified(dirstateItemObject *self)
646 {
646 {
647 if (dirstate_item_c_merged(self)) {
647 if (dirstate_item_c_modified(self)) {
648 Py_RETURN_TRUE;
648 Py_RETURN_TRUE;
649 } else {
649 } else {
650 Py_RETURN_FALSE;
650 Py_RETURN_FALSE;
651 }
651 }
652 };
652 };
653
653
654 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
654 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
655 {
655 {
656 if (dirstate_item_c_from_p2(self)) {
656 if (dirstate_item_c_from_p2(self)) {
657 Py_RETURN_TRUE;
657 Py_RETURN_TRUE;
658 } else {
658 } else {
659 Py_RETURN_FALSE;
659 Py_RETURN_FALSE;
660 }
660 }
661 };
661 };
662
662
663 static PyObject *dirstate_item_get_maybe_clean(dirstateItemObject *self)
663 static PyObject *dirstate_item_get_maybe_clean(dirstateItemObject *self)
664 {
664 {
665 if (!(self->flags & dirstate_flag_wc_tracked)) {
665 if (!(self->flags & dirstate_flag_wc_tracked)) {
666 Py_RETURN_FALSE;
666 Py_RETURN_FALSE;
667 } else if (!(self->flags & dirstate_flag_p1_tracked)) {
667 } else if (!(self->flags & dirstate_flag_p1_tracked)) {
668 Py_RETURN_FALSE;
668 Py_RETURN_FALSE;
669 } else if (self->flags & dirstate_flag_p2_info) {
669 } else if (self->flags & dirstate_flag_p2_info) {
670 Py_RETURN_FALSE;
670 Py_RETURN_FALSE;
671 } else {
671 } else {
672 Py_RETURN_TRUE;
672 Py_RETURN_TRUE;
673 }
673 }
674 };
674 };
675
675
676 static PyObject *dirstate_item_get_any_tracked(dirstateItemObject *self)
676 static PyObject *dirstate_item_get_any_tracked(dirstateItemObject *self)
677 {
677 {
678 if (dirstate_item_c_any_tracked(self)) {
678 if (dirstate_item_c_any_tracked(self)) {
679 Py_RETURN_TRUE;
679 Py_RETURN_TRUE;
680 } else {
680 } else {
681 Py_RETURN_FALSE;
681 Py_RETURN_FALSE;
682 }
682 }
683 };
683 };
684
684
685 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
685 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
686 {
686 {
687 if (dirstate_item_c_removed(self)) {
687 if (dirstate_item_c_removed(self)) {
688 Py_RETURN_TRUE;
688 Py_RETURN_TRUE;
689 } else {
689 } else {
690 Py_RETURN_FALSE;
690 Py_RETURN_FALSE;
691 }
691 }
692 };
692 };
693
693
694 static PyGetSetDef dirstate_item_getset[] = {
694 static PyGetSetDef dirstate_item_getset[] = {
695 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
695 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
696 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
696 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
697 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
697 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
698 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
698 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
699 {"has_fallback_exec", (getter)dirstate_item_get_has_fallback_exec, NULL,
699 {"has_fallback_exec", (getter)dirstate_item_get_has_fallback_exec, NULL,
700 "has_fallback_exec", NULL},
700 "has_fallback_exec", NULL},
701 {"fallback_exec", (getter)dirstate_item_get_fallback_exec,
701 {"fallback_exec", (getter)dirstate_item_get_fallback_exec,
702 (setter)dirstate_item_set_fallback_exec, "fallback_exec", NULL},
702 (setter)dirstate_item_set_fallback_exec, "fallback_exec", NULL},
703 {"has_fallback_symlink", (getter)dirstate_item_get_has_fallback_symlink,
703 {"has_fallback_symlink", (getter)dirstate_item_get_has_fallback_symlink,
704 NULL, "has_fallback_symlink", NULL},
704 NULL, "has_fallback_symlink", NULL},
705 {"fallback_symlink", (getter)dirstate_item_get_fallback_symlink,
705 {"fallback_symlink", (getter)dirstate_item_get_fallback_symlink,
706 (setter)dirstate_item_set_fallback_symlink, "fallback_symlink", NULL},
706 (setter)dirstate_item_set_fallback_symlink, "fallback_symlink", NULL},
707 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
707 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
708 {"p1_tracked", (getter)dirstate_item_get_p1_tracked, NULL, "p1_tracked",
708 {"p1_tracked", (getter)dirstate_item_get_p1_tracked, NULL, "p1_tracked",
709 NULL},
709 NULL},
710 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
710 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
711 {"p2_info", (getter)dirstate_item_get_p2_info, NULL, "p2_info", NULL},
711 {"p2_info", (getter)dirstate_item_get_p2_info, NULL, "p2_info", NULL},
712 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
712 {"modified", (getter)dirstate_item_get_modified, NULL, "modified", NULL},
713 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
713 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
714 {"maybe_clean", (getter)dirstate_item_get_maybe_clean, NULL, "maybe_clean",
714 {"maybe_clean", (getter)dirstate_item_get_maybe_clean, NULL, "maybe_clean",
715 NULL},
715 NULL},
716 {"any_tracked", (getter)dirstate_item_get_any_tracked, NULL, "any_tracked",
716 {"any_tracked", (getter)dirstate_item_get_any_tracked, NULL, "any_tracked",
717 NULL},
717 NULL},
718 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
718 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
719 {NULL} /* Sentinel */
719 {NULL} /* Sentinel */
720 };
720 };
721
721
722 PyTypeObject dirstateItemType = {
722 PyTypeObject dirstateItemType = {
723 PyVarObject_HEAD_INIT(NULL, 0) /* header */
723 PyVarObject_HEAD_INIT(NULL, 0) /* header */
724 "dirstate_tuple", /* tp_name */
724 "dirstate_tuple", /* tp_name */
725 sizeof(dirstateItemObject), /* tp_basicsize */
725 sizeof(dirstateItemObject), /* tp_basicsize */
726 0, /* tp_itemsize */
726 0, /* tp_itemsize */
727 (destructor)dirstate_item_dealloc, /* tp_dealloc */
727 (destructor)dirstate_item_dealloc, /* tp_dealloc */
728 0, /* tp_print */
728 0, /* tp_print */
729 0, /* tp_getattr */
729 0, /* tp_getattr */
730 0, /* tp_setattr */
730 0, /* tp_setattr */
731 0, /* tp_compare */
731 0, /* tp_compare */
732 0, /* tp_repr */
732 0, /* tp_repr */
733 0, /* tp_as_number */
733 0, /* tp_as_number */
734 0, /* tp_as_sequence */
734 0, /* tp_as_sequence */
735 0, /* tp_as_mapping */
735 0, /* tp_as_mapping */
736 0, /* tp_hash */
736 0, /* tp_hash */
737 0, /* tp_call */
737 0, /* tp_call */
738 0, /* tp_str */
738 0, /* tp_str */
739 0, /* tp_getattro */
739 0, /* tp_getattro */
740 0, /* tp_setattro */
740 0, /* tp_setattro */
741 0, /* tp_as_buffer */
741 0, /* tp_as_buffer */
742 Py_TPFLAGS_DEFAULT, /* tp_flags */
742 Py_TPFLAGS_DEFAULT, /* tp_flags */
743 "dirstate tuple", /* tp_doc */
743 "dirstate tuple", /* tp_doc */
744 0, /* tp_traverse */
744 0, /* tp_traverse */
745 0, /* tp_clear */
745 0, /* tp_clear */
746 0, /* tp_richcompare */
746 0, /* tp_richcompare */
747 0, /* tp_weaklistoffset */
747 0, /* tp_weaklistoffset */
748 0, /* tp_iter */
748 0, /* tp_iter */
749 0, /* tp_iternext */
749 0, /* tp_iternext */
750 dirstate_item_methods, /* tp_methods */
750 dirstate_item_methods, /* tp_methods */
751 0, /* tp_members */
751 0, /* tp_members */
752 dirstate_item_getset, /* tp_getset */
752 dirstate_item_getset, /* tp_getset */
753 0, /* tp_base */
753 0, /* tp_base */
754 0, /* tp_dict */
754 0, /* tp_dict */
755 0, /* tp_descr_get */
755 0, /* tp_descr_get */
756 0, /* tp_descr_set */
756 0, /* tp_descr_set */
757 0, /* tp_dictoffset */
757 0, /* tp_dictoffset */
758 0, /* tp_init */
758 0, /* tp_init */
759 0, /* tp_alloc */
759 0, /* tp_alloc */
760 dirstate_item_new, /* tp_new */
760 dirstate_item_new, /* tp_new */
761 };
761 };
762
762
763 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
763 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
764 {
764 {
765 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
765 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
766 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
766 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
767 char state, *cur, *str, *cpos;
767 char state, *cur, *str, *cpos;
768 int mode, size, mtime;
768 int mode, size, mtime;
769 unsigned int flen, pos = 40;
769 unsigned int flen, pos = 40;
770 Py_ssize_t len = 40;
770 Py_ssize_t len = 40;
771 Py_ssize_t readlen;
771 Py_ssize_t readlen;
772
772
773 if (!PyArg_ParseTuple(args, "O!O!y#:parse_dirstate", &PyDict_Type,
773 if (!PyArg_ParseTuple(args, "O!O!y#:parse_dirstate", &PyDict_Type,
774 &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
774 &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
775 goto quit;
775 goto quit;
776 }
776 }
777
777
778 len = readlen;
778 len = readlen;
779
779
780 /* read parents */
780 /* read parents */
781 if (len < 40) {
781 if (len < 40) {
782 PyErr_SetString(PyExc_ValueError,
782 PyErr_SetString(PyExc_ValueError,
783 "too little data for parents");
783 "too little data for parents");
784 goto quit;
784 goto quit;
785 }
785 }
786
786
787 parents = Py_BuildValue("y#y#", str, (Py_ssize_t)20, str + 20,
787 parents = Py_BuildValue("y#y#", str, (Py_ssize_t)20, str + 20,
788 (Py_ssize_t)20);
788 (Py_ssize_t)20);
789 if (!parents) {
789 if (!parents) {
790 goto quit;
790 goto quit;
791 }
791 }
792
792
793 /* read filenames */
793 /* read filenames */
794 while (pos >= 40 && pos < len) {
794 while (pos >= 40 && pos < len) {
795 if (pos + 17 > len) {
795 if (pos + 17 > len) {
796 PyErr_SetString(PyExc_ValueError,
796 PyErr_SetString(PyExc_ValueError,
797 "overflow in dirstate");
797 "overflow in dirstate");
798 goto quit;
798 goto quit;
799 }
799 }
800 cur = str + pos;
800 cur = str + pos;
801 /* unpack header */
801 /* unpack header */
802 state = *cur;
802 state = *cur;
803 mode = getbe32(cur + 1);
803 mode = getbe32(cur + 1);
804 size = getbe32(cur + 5);
804 size = getbe32(cur + 5);
805 mtime = getbe32(cur + 9);
805 mtime = getbe32(cur + 9);
806 flen = getbe32(cur + 13);
806 flen = getbe32(cur + 13);
807 pos += 17;
807 pos += 17;
808 cur += 17;
808 cur += 17;
809 if (flen > len - pos) {
809 if (flen > len - pos) {
810 PyErr_SetString(PyExc_ValueError,
810 PyErr_SetString(PyExc_ValueError,
811 "overflow in dirstate");
811 "overflow in dirstate");
812 goto quit;
812 goto quit;
813 }
813 }
814
814
815 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
815 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
816 size, mtime);
816 size, mtime);
817 if (!entry)
817 if (!entry)
818 goto quit;
818 goto quit;
819 cpos = memchr(cur, 0, flen);
819 cpos = memchr(cur, 0, flen);
820 if (cpos) {
820 if (cpos) {
821 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
821 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
822 cname = PyBytes_FromStringAndSize(
822 cname = PyBytes_FromStringAndSize(
823 cpos + 1, flen - (cpos - cur) - 1);
823 cpos + 1, flen - (cpos - cur) - 1);
824 if (!fname || !cname ||
824 if (!fname || !cname ||
825 PyDict_SetItem(cmap, fname, cname) == -1 ||
825 PyDict_SetItem(cmap, fname, cname) == -1 ||
826 PyDict_SetItem(dmap, fname, entry) == -1) {
826 PyDict_SetItem(dmap, fname, entry) == -1) {
827 goto quit;
827 goto quit;
828 }
828 }
829 Py_DECREF(cname);
829 Py_DECREF(cname);
830 } else {
830 } else {
831 fname = PyBytes_FromStringAndSize(cur, flen);
831 fname = PyBytes_FromStringAndSize(cur, flen);
832 if (!fname ||
832 if (!fname ||
833 PyDict_SetItem(dmap, fname, entry) == -1) {
833 PyDict_SetItem(dmap, fname, entry) == -1) {
834 goto quit;
834 goto quit;
835 }
835 }
836 }
836 }
837 Py_DECREF(fname);
837 Py_DECREF(fname);
838 Py_DECREF(entry);
838 Py_DECREF(entry);
839 fname = cname = entry = NULL;
839 fname = cname = entry = NULL;
840 pos += flen;
840 pos += flen;
841 }
841 }
842
842
843 ret = parents;
843 ret = parents;
844 Py_INCREF(ret);
844 Py_INCREF(ret);
845 quit:
845 quit:
846 Py_XDECREF(fname);
846 Py_XDECREF(fname);
847 Py_XDECREF(cname);
847 Py_XDECREF(cname);
848 Py_XDECREF(entry);
848 Py_XDECREF(entry);
849 Py_XDECREF(parents);
849 Py_XDECREF(parents);
850 return ret;
850 return ret;
851 }
851 }
852
852
853 /*
853 /*
854 * Efficiently pack a dirstate object into its on-disk format.
854 * Efficiently pack a dirstate object into its on-disk format.
855 */
855 */
856 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
856 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
857 {
857 {
858 PyObject *packobj = NULL;
858 PyObject *packobj = NULL;
859 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
859 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
860 Py_ssize_t nbytes, pos, l;
860 Py_ssize_t nbytes, pos, l;
861 PyObject *k, *v = NULL, *pn;
861 PyObject *k, *v = NULL, *pn;
862 char *p, *s;
862 char *p, *s;
863
863
864 if (!PyArg_ParseTuple(args, "O!O!O!:pack_dirstate", &PyDict_Type, &map,
864 if (!PyArg_ParseTuple(args, "O!O!O!:pack_dirstate", &PyDict_Type, &map,
865 &PyDict_Type, &copymap, &PyTuple_Type, &pl)) {
865 &PyDict_Type, &copymap, &PyTuple_Type, &pl)) {
866 return NULL;
866 return NULL;
867 }
867 }
868
868
869 if (PyTuple_Size(pl) != 2) {
869 if (PyTuple_Size(pl) != 2) {
870 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
870 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
871 return NULL;
871 return NULL;
872 }
872 }
873
873
874 /* Figure out how much we need to allocate. */
874 /* Figure out how much we need to allocate. */
875 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
875 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
876 PyObject *c;
876 PyObject *c;
877 if (!PyBytes_Check(k)) {
877 if (!PyBytes_Check(k)) {
878 PyErr_SetString(PyExc_TypeError, "expected string key");
878 PyErr_SetString(PyExc_TypeError, "expected string key");
879 goto bail;
879 goto bail;
880 }
880 }
881 nbytes += PyBytes_GET_SIZE(k) + 17;
881 nbytes += PyBytes_GET_SIZE(k) + 17;
882 c = PyDict_GetItem(copymap, k);
882 c = PyDict_GetItem(copymap, k);
883 if (c) {
883 if (c) {
884 if (!PyBytes_Check(c)) {
884 if (!PyBytes_Check(c)) {
885 PyErr_SetString(PyExc_TypeError,
885 PyErr_SetString(PyExc_TypeError,
886 "expected string key");
886 "expected string key");
887 goto bail;
887 goto bail;
888 }
888 }
889 nbytes += PyBytes_GET_SIZE(c) + 1;
889 nbytes += PyBytes_GET_SIZE(c) + 1;
890 }
890 }
891 }
891 }
892
892
893 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
893 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
894 if (packobj == NULL) {
894 if (packobj == NULL) {
895 goto bail;
895 goto bail;
896 }
896 }
897
897
898 p = PyBytes_AS_STRING(packobj);
898 p = PyBytes_AS_STRING(packobj);
899
899
900 pn = PyTuple_GET_ITEM(pl, 0);
900 pn = PyTuple_GET_ITEM(pl, 0);
901 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
901 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
902 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
902 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
903 goto bail;
903 goto bail;
904 }
904 }
905 memcpy(p, s, l);
905 memcpy(p, s, l);
906 p += 20;
906 p += 20;
907 pn = PyTuple_GET_ITEM(pl, 1);
907 pn = PyTuple_GET_ITEM(pl, 1);
908 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
908 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
909 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
909 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
910 goto bail;
910 goto bail;
911 }
911 }
912 memcpy(p, s, l);
912 memcpy(p, s, l);
913 p += 20;
913 p += 20;
914
914
915 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
915 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
916 dirstateItemObject *tuple;
916 dirstateItemObject *tuple;
917 char state;
917 char state;
918 int mode, size, mtime;
918 int mode, size, mtime;
919 Py_ssize_t len, l;
919 Py_ssize_t len, l;
920 PyObject *o;
920 PyObject *o;
921 char *t;
921 char *t;
922
922
923 if (!dirstate_tuple_check(v)) {
923 if (!dirstate_tuple_check(v)) {
924 PyErr_SetString(PyExc_TypeError,
924 PyErr_SetString(PyExc_TypeError,
925 "expected a dirstate tuple");
925 "expected a dirstate tuple");
926 goto bail;
926 goto bail;
927 }
927 }
928 tuple = (dirstateItemObject *)v;
928 tuple = (dirstateItemObject *)v;
929
929
930 state = dirstate_item_c_v1_state(tuple);
930 state = dirstate_item_c_v1_state(tuple);
931 mode = dirstate_item_c_v1_mode(tuple);
931 mode = dirstate_item_c_v1_mode(tuple);
932 size = dirstate_item_c_v1_size(tuple);
932 size = dirstate_item_c_v1_size(tuple);
933 mtime = dirstate_item_c_v1_mtime(tuple);
933 mtime = dirstate_item_c_v1_mtime(tuple);
934 *p++ = state;
934 *p++ = state;
935 putbe32((uint32_t)mode, p);
935 putbe32((uint32_t)mode, p);
936 putbe32((uint32_t)size, p + 4);
936 putbe32((uint32_t)size, p + 4);
937 putbe32((uint32_t)mtime, p + 8);
937 putbe32((uint32_t)mtime, p + 8);
938 t = p + 12;
938 t = p + 12;
939 p += 16;
939 p += 16;
940 len = PyBytes_GET_SIZE(k);
940 len = PyBytes_GET_SIZE(k);
941 memcpy(p, PyBytes_AS_STRING(k), len);
941 memcpy(p, PyBytes_AS_STRING(k), len);
942 p += len;
942 p += len;
943 o = PyDict_GetItem(copymap, k);
943 o = PyDict_GetItem(copymap, k);
944 if (o) {
944 if (o) {
945 *p++ = '\0';
945 *p++ = '\0';
946 l = PyBytes_GET_SIZE(o);
946 l = PyBytes_GET_SIZE(o);
947 memcpy(p, PyBytes_AS_STRING(o), l);
947 memcpy(p, PyBytes_AS_STRING(o), l);
948 p += l;
948 p += l;
949 len += l + 1;
949 len += l + 1;
950 }
950 }
951 putbe32((uint32_t)len, t);
951 putbe32((uint32_t)len, t);
952 }
952 }
953
953
954 pos = p - PyBytes_AS_STRING(packobj);
954 pos = p - PyBytes_AS_STRING(packobj);
955 if (pos != nbytes) {
955 if (pos != nbytes) {
956 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
956 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
957 (long)pos, (long)nbytes);
957 (long)pos, (long)nbytes);
958 goto bail;
958 goto bail;
959 }
959 }
960
960
961 return packobj;
961 return packobj;
962 bail:
962 bail:
963 Py_XDECREF(mtime_unset);
963 Py_XDECREF(mtime_unset);
964 Py_XDECREF(packobj);
964 Py_XDECREF(packobj);
965 Py_XDECREF(v);
965 Py_XDECREF(v);
966 return NULL;
966 return NULL;
967 }
967 }
968
968
969 #define BUMPED_FIX 1
969 #define BUMPED_FIX 1
970 #define USING_SHA_256 2
970 #define USING_SHA_256 2
971 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
971 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
972
972
973 static PyObject *readshas(const char *source, unsigned char num,
973 static PyObject *readshas(const char *source, unsigned char num,
974 Py_ssize_t hashwidth)
974 Py_ssize_t hashwidth)
975 {
975 {
976 int i;
976 int i;
977 PyObject *list = PyTuple_New(num);
977 PyObject *list = PyTuple_New(num);
978 if (list == NULL) {
978 if (list == NULL) {
979 return NULL;
979 return NULL;
980 }
980 }
981 for (i = 0; i < num; i++) {
981 for (i = 0; i < num; i++) {
982 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
982 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
983 if (hash == NULL) {
983 if (hash == NULL) {
984 Py_DECREF(list);
984 Py_DECREF(list);
985 return NULL;
985 return NULL;
986 }
986 }
987 PyTuple_SET_ITEM(list, i, hash);
987 PyTuple_SET_ITEM(list, i, hash);
988 source += hashwidth;
988 source += hashwidth;
989 }
989 }
990 return list;
990 return list;
991 }
991 }
992
992
993 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
993 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
994 uint32_t *msize)
994 uint32_t *msize)
995 {
995 {
996 const char *data = databegin;
996 const char *data = databegin;
997 const char *meta;
997 const char *meta;
998
998
999 double mtime;
999 double mtime;
1000 int16_t tz;
1000 int16_t tz;
1001 uint16_t flags;
1001 uint16_t flags;
1002 unsigned char nsuccs, nparents, nmetadata;
1002 unsigned char nsuccs, nparents, nmetadata;
1003 Py_ssize_t hashwidth = 20;
1003 Py_ssize_t hashwidth = 20;
1004
1004
1005 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
1005 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
1006 PyObject *metadata = NULL, *ret = NULL;
1006 PyObject *metadata = NULL, *ret = NULL;
1007 int i;
1007 int i;
1008
1008
1009 if (data + FM1_HEADER_SIZE > dataend) {
1009 if (data + FM1_HEADER_SIZE > dataend) {
1010 goto overflow;
1010 goto overflow;
1011 }
1011 }
1012
1012
1013 *msize = getbe32(data);
1013 *msize = getbe32(data);
1014 data += 4;
1014 data += 4;
1015 mtime = getbefloat64(data);
1015 mtime = getbefloat64(data);
1016 data += 8;
1016 data += 8;
1017 tz = getbeint16(data);
1017 tz = getbeint16(data);
1018 data += 2;
1018 data += 2;
1019 flags = getbeuint16(data);
1019 flags = getbeuint16(data);
1020 data += 2;
1020 data += 2;
1021
1021
1022 if (flags & USING_SHA_256) {
1022 if (flags & USING_SHA_256) {
1023 hashwidth = 32;
1023 hashwidth = 32;
1024 }
1024 }
1025
1025
1026 nsuccs = (unsigned char)(*data++);
1026 nsuccs = (unsigned char)(*data++);
1027 nparents = (unsigned char)(*data++);
1027 nparents = (unsigned char)(*data++);
1028 nmetadata = (unsigned char)(*data++);
1028 nmetadata = (unsigned char)(*data++);
1029
1029
1030 if (databegin + *msize > dataend) {
1030 if (databegin + *msize > dataend) {
1031 goto overflow;
1031 goto overflow;
1032 }
1032 }
1033 dataend = databegin + *msize; /* narrow down to marker size */
1033 dataend = databegin + *msize; /* narrow down to marker size */
1034
1034
1035 if (data + hashwidth > dataend) {
1035 if (data + hashwidth > dataend) {
1036 goto overflow;
1036 goto overflow;
1037 }
1037 }
1038 prec = PyBytes_FromStringAndSize(data, hashwidth);
1038 prec = PyBytes_FromStringAndSize(data, hashwidth);
1039 data += hashwidth;
1039 data += hashwidth;
1040 if (prec == NULL) {
1040 if (prec == NULL) {
1041 goto bail;
1041 goto bail;
1042 }
1042 }
1043
1043
1044 if (data + nsuccs * hashwidth > dataend) {
1044 if (data + nsuccs * hashwidth > dataend) {
1045 goto overflow;
1045 goto overflow;
1046 }
1046 }
1047 succs = readshas(data, nsuccs, hashwidth);
1047 succs = readshas(data, nsuccs, hashwidth);
1048 if (succs == NULL) {
1048 if (succs == NULL) {
1049 goto bail;
1049 goto bail;
1050 }
1050 }
1051 data += nsuccs * hashwidth;
1051 data += nsuccs * hashwidth;
1052
1052
1053 if (nparents == 1 || nparents == 2) {
1053 if (nparents == 1 || nparents == 2) {
1054 if (data + nparents * hashwidth > dataend) {
1054 if (data + nparents * hashwidth > dataend) {
1055 goto overflow;
1055 goto overflow;
1056 }
1056 }
1057 parents = readshas(data, nparents, hashwidth);
1057 parents = readshas(data, nparents, hashwidth);
1058 if (parents == NULL) {
1058 if (parents == NULL) {
1059 goto bail;
1059 goto bail;
1060 }
1060 }
1061 data += nparents * hashwidth;
1061 data += nparents * hashwidth;
1062 } else {
1062 } else {
1063 parents = Py_None;
1063 parents = Py_None;
1064 Py_INCREF(parents);
1064 Py_INCREF(parents);
1065 }
1065 }
1066
1066
1067 if (data + 2 * nmetadata > dataend) {
1067 if (data + 2 * nmetadata > dataend) {
1068 goto overflow;
1068 goto overflow;
1069 }
1069 }
1070 meta = data + (2 * nmetadata);
1070 meta = data + (2 * nmetadata);
1071 metadata = PyTuple_New(nmetadata);
1071 metadata = PyTuple_New(nmetadata);
1072 if (metadata == NULL) {
1072 if (metadata == NULL) {
1073 goto bail;
1073 goto bail;
1074 }
1074 }
1075 for (i = 0; i < nmetadata; i++) {
1075 for (i = 0; i < nmetadata; i++) {
1076 PyObject *tmp, *left = NULL, *right = NULL;
1076 PyObject *tmp, *left = NULL, *right = NULL;
1077 Py_ssize_t leftsize = (unsigned char)(*data++);
1077 Py_ssize_t leftsize = (unsigned char)(*data++);
1078 Py_ssize_t rightsize = (unsigned char)(*data++);
1078 Py_ssize_t rightsize = (unsigned char)(*data++);
1079 if (meta + leftsize + rightsize > dataend) {
1079 if (meta + leftsize + rightsize > dataend) {
1080 goto overflow;
1080 goto overflow;
1081 }
1081 }
1082 left = PyBytes_FromStringAndSize(meta, leftsize);
1082 left = PyBytes_FromStringAndSize(meta, leftsize);
1083 meta += leftsize;
1083 meta += leftsize;
1084 right = PyBytes_FromStringAndSize(meta, rightsize);
1084 right = PyBytes_FromStringAndSize(meta, rightsize);
1085 meta += rightsize;
1085 meta += rightsize;
1086 tmp = PyTuple_New(2);
1086 tmp = PyTuple_New(2);
1087 if (!left || !right || !tmp) {
1087 if (!left || !right || !tmp) {
1088 Py_XDECREF(left);
1088 Py_XDECREF(left);
1089 Py_XDECREF(right);
1089 Py_XDECREF(right);
1090 Py_XDECREF(tmp);
1090 Py_XDECREF(tmp);
1091 goto bail;
1091 goto bail;
1092 }
1092 }
1093 PyTuple_SET_ITEM(tmp, 0, left);
1093 PyTuple_SET_ITEM(tmp, 0, left);
1094 PyTuple_SET_ITEM(tmp, 1, right);
1094 PyTuple_SET_ITEM(tmp, 1, right);
1095 PyTuple_SET_ITEM(metadata, i, tmp);
1095 PyTuple_SET_ITEM(metadata, i, tmp);
1096 }
1096 }
1097 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
1097 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
1098 (int)tz * 60, parents);
1098 (int)tz * 60, parents);
1099 goto bail; /* return successfully */
1099 goto bail; /* return successfully */
1100
1100
1101 overflow:
1101 overflow:
1102 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
1102 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
1103 bail:
1103 bail:
1104 Py_XDECREF(prec);
1104 Py_XDECREF(prec);
1105 Py_XDECREF(succs);
1105 Py_XDECREF(succs);
1106 Py_XDECREF(metadata);
1106 Py_XDECREF(metadata);
1107 Py_XDECREF(parents);
1107 Py_XDECREF(parents);
1108 return ret;
1108 return ret;
1109 }
1109 }
1110
1110
1111 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
1111 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
1112 {
1112 {
1113 const char *data, *dataend;
1113 const char *data, *dataend;
1114 Py_ssize_t datalen, offset, stop;
1114 Py_ssize_t datalen, offset, stop;
1115 PyObject *markers = NULL;
1115 PyObject *markers = NULL;
1116
1116
1117 if (!PyArg_ParseTuple(args, "y#nn", &data, &datalen, &offset, &stop)) {
1117 if (!PyArg_ParseTuple(args, "y#nn", &data, &datalen, &offset, &stop)) {
1118 return NULL;
1118 return NULL;
1119 }
1119 }
1120 if (offset < 0) {
1120 if (offset < 0) {
1121 PyErr_SetString(PyExc_ValueError,
1121 PyErr_SetString(PyExc_ValueError,
1122 "invalid negative offset in fm1readmarkers");
1122 "invalid negative offset in fm1readmarkers");
1123 return NULL;
1123 return NULL;
1124 }
1124 }
1125 if (stop > datalen) {
1125 if (stop > datalen) {
1126 PyErr_SetString(
1126 PyErr_SetString(
1127 PyExc_ValueError,
1127 PyExc_ValueError,
1128 "stop longer than data length in fm1readmarkers");
1128 "stop longer than data length in fm1readmarkers");
1129 return NULL;
1129 return NULL;
1130 }
1130 }
1131 dataend = data + datalen;
1131 dataend = data + datalen;
1132 data += offset;
1132 data += offset;
1133 markers = PyList_New(0);
1133 markers = PyList_New(0);
1134 if (!markers) {
1134 if (!markers) {
1135 return NULL;
1135 return NULL;
1136 }
1136 }
1137 while (offset < stop) {
1137 while (offset < stop) {
1138 uint32_t msize;
1138 uint32_t msize;
1139 int error;
1139 int error;
1140 PyObject *record = fm1readmarker(data, dataend, &msize);
1140 PyObject *record = fm1readmarker(data, dataend, &msize);
1141 if (!record) {
1141 if (!record) {
1142 goto bail;
1142 goto bail;
1143 }
1143 }
1144 error = PyList_Append(markers, record);
1144 error = PyList_Append(markers, record);
1145 Py_DECREF(record);
1145 Py_DECREF(record);
1146 if (error) {
1146 if (error) {
1147 goto bail;
1147 goto bail;
1148 }
1148 }
1149 data += msize;
1149 data += msize;
1150 offset += msize;
1150 offset += msize;
1151 }
1151 }
1152 return markers;
1152 return markers;
1153 bail:
1153 bail:
1154 Py_DECREF(markers);
1154 Py_DECREF(markers);
1155 return NULL;
1155 return NULL;
1156 }
1156 }
1157
1157
1158 static char parsers_doc[] = "Efficient content parsing.";
1158 static char parsers_doc[] = "Efficient content parsing.";
1159
1159
1160 PyObject *encodedir(PyObject *self, PyObject *args);
1160 PyObject *encodedir(PyObject *self, PyObject *args);
1161 PyObject *pathencode(PyObject *self, PyObject *args);
1161 PyObject *pathencode(PyObject *self, PyObject *args);
1162 PyObject *lowerencode(PyObject *self, PyObject *args);
1162 PyObject *lowerencode(PyObject *self, PyObject *args);
1163 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
1163 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
1164
1164
1165 static PyMethodDef methods[] = {
1165 static PyMethodDef methods[] = {
1166 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1166 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1167 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1167 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1168 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
1168 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
1169 "parse a revlog index\n"},
1169 "parse a revlog index\n"},
1170 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
1170 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
1171 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
1171 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
1172 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
1172 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
1173 {"dict_new_presized", dict_new_presized, METH_VARARGS,
1173 {"dict_new_presized", dict_new_presized, METH_VARARGS,
1174 "construct a dict with an expected size\n"},
1174 "construct a dict with an expected size\n"},
1175 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
1175 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
1176 "make file foldmap\n"},
1176 "make file foldmap\n"},
1177 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
1177 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
1178 "escape a UTF-8 byte string to JSON (fast path)\n"},
1178 "escape a UTF-8 byte string to JSON (fast path)\n"},
1179 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1179 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1180 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1180 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1181 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1181 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1182 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
1182 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
1183 "parse v1 obsolete markers\n"},
1183 "parse v1 obsolete markers\n"},
1184 {NULL, NULL}};
1184 {NULL, NULL}};
1185
1185
1186 void dirs_module_init(PyObject *mod);
1186 void dirs_module_init(PyObject *mod);
1187 void manifest_module_init(PyObject *mod);
1187 void manifest_module_init(PyObject *mod);
1188 void revlog_module_init(PyObject *mod);
1188 void revlog_module_init(PyObject *mod);
1189
1189
1190 static const int version = 20;
1190 static const int version = 21;
1191
1191
1192 static void module_init(PyObject *mod)
1192 static void module_init(PyObject *mod)
1193 {
1193 {
1194 PyModule_AddIntConstant(mod, "version", version);
1194 PyModule_AddIntConstant(mod, "version", version);
1195
1195
1196 /* This module constant has two purposes. First, it lets us unit test
1196 /* This module constant has two purposes. First, it lets us unit test
1197 * the ImportError raised without hard-coding any error text. This
1197 * the ImportError raised without hard-coding any error text. This
1198 * means we can change the text in the future without breaking tests,
1198 * means we can change the text in the future without breaking tests,
1199 * even across changesets without a recompile. Second, its presence
1199 * even across changesets without a recompile. Second, its presence
1200 * can be used to determine whether the version-checking logic is
1200 * can be used to determine whether the version-checking logic is
1201 * present, which also helps in testing across changesets without a
1201 * present, which also helps in testing across changesets without a
1202 * recompile. Note that this means the pure-Python version of parsers
1202 * recompile. Note that this means the pure-Python version of parsers
1203 * should not have this module constant. */
1203 * should not have this module constant. */
1204 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
1204 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
1205
1205
1206 dirs_module_init(mod);
1206 dirs_module_init(mod);
1207 manifest_module_init(mod);
1207 manifest_module_init(mod);
1208 revlog_module_init(mod);
1208 revlog_module_init(mod);
1209
1209
1210 if (PyType_Ready(&dirstateItemType) < 0) {
1210 if (PyType_Ready(&dirstateItemType) < 0) {
1211 return;
1211 return;
1212 }
1212 }
1213 Py_INCREF(&dirstateItemType);
1213 Py_INCREF(&dirstateItemType);
1214 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
1214 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
1215 }
1215 }
1216
1216
1217 static int check_python_version(void)
1217 static int check_python_version(void)
1218 {
1218 {
1219 PyObject *sys = PyImport_ImportModule("sys"), *ver;
1219 PyObject *sys = PyImport_ImportModule("sys"), *ver;
1220 long hexversion;
1220 long hexversion;
1221 if (!sys) {
1221 if (!sys) {
1222 return -1;
1222 return -1;
1223 }
1223 }
1224 ver = PyObject_GetAttrString(sys, "hexversion");
1224 ver = PyObject_GetAttrString(sys, "hexversion");
1225 Py_DECREF(sys);
1225 Py_DECREF(sys);
1226 if (!ver) {
1226 if (!ver) {
1227 return -1;
1227 return -1;
1228 }
1228 }
1229 hexversion = PyLong_AsLong(ver);
1229 hexversion = PyLong_AsLong(ver);
1230 Py_DECREF(ver);
1230 Py_DECREF(ver);
1231 /* sys.hexversion is a 32-bit number by default, so the -1 case
1231 /* sys.hexversion is a 32-bit number by default, so the -1 case
1232 * should only occur in unusual circumstances (e.g. if sys.hexversion
1232 * should only occur in unusual circumstances (e.g. if sys.hexversion
1233 * is manually set to an invalid value). */
1233 * is manually set to an invalid value). */
1234 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
1234 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
1235 PyErr_Format(PyExc_ImportError,
1235 PyErr_Format(PyExc_ImportError,
1236 "%s: The Mercurial extension "
1236 "%s: The Mercurial extension "
1237 "modules were compiled with Python " PY_VERSION
1237 "modules were compiled with Python " PY_VERSION
1238 ", but "
1238 ", but "
1239 "Mercurial is currently using Python with "
1239 "Mercurial is currently using Python with "
1240 "sys.hexversion=%ld: "
1240 "sys.hexversion=%ld: "
1241 "Python %s\n at: %s",
1241 "Python %s\n at: %s",
1242 versionerrortext, hexversion, Py_GetVersion(),
1242 versionerrortext, hexversion, Py_GetVersion(),
1243 Py_GetProgramFullPath());
1243 Py_GetProgramFullPath());
1244 return -1;
1244 return -1;
1245 }
1245 }
1246 return 0;
1246 return 0;
1247 }
1247 }
1248
1248
1249 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
1249 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
1250 parsers_doc, -1, methods};
1250 parsers_doc, -1, methods};
1251
1251
1252 PyMODINIT_FUNC PyInit_parsers(void)
1252 PyMODINIT_FUNC PyInit_parsers(void)
1253 {
1253 {
1254 PyObject *mod;
1254 PyObject *mod;
1255
1255
1256 if (check_python_version() == -1)
1256 if (check_python_version() == -1)
1257 return NULL;
1257 return NULL;
1258 mod = PyModule_Create(&parsers_module);
1258 mod = PyModule_Create(&parsers_module);
1259 module_init(mod);
1259 module_init(mod);
1260 return mod;
1260 return mod;
1261 }
1261 }
@@ -1,153 +1,153 b''
1 # policy.py - module policy logic for Mercurial.
1 # policy.py - module policy logic for Mercurial.
2 #
2 #
3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8
8
9 import os
9 import os
10 import sys
10 import sys
11
11
12 from .pycompat import getattr
12 from .pycompat import getattr
13
13
14 # Rules for how modules can be loaded. Values are:
14 # Rules for how modules can be loaded. Values are:
15 #
15 #
16 # c - require C extensions
16 # c - require C extensions
17 # rust+c - require Rust and C extensions
17 # rust+c - require Rust and C extensions
18 # rust+c-allow - allow Rust and C extensions with fallback to pure Python
18 # rust+c-allow - allow Rust and C extensions with fallback to pure Python
19 # for each
19 # for each
20 # allow - allow pure Python implementation when C loading fails
20 # allow - allow pure Python implementation when C loading fails
21 # cffi - required cffi versions (implemented within pure module)
21 # cffi - required cffi versions (implemented within pure module)
22 # cffi-allow - allow pure Python implementation if cffi version is missing
22 # cffi-allow - allow pure Python implementation if cffi version is missing
23 # py - only load pure Python modules
23 # py - only load pure Python modules
24 #
24 #
25 # By default, fall back to the pure modules so the in-place build can
25 # By default, fall back to the pure modules so the in-place build can
26 # run without recompiling the C extensions. This will be overridden by
26 # run without recompiling the C extensions. This will be overridden by
27 # __modulepolicy__ generated by setup.py.
27 # __modulepolicy__ generated by setup.py.
28 policy = b'allow'
28 policy = b'allow'
29 _packageprefs = {
29 _packageprefs = {
30 # policy: (versioned package, pure package)
30 # policy: (versioned package, pure package)
31 b'c': ('cext', None),
31 b'c': ('cext', None),
32 b'allow': ('cext', 'pure'),
32 b'allow': ('cext', 'pure'),
33 b'cffi': ('cffi', None),
33 b'cffi': ('cffi', None),
34 b'cffi-allow': ('cffi', 'pure'),
34 b'cffi-allow': ('cffi', 'pure'),
35 b'py': (None, 'pure'),
35 b'py': (None, 'pure'),
36 # For now, rust policies impact importrust only
36 # For now, rust policies impact importrust only
37 b'rust+c': ('cext', None),
37 b'rust+c': ('cext', None),
38 b'rust+c-allow': ('cext', 'pure'),
38 b'rust+c-allow': ('cext', 'pure'),
39 }
39 }
40
40
41 try:
41 try:
42 from . import __modulepolicy__
42 from . import __modulepolicy__
43
43
44 policy = __modulepolicy__.modulepolicy
44 policy = __modulepolicy__.modulepolicy
45 except ImportError:
45 except ImportError:
46 pass
46 pass
47
47
48 # PyPy doesn't load C extensions.
48 # PyPy doesn't load C extensions.
49 #
49 #
50 # The canonical way to do this is to test platform.python_implementation().
50 # The canonical way to do this is to test platform.python_implementation().
51 # But we don't import platform and don't bloat for it here.
51 # But we don't import platform and don't bloat for it here.
52 if '__pypy__' in sys.builtin_module_names:
52 if '__pypy__' in sys.builtin_module_names:
53 policy = b'cffi'
53 policy = b'cffi'
54
54
55 # Environment variable can always force settings.
55 # Environment variable can always force settings.
56 if 'HGMODULEPOLICY' in os.environ:
56 if 'HGMODULEPOLICY' in os.environ:
57 policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
57 policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
58
58
59
59
60 def _importfrom(pkgname, modname):
60 def _importfrom(pkgname, modname):
61 # from .<pkgname> import <modname> (where . is looked through this module)
61 # from .<pkgname> import <modname> (where . is looked through this module)
62 fakelocals = {}
62 fakelocals = {}
63 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1)
63 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1)
64 try:
64 try:
65 fakelocals[modname] = mod = getattr(pkg, modname)
65 fakelocals[modname] = mod = getattr(pkg, modname)
66 except AttributeError:
66 except AttributeError:
67 raise ImportError('cannot import name %s' % modname)
67 raise ImportError('cannot import name %s' % modname)
68 # force import; fakelocals[modname] may be replaced with the real module
68 # force import; fakelocals[modname] may be replaced with the real module
69 getattr(mod, '__doc__', None)
69 getattr(mod, '__doc__', None)
70 return fakelocals[modname]
70 return fakelocals[modname]
71
71
72
72
73 # keep in sync with "version" in C modules
73 # keep in sync with "version" in C modules
74 _cextversions = {
74 _cextversions = {
75 ('cext', 'base85'): 1,
75 ('cext', 'base85'): 1,
76 ('cext', 'bdiff'): 3,
76 ('cext', 'bdiff'): 3,
77 ('cext', 'mpatch'): 1,
77 ('cext', 'mpatch'): 1,
78 ('cext', 'osutil'): 4,
78 ('cext', 'osutil'): 4,
79 ('cext', 'parsers'): 20,
79 ('cext', 'parsers'): 21,
80 }
80 }
81
81
82 # map import request to other package or module
82 # map import request to other package or module
83 _modredirects = {
83 _modredirects = {
84 ('cext', 'charencode'): ('cext', 'parsers'),
84 ('cext', 'charencode'): ('cext', 'parsers'),
85 ('cffi', 'base85'): ('pure', 'base85'),
85 ('cffi', 'base85'): ('pure', 'base85'),
86 ('cffi', 'charencode'): ('pure', 'charencode'),
86 ('cffi', 'charencode'): ('pure', 'charencode'),
87 ('cffi', 'parsers'): ('pure', 'parsers'),
87 ('cffi', 'parsers'): ('pure', 'parsers'),
88 }
88 }
89
89
90
90
91 def _checkmod(pkgname, modname, mod):
91 def _checkmod(pkgname, modname, mod):
92 expected = _cextversions.get((pkgname, modname))
92 expected = _cextversions.get((pkgname, modname))
93 actual = getattr(mod, 'version', None)
93 actual = getattr(mod, 'version', None)
94 if actual != expected:
94 if actual != expected:
95 raise ImportError(
95 raise ImportError(
96 'cannot import module %s.%s '
96 'cannot import module %s.%s '
97 '(expected version: %d, actual: %r)'
97 '(expected version: %d, actual: %r)'
98 % (pkgname, modname, expected, actual)
98 % (pkgname, modname, expected, actual)
99 )
99 )
100
100
101
101
102 def importmod(modname):
102 def importmod(modname):
103 """Import module according to policy and check API version"""
103 """Import module according to policy and check API version"""
104 try:
104 try:
105 verpkg, purepkg = _packageprefs[policy]
105 verpkg, purepkg = _packageprefs[policy]
106 except KeyError:
106 except KeyError:
107 raise ImportError('invalid HGMODULEPOLICY %r' % policy)
107 raise ImportError('invalid HGMODULEPOLICY %r' % policy)
108 assert verpkg or purepkg
108 assert verpkg or purepkg
109 if verpkg:
109 if verpkg:
110 pn, mn = _modredirects.get((verpkg, modname), (verpkg, modname))
110 pn, mn = _modredirects.get((verpkg, modname), (verpkg, modname))
111 try:
111 try:
112 mod = _importfrom(pn, mn)
112 mod = _importfrom(pn, mn)
113 if pn == verpkg:
113 if pn == verpkg:
114 _checkmod(pn, mn, mod)
114 _checkmod(pn, mn, mod)
115 return mod
115 return mod
116 except ImportError:
116 except ImportError:
117 if not purepkg:
117 if not purepkg:
118 raise
118 raise
119 pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname))
119 pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname))
120 return _importfrom(pn, mn)
120 return _importfrom(pn, mn)
121
121
122
122
123 def _isrustpermissive():
123 def _isrustpermissive():
124 """Assuming the policy is a Rust one, tell if it's permissive."""
124 """Assuming the policy is a Rust one, tell if it's permissive."""
125 return policy.endswith(b'-allow')
125 return policy.endswith(b'-allow')
126
126
127
127
128 def importrust(modname, member=None, default=None):
128 def importrust(modname, member=None, default=None):
129 """Import Rust module according to policy and availability.
129 """Import Rust module according to policy and availability.
130
130
131 If policy isn't a Rust one, this returns `default`.
131 If policy isn't a Rust one, this returns `default`.
132
132
133 If either the module or its member is not available, this returns `default`
133 If either the module or its member is not available, this returns `default`
134 if policy is permissive and raises `ImportError` if not.
134 if policy is permissive and raises `ImportError` if not.
135 """
135 """
136 if not policy.startswith(b'rust'):
136 if not policy.startswith(b'rust'):
137 return default
137 return default
138
138
139 try:
139 try:
140 mod = _importfrom('rustext', modname)
140 mod = _importfrom('rustext', modname)
141 except ImportError:
141 except ImportError:
142 if _isrustpermissive():
142 if _isrustpermissive():
143 return default
143 return default
144 raise
144 raise
145 if member is None:
145 if member is None:
146 return mod
146 return mod
147
147
148 try:
148 try:
149 return getattr(mod, member)
149 return getattr(mod, member)
150 except AttributeError:
150 except AttributeError:
151 if _isrustpermissive():
151 if _isrustpermissive():
152 return default
152 return default
153 raise ImportError("Cannot import name %s" % member)
153 raise ImportError("Cannot import name %s" % member)
@@ -1,974 +1,979 b''
1 # parsers.py - Python implementation of parsers.c
1 # parsers.py - Python implementation of parsers.c
2 #
2 #
3 # Copyright 2009 Olivia Mackall <olivia@selenic.com> and others
3 # Copyright 2009 Olivia Mackall <olivia@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8
8
9 import io
9 import io
10 import stat
10 import stat
11 import struct
11 import struct
12 import zlib
12 import zlib
13
13
14 from ..node import (
14 from ..node import (
15 nullrev,
15 nullrev,
16 sha1nodeconstants,
16 sha1nodeconstants,
17 )
17 )
18 from ..thirdparty import attr
18 from ..thirdparty import attr
19 from .. import (
19 from .. import (
20 error,
20 error,
21 revlogutils,
21 revlogutils,
22 util,
22 util,
23 )
23 )
24
24
25 from ..revlogutils import nodemap as nodemaputil
25 from ..revlogutils import nodemap as nodemaputil
26 from ..revlogutils import constants as revlog_constants
26 from ..revlogutils import constants as revlog_constants
27
27
28 stringio = io.BytesIO
28 stringio = io.BytesIO
29
29
30
30
31 _pack = struct.pack
31 _pack = struct.pack
32 _unpack = struct.unpack
32 _unpack = struct.unpack
33 _compress = zlib.compress
33 _compress = zlib.compress
34 _decompress = zlib.decompress
34 _decompress = zlib.decompress
35
35
36
36
37 # a special value used internally for `size` if the file come from the other parent
37 # a special value used internally for `size` if the file come from the other parent
38 FROM_P2 = -2
38 FROM_P2 = -2
39
39
40 # a special value used internally for `size` if the file is modified/merged/added
40 # a special value used internally for `size` if the file is modified/merged/added
41 NONNORMAL = -1
41 NONNORMAL = -1
42
42
43 # a special value used internally for `time` if the time is ambigeous
43 # a special value used internally for `time` if the time is ambigeous
44 AMBIGUOUS_TIME = -1
44 AMBIGUOUS_TIME = -1
45
45
46 # Bits of the `flags` byte inside a node in the file format
46 # Bits of the `flags` byte inside a node in the file format
47 DIRSTATE_V2_WDIR_TRACKED = 1 << 0
47 DIRSTATE_V2_WDIR_TRACKED = 1 << 0
48 DIRSTATE_V2_P1_TRACKED = 1 << 1
48 DIRSTATE_V2_P1_TRACKED = 1 << 1
49 DIRSTATE_V2_P2_INFO = 1 << 2
49 DIRSTATE_V2_P2_INFO = 1 << 2
50 DIRSTATE_V2_MODE_EXEC_PERM = 1 << 3
50 DIRSTATE_V2_MODE_EXEC_PERM = 1 << 3
51 DIRSTATE_V2_MODE_IS_SYMLINK = 1 << 4
51 DIRSTATE_V2_MODE_IS_SYMLINK = 1 << 4
52 DIRSTATE_V2_HAS_FALLBACK_EXEC = 1 << 5
52 DIRSTATE_V2_HAS_FALLBACK_EXEC = 1 << 5
53 DIRSTATE_V2_FALLBACK_EXEC = 1 << 6
53 DIRSTATE_V2_FALLBACK_EXEC = 1 << 6
54 DIRSTATE_V2_HAS_FALLBACK_SYMLINK = 1 << 7
54 DIRSTATE_V2_HAS_FALLBACK_SYMLINK = 1 << 7
55 DIRSTATE_V2_FALLBACK_SYMLINK = 1 << 8
55 DIRSTATE_V2_FALLBACK_SYMLINK = 1 << 8
56 DIRSTATE_V2_EXPECTED_STATE_IS_MODIFIED = 1 << 9
56 DIRSTATE_V2_EXPECTED_STATE_IS_MODIFIED = 1 << 9
57 DIRSTATE_V2_HAS_MODE_AND_SIZE = 1 << 10
57 DIRSTATE_V2_HAS_MODE_AND_SIZE = 1 << 10
58 DIRSTATE_V2_HAS_MTIME = 1 << 11
58 DIRSTATE_V2_HAS_MTIME = 1 << 11
59 DIRSTATE_V2_MTIME_SECOND_AMBIGUOUS = 1 << 12
59 DIRSTATE_V2_MTIME_SECOND_AMBIGUOUS = 1 << 12
60 DIRSTATE_V2_DIRECTORY = 1 << 13
60 DIRSTATE_V2_DIRECTORY = 1 << 13
61 DIRSTATE_V2_ALL_UNKNOWN_RECORDED = 1 << 14
61 DIRSTATE_V2_ALL_UNKNOWN_RECORDED = 1 << 14
62 DIRSTATE_V2_ALL_IGNORED_RECORDED = 1 << 15
62 DIRSTATE_V2_ALL_IGNORED_RECORDED = 1 << 15
63
63
64
64
65 @attr.s(slots=True, init=False)
65 @attr.s(slots=True, init=False)
66 class DirstateItem:
66 class DirstateItem:
67 """represent a dirstate entry
67 """represent a dirstate entry
68
68
69 It hold multiple attributes
69 It hold multiple attributes
70
70
71 # about file tracking
71 # about file tracking
72 - wc_tracked: is the file tracked by the working copy
72 - wc_tracked: is the file tracked by the working copy
73 - p1_tracked: is the file tracked in working copy first parent
73 - p1_tracked: is the file tracked in working copy first parent
74 - p2_info: the file has been involved in some merge operation. Either
74 - p2_info: the file has been involved in some merge operation. Either
75 because it was actually merged, or because the p2 version was
75 because it was actually merged, or because the p2 version was
76 ahead, or because some rename moved it there. In either case
76 ahead, or because some rename moved it there. In either case
77 `hg status` will want it displayed as modified.
77 `hg status` will want it displayed as modified.
78
78
79 # about the file state expected from p1 manifest:
79 # about the file state expected from p1 manifest:
80 - mode: the file mode in p1
80 - mode: the file mode in p1
81 - size: the file size in p1
81 - size: the file size in p1
82
82
83 These value can be set to None, which mean we don't have a meaningful value
83 These value can be set to None, which mean we don't have a meaningful value
84 to compare with. Either because we don't really care about them as there
84 to compare with. Either because we don't really care about them as there
85 `status` is known without having to look at the disk or because we don't
85 `status` is known without having to look at the disk or because we don't
86 know these right now and a full comparison will be needed to find out if
86 know these right now and a full comparison will be needed to find out if
87 the file is clean.
87 the file is clean.
88
88
89 # about the file state on disk last time we saw it:
89 # about the file state on disk last time we saw it:
90 - mtime: the last known clean mtime for the file.
90 - mtime: the last known clean mtime for the file.
91
91
92 This value can be set to None if no cachable state exist. Either because we
92 This value can be set to None if no cachable state exist. Either because we
93 do not care (see previous section) or because we could not cache something
93 do not care (see previous section) or because we could not cache something
94 yet.
94 yet.
95 """
95 """
96
96
97 _wc_tracked = attr.ib()
97 _wc_tracked = attr.ib()
98 _p1_tracked = attr.ib()
98 _p1_tracked = attr.ib()
99 _p2_info = attr.ib()
99 _p2_info = attr.ib()
100 _mode = attr.ib()
100 _mode = attr.ib()
101 _size = attr.ib()
101 _size = attr.ib()
102 _mtime_s = attr.ib()
102 _mtime_s = attr.ib()
103 _mtime_ns = attr.ib()
103 _mtime_ns = attr.ib()
104 _fallback_exec = attr.ib()
104 _fallback_exec = attr.ib()
105 _fallback_symlink = attr.ib()
105 _fallback_symlink = attr.ib()
106 _mtime_second_ambiguous = attr.ib()
106 _mtime_second_ambiguous = attr.ib()
107
107
108 def __init__(
108 def __init__(
109 self,
109 self,
110 wc_tracked=False,
110 wc_tracked=False,
111 p1_tracked=False,
111 p1_tracked=False,
112 p2_info=False,
112 p2_info=False,
113 has_meaningful_data=True,
113 has_meaningful_data=True,
114 has_meaningful_mtime=True,
114 has_meaningful_mtime=True,
115 parentfiledata=None,
115 parentfiledata=None,
116 fallback_exec=None,
116 fallback_exec=None,
117 fallback_symlink=None,
117 fallback_symlink=None,
118 ):
118 ):
119 self._wc_tracked = wc_tracked
119 self._wc_tracked = wc_tracked
120 self._p1_tracked = p1_tracked
120 self._p1_tracked = p1_tracked
121 self._p2_info = p2_info
121 self._p2_info = p2_info
122
122
123 self._fallback_exec = fallback_exec
123 self._fallback_exec = fallback_exec
124 self._fallback_symlink = fallback_symlink
124 self._fallback_symlink = fallback_symlink
125
125
126 self._mode = None
126 self._mode = None
127 self._size = None
127 self._size = None
128 self._mtime_s = None
128 self._mtime_s = None
129 self._mtime_ns = None
129 self._mtime_ns = None
130 self._mtime_second_ambiguous = False
130 self._mtime_second_ambiguous = False
131 if parentfiledata is None:
131 if parentfiledata is None:
132 has_meaningful_mtime = False
132 has_meaningful_mtime = False
133 has_meaningful_data = False
133 has_meaningful_data = False
134 elif parentfiledata[2] is None:
134 elif parentfiledata[2] is None:
135 has_meaningful_mtime = False
135 has_meaningful_mtime = False
136 if has_meaningful_data:
136 if has_meaningful_data:
137 self._mode = parentfiledata[0]
137 self._mode = parentfiledata[0]
138 self._size = parentfiledata[1]
138 self._size = parentfiledata[1]
139 if has_meaningful_mtime:
139 if has_meaningful_mtime:
140 (
140 (
141 self._mtime_s,
141 self._mtime_s,
142 self._mtime_ns,
142 self._mtime_ns,
143 self._mtime_second_ambiguous,
143 self._mtime_second_ambiguous,
144 ) = parentfiledata[2]
144 ) = parentfiledata[2]
145
145
146 @classmethod
146 @classmethod
147 def from_v2_data(cls, flags, size, mtime_s, mtime_ns):
147 def from_v2_data(cls, flags, size, mtime_s, mtime_ns):
148 """Build a new DirstateItem object from V2 data"""
148 """Build a new DirstateItem object from V2 data"""
149 has_mode_size = bool(flags & DIRSTATE_V2_HAS_MODE_AND_SIZE)
149 has_mode_size = bool(flags & DIRSTATE_V2_HAS_MODE_AND_SIZE)
150 has_meaningful_mtime = bool(flags & DIRSTATE_V2_HAS_MTIME)
150 has_meaningful_mtime = bool(flags & DIRSTATE_V2_HAS_MTIME)
151 mode = None
151 mode = None
152
152
153 if flags & +DIRSTATE_V2_EXPECTED_STATE_IS_MODIFIED:
153 if flags & +DIRSTATE_V2_EXPECTED_STATE_IS_MODIFIED:
154 # we do not have support for this flag in the code yet,
154 # we do not have support for this flag in the code yet,
155 # force a lookup for this file.
155 # force a lookup for this file.
156 has_mode_size = False
156 has_mode_size = False
157 has_meaningful_mtime = False
157 has_meaningful_mtime = False
158
158
159 fallback_exec = None
159 fallback_exec = None
160 if flags & DIRSTATE_V2_HAS_FALLBACK_EXEC:
160 if flags & DIRSTATE_V2_HAS_FALLBACK_EXEC:
161 fallback_exec = flags & DIRSTATE_V2_FALLBACK_EXEC
161 fallback_exec = flags & DIRSTATE_V2_FALLBACK_EXEC
162
162
163 fallback_symlink = None
163 fallback_symlink = None
164 if flags & DIRSTATE_V2_HAS_FALLBACK_SYMLINK:
164 if flags & DIRSTATE_V2_HAS_FALLBACK_SYMLINK:
165 fallback_symlink = flags & DIRSTATE_V2_FALLBACK_SYMLINK
165 fallback_symlink = flags & DIRSTATE_V2_FALLBACK_SYMLINK
166
166
167 if has_mode_size:
167 if has_mode_size:
168 assert stat.S_IXUSR == 0o100
168 assert stat.S_IXUSR == 0o100
169 if flags & DIRSTATE_V2_MODE_EXEC_PERM:
169 if flags & DIRSTATE_V2_MODE_EXEC_PERM:
170 mode = 0o755
170 mode = 0o755
171 else:
171 else:
172 mode = 0o644
172 mode = 0o644
173 if flags & DIRSTATE_V2_MODE_IS_SYMLINK:
173 if flags & DIRSTATE_V2_MODE_IS_SYMLINK:
174 mode |= stat.S_IFLNK
174 mode |= stat.S_IFLNK
175 else:
175 else:
176 mode |= stat.S_IFREG
176 mode |= stat.S_IFREG
177
177
178 second_ambiguous = flags & DIRSTATE_V2_MTIME_SECOND_AMBIGUOUS
178 second_ambiguous = flags & DIRSTATE_V2_MTIME_SECOND_AMBIGUOUS
179 return cls(
179 return cls(
180 wc_tracked=bool(flags & DIRSTATE_V2_WDIR_TRACKED),
180 wc_tracked=bool(flags & DIRSTATE_V2_WDIR_TRACKED),
181 p1_tracked=bool(flags & DIRSTATE_V2_P1_TRACKED),
181 p1_tracked=bool(flags & DIRSTATE_V2_P1_TRACKED),
182 p2_info=bool(flags & DIRSTATE_V2_P2_INFO),
182 p2_info=bool(flags & DIRSTATE_V2_P2_INFO),
183 has_meaningful_data=has_mode_size,
183 has_meaningful_data=has_mode_size,
184 has_meaningful_mtime=has_meaningful_mtime,
184 has_meaningful_mtime=has_meaningful_mtime,
185 parentfiledata=(mode, size, (mtime_s, mtime_ns, second_ambiguous)),
185 parentfiledata=(mode, size, (mtime_s, mtime_ns, second_ambiguous)),
186 fallback_exec=fallback_exec,
186 fallback_exec=fallback_exec,
187 fallback_symlink=fallback_symlink,
187 fallback_symlink=fallback_symlink,
188 )
188 )
189
189
190 @classmethod
190 @classmethod
191 def from_v1_data(cls, state, mode, size, mtime):
191 def from_v1_data(cls, state, mode, size, mtime):
192 """Build a new DirstateItem object from V1 data
192 """Build a new DirstateItem object from V1 data
193
193
194 Since the dirstate-v1 format is frozen, the signature of this function
194 Since the dirstate-v1 format is frozen, the signature of this function
195 is not expected to change, unlike the __init__ one.
195 is not expected to change, unlike the __init__ one.
196 """
196 """
197 if state == b'm':
197 if state == b'm':
198 return cls(wc_tracked=True, p1_tracked=True, p2_info=True)
198 return cls(wc_tracked=True, p1_tracked=True, p2_info=True)
199 elif state == b'a':
199 elif state == b'a':
200 return cls(wc_tracked=True)
200 return cls(wc_tracked=True)
201 elif state == b'r':
201 elif state == b'r':
202 if size == NONNORMAL:
202 if size == NONNORMAL:
203 p1_tracked = True
203 p1_tracked = True
204 p2_info = True
204 p2_info = True
205 elif size == FROM_P2:
205 elif size == FROM_P2:
206 p1_tracked = False
206 p1_tracked = False
207 p2_info = True
207 p2_info = True
208 else:
208 else:
209 p1_tracked = True
209 p1_tracked = True
210 p2_info = False
210 p2_info = False
211 return cls(p1_tracked=p1_tracked, p2_info=p2_info)
211 return cls(p1_tracked=p1_tracked, p2_info=p2_info)
212 elif state == b'n':
212 elif state == b'n':
213 if size == FROM_P2:
213 if size == FROM_P2:
214 return cls(wc_tracked=True, p2_info=True)
214 return cls(wc_tracked=True, p2_info=True)
215 elif size == NONNORMAL:
215 elif size == NONNORMAL:
216 return cls(wc_tracked=True, p1_tracked=True)
216 return cls(wc_tracked=True, p1_tracked=True)
217 elif mtime == AMBIGUOUS_TIME:
217 elif mtime == AMBIGUOUS_TIME:
218 return cls(
218 return cls(
219 wc_tracked=True,
219 wc_tracked=True,
220 p1_tracked=True,
220 p1_tracked=True,
221 has_meaningful_mtime=False,
221 has_meaningful_mtime=False,
222 parentfiledata=(mode, size, (42, 0, False)),
222 parentfiledata=(mode, size, (42, 0, False)),
223 )
223 )
224 else:
224 else:
225 return cls(
225 return cls(
226 wc_tracked=True,
226 wc_tracked=True,
227 p1_tracked=True,
227 p1_tracked=True,
228 parentfiledata=(mode, size, (mtime, 0, False)),
228 parentfiledata=(mode, size, (mtime, 0, False)),
229 )
229 )
230 else:
230 else:
231 raise RuntimeError(b'unknown state: %s' % state)
231 raise RuntimeError(b'unknown state: %s' % state)
232
232
233 def set_possibly_dirty(self):
233 def set_possibly_dirty(self):
234 """Mark a file as "possibly dirty"
234 """Mark a file as "possibly dirty"
235
235
236 This means the next status call will have to actually check its content
236 This means the next status call will have to actually check its content
237 to make sure it is correct.
237 to make sure it is correct.
238 """
238 """
239 self._mtime_s = None
239 self._mtime_s = None
240 self._mtime_ns = None
240 self._mtime_ns = None
241
241
242 def set_clean(self, mode, size, mtime):
242 def set_clean(self, mode, size, mtime):
243 """mark a file as "clean" cancelling potential "possibly dirty call"
243 """mark a file as "clean" cancelling potential "possibly dirty call"
244
244
245 Note: this function is a descendant of `dirstate.normal` and is
245 Note: this function is a descendant of `dirstate.normal` and is
246 currently expected to be call on "normal" entry only. There are not
246 currently expected to be call on "normal" entry only. There are not
247 reason for this to not change in the future as long as the ccode is
247 reason for this to not change in the future as long as the ccode is
248 updated to preserve the proper state of the non-normal files.
248 updated to preserve the proper state of the non-normal files.
249 """
249 """
250 self._wc_tracked = True
250 self._wc_tracked = True
251 self._p1_tracked = True
251 self._p1_tracked = True
252 self._mode = mode
252 self._mode = mode
253 self._size = size
253 self._size = size
254 self._mtime_s, self._mtime_ns, self._mtime_second_ambiguous = mtime
254 self._mtime_s, self._mtime_ns, self._mtime_second_ambiguous = mtime
255
255
256 def set_tracked(self):
256 def set_tracked(self):
257 """mark a file as tracked in the working copy
257 """mark a file as tracked in the working copy
258
258
259 This will ultimately be called by command like `hg add`.
259 This will ultimately be called by command like `hg add`.
260 """
260 """
261 self._wc_tracked = True
261 self._wc_tracked = True
262 # `set_tracked` is replacing various `normallookup` call. So we mark
262 # `set_tracked` is replacing various `normallookup` call. So we mark
263 # the files as needing lookup
263 # the files as needing lookup
264 #
264 #
265 # Consider dropping this in the future in favor of something less broad.
265 # Consider dropping this in the future in favor of something less broad.
266 self._mtime_s = None
266 self._mtime_s = None
267 self._mtime_ns = None
267 self._mtime_ns = None
268
268
269 def set_untracked(self):
269 def set_untracked(self):
270 """mark a file as untracked in the working copy
270 """mark a file as untracked in the working copy
271
271
272 This will ultimately be called by command like `hg remove`.
272 This will ultimately be called by command like `hg remove`.
273 """
273 """
274 self._wc_tracked = False
274 self._wc_tracked = False
275 self._mode = None
275 self._mode = None
276 self._size = None
276 self._size = None
277 self._mtime_s = None
277 self._mtime_s = None
278 self._mtime_ns = None
278 self._mtime_ns = None
279
279
280 def drop_merge_data(self):
280 def drop_merge_data(self):
281 """remove all "merge-only" information from a DirstateItem
281 """remove all "merge-only" information from a DirstateItem
282
282
283 This is to be call by the dirstatemap code when the second parent is dropped
283 This is to be call by the dirstatemap code when the second parent is dropped
284 """
284 """
285 if self._p2_info:
285 if self._p2_info:
286 self._p2_info = False
286 self._p2_info = False
287 self._mode = None
287 self._mode = None
288 self._size = None
288 self._size = None
289 self._mtime_s = None
289 self._mtime_s = None
290 self._mtime_ns = None
290 self._mtime_ns = None
291
291
292 @property
292 @property
293 def mode(self):
293 def mode(self):
294 return self._v1_mode()
294 return self._v1_mode()
295
295
296 @property
296 @property
297 def size(self):
297 def size(self):
298 return self._v1_size()
298 return self._v1_size()
299
299
300 @property
300 @property
301 def mtime(self):
301 def mtime(self):
302 return self._v1_mtime()
302 return self._v1_mtime()
303
303
304 def mtime_likely_equal_to(self, other_mtime):
304 def mtime_likely_equal_to(self, other_mtime):
305 self_sec = self._mtime_s
305 self_sec = self._mtime_s
306 if self_sec is None:
306 if self_sec is None:
307 return False
307 return False
308 self_ns = self._mtime_ns
308 self_ns = self._mtime_ns
309 other_sec, other_ns, second_ambiguous = other_mtime
309 other_sec, other_ns, second_ambiguous = other_mtime
310 if self_sec != other_sec:
310 if self_sec != other_sec:
311 # seconds are different theses mtime are definitly not equal
311 # seconds are different theses mtime are definitly not equal
312 return False
312 return False
313 elif other_ns == 0 or self_ns == 0:
313 elif other_ns == 0 or self_ns == 0:
314 # at least one side as no nano-seconds information
314 # at least one side as no nano-seconds information
315
315
316 if self._mtime_second_ambiguous:
316 if self._mtime_second_ambiguous:
317 # We cannot trust the mtime in this case
317 # We cannot trust the mtime in this case
318 return False
318 return False
319 else:
319 else:
320 # the "seconds" value was reliable on its own. We are good to go.
320 # the "seconds" value was reliable on its own. We are good to go.
321 return True
321 return True
322 else:
322 else:
323 # We have nano second information, let us use them !
323 # We have nano second information, let us use them !
324 return self_ns == other_ns
324 return self_ns == other_ns
325
325
326 @property
326 @property
327 def state(self):
327 def state(self):
328 """
328 """
329 States are:
329 States are:
330 n normal
330 n normal
331 m needs merging
331 m needs merging
332 r marked for removal
332 r marked for removal
333 a marked for addition
333 a marked for addition
334
334
335 XXX This "state" is a bit obscure and mostly a direct expression of the
335 XXX This "state" is a bit obscure and mostly a direct expression of the
336 dirstatev1 format. It would make sense to ultimately deprecate it in
336 dirstatev1 format. It would make sense to ultimately deprecate it in
337 favor of the more "semantic" attributes.
337 favor of the more "semantic" attributes.
338 """
338 """
339 if not self.any_tracked:
339 if not self.any_tracked:
340 return b'?'
340 return b'?'
341 return self._v1_state()
341 return self._v1_state()
342
342
343 @property
343 @property
344 def has_fallback_exec(self):
344 def has_fallback_exec(self):
345 """True if "fallback" information are available for the "exec" bit
345 """True if "fallback" information are available for the "exec" bit
346
346
347 Fallback information can be stored in the dirstate to keep track of
347 Fallback information can be stored in the dirstate to keep track of
348 filesystem attribute tracked by Mercurial when the underlying file
348 filesystem attribute tracked by Mercurial when the underlying file
349 system or operating system does not support that property, (e.g.
349 system or operating system does not support that property, (e.g.
350 Windows).
350 Windows).
351
351
352 Not all version of the dirstate on-disk storage support preserving this
352 Not all version of the dirstate on-disk storage support preserving this
353 information.
353 information.
354 """
354 """
355 return self._fallback_exec is not None
355 return self._fallback_exec is not None
356
356
357 @property
357 @property
358 def fallback_exec(self):
358 def fallback_exec(self):
359 """ "fallback" information for the executable bit
359 """ "fallback" information for the executable bit
360
360
361 True if the file should be considered executable when we cannot get
361 True if the file should be considered executable when we cannot get
362 this information from the files system. False if it should be
362 this information from the files system. False if it should be
363 considered non-executable.
363 considered non-executable.
364
364
365 See has_fallback_exec for details."""
365 See has_fallback_exec for details."""
366 return self._fallback_exec
366 return self._fallback_exec
367
367
368 @fallback_exec.setter
368 @fallback_exec.setter
369 def set_fallback_exec(self, value):
369 def set_fallback_exec(self, value):
370 """control "fallback" executable bit
370 """control "fallback" executable bit
371
371
372 Set to:
372 Set to:
373 - True if the file should be considered executable,
373 - True if the file should be considered executable,
374 - False if the file should be considered non-executable,
374 - False if the file should be considered non-executable,
375 - None if we do not have valid fallback data.
375 - None if we do not have valid fallback data.
376
376
377 See has_fallback_exec for details."""
377 See has_fallback_exec for details."""
378 if value is None:
378 if value is None:
379 self._fallback_exec = None
379 self._fallback_exec = None
380 else:
380 else:
381 self._fallback_exec = bool(value)
381 self._fallback_exec = bool(value)
382
382
383 @property
383 @property
384 def has_fallback_symlink(self):
384 def has_fallback_symlink(self):
385 """True if "fallback" information are available for symlink status
385 """True if "fallback" information are available for symlink status
386
386
387 Fallback information can be stored in the dirstate to keep track of
387 Fallback information can be stored in the dirstate to keep track of
388 filesystem attribute tracked by Mercurial when the underlying file
388 filesystem attribute tracked by Mercurial when the underlying file
389 system or operating system does not support that property, (e.g.
389 system or operating system does not support that property, (e.g.
390 Windows).
390 Windows).
391
391
392 Not all version of the dirstate on-disk storage support preserving this
392 Not all version of the dirstate on-disk storage support preserving this
393 information."""
393 information."""
394 return self._fallback_symlink is not None
394 return self._fallback_symlink is not None
395
395
396 @property
396 @property
397 def fallback_symlink(self):
397 def fallback_symlink(self):
398 """ "fallback" information for symlink status
398 """ "fallback" information for symlink status
399
399
400 True if the file should be considered executable when we cannot get
400 True if the file should be considered executable when we cannot get
401 this information from the files system. False if it should be
401 this information from the files system. False if it should be
402 considered non-executable.
402 considered non-executable.
403
403
404 See has_fallback_exec for details."""
404 See has_fallback_exec for details."""
405 return self._fallback_symlink
405 return self._fallback_symlink
406
406
407 @fallback_symlink.setter
407 @fallback_symlink.setter
408 def set_fallback_symlink(self, value):
408 def set_fallback_symlink(self, value):
409 """control "fallback" symlink status
409 """control "fallback" symlink status
410
410
411 Set to:
411 Set to:
412 - True if the file should be considered a symlink,
412 - True if the file should be considered a symlink,
413 - False if the file should be considered not a symlink,
413 - False if the file should be considered not a symlink,
414 - None if we do not have valid fallback data.
414 - None if we do not have valid fallback data.
415
415
416 See has_fallback_symlink for details."""
416 See has_fallback_symlink for details."""
417 if value is None:
417 if value is None:
418 self._fallback_symlink = None
418 self._fallback_symlink = None
419 else:
419 else:
420 self._fallback_symlink = bool(value)
420 self._fallback_symlink = bool(value)
421
421
422 @property
422 @property
423 def tracked(self):
423 def tracked(self):
424 """True is the file is tracked in the working copy"""
424 """True is the file is tracked in the working copy"""
425 return self._wc_tracked
425 return self._wc_tracked
426
426
427 @property
427 @property
428 def any_tracked(self):
428 def any_tracked(self):
429 """True is the file is tracked anywhere (wc or parents)"""
429 """True is the file is tracked anywhere (wc or parents)"""
430 return self._wc_tracked or self._p1_tracked or self._p2_info
430 return self._wc_tracked or self._p1_tracked or self._p2_info
431
431
432 @property
432 @property
433 def added(self):
433 def added(self):
434 """True if the file has been added"""
434 """True if the file has been added"""
435 return self._wc_tracked and not (self._p1_tracked or self._p2_info)
435 return self._wc_tracked and not (self._p1_tracked or self._p2_info)
436
436
437 @property
437 @property
438 def modified(self):
439 """True if the file has been modified"""
440 return self._wc_tracked and self._p1_tracked and self._p2_info
441
442 @property
438 def maybe_clean(self):
443 def maybe_clean(self):
439 """True if the file has a chance to be in the "clean" state"""
444 """True if the file has a chance to be in the "clean" state"""
440 if not self._wc_tracked:
445 if not self._wc_tracked:
441 return False
446 return False
442 elif not self._p1_tracked:
447 elif not self._p1_tracked:
443 return False
448 return False
444 elif self._p2_info:
449 elif self._p2_info:
445 return False
450 return False
446 return True
451 return True
447
452
448 @property
453 @property
449 def p1_tracked(self):
454 def p1_tracked(self):
450 """True if the file is tracked in the first parent manifest"""
455 """True if the file is tracked in the first parent manifest"""
451 return self._p1_tracked
456 return self._p1_tracked
452
457
453 @property
458 @property
454 def p2_info(self):
459 def p2_info(self):
455 """True if the file needed to merge or apply any input from p2
460 """True if the file needed to merge or apply any input from p2
456
461
457 See the class documentation for details.
462 See the class documentation for details.
458 """
463 """
459 return self._wc_tracked and self._p2_info
464 return self._wc_tracked and self._p2_info
460
465
461 @property
466 @property
462 def removed(self):
467 def removed(self):
463 """True if the file has been removed"""
468 """True if the file has been removed"""
464 return not self._wc_tracked and (self._p1_tracked or self._p2_info)
469 return not self._wc_tracked and (self._p1_tracked or self._p2_info)
465
470
466 def v2_data(self):
471 def v2_data(self):
467 """Returns (flags, mode, size, mtime) for v2 serialization"""
472 """Returns (flags, mode, size, mtime) for v2 serialization"""
468 flags = 0
473 flags = 0
469 if self._wc_tracked:
474 if self._wc_tracked:
470 flags |= DIRSTATE_V2_WDIR_TRACKED
475 flags |= DIRSTATE_V2_WDIR_TRACKED
471 if self._p1_tracked:
476 if self._p1_tracked:
472 flags |= DIRSTATE_V2_P1_TRACKED
477 flags |= DIRSTATE_V2_P1_TRACKED
473 if self._p2_info:
478 if self._p2_info:
474 flags |= DIRSTATE_V2_P2_INFO
479 flags |= DIRSTATE_V2_P2_INFO
475 if self._mode is not None and self._size is not None:
480 if self._mode is not None and self._size is not None:
476 flags |= DIRSTATE_V2_HAS_MODE_AND_SIZE
481 flags |= DIRSTATE_V2_HAS_MODE_AND_SIZE
477 if self.mode & stat.S_IXUSR:
482 if self.mode & stat.S_IXUSR:
478 flags |= DIRSTATE_V2_MODE_EXEC_PERM
483 flags |= DIRSTATE_V2_MODE_EXEC_PERM
479 if stat.S_ISLNK(self.mode):
484 if stat.S_ISLNK(self.mode):
480 flags |= DIRSTATE_V2_MODE_IS_SYMLINK
485 flags |= DIRSTATE_V2_MODE_IS_SYMLINK
481 if self._mtime_s is not None:
486 if self._mtime_s is not None:
482 flags |= DIRSTATE_V2_HAS_MTIME
487 flags |= DIRSTATE_V2_HAS_MTIME
483 if self._mtime_second_ambiguous:
488 if self._mtime_second_ambiguous:
484 flags |= DIRSTATE_V2_MTIME_SECOND_AMBIGUOUS
489 flags |= DIRSTATE_V2_MTIME_SECOND_AMBIGUOUS
485
490
486 if self._fallback_exec is not None:
491 if self._fallback_exec is not None:
487 flags |= DIRSTATE_V2_HAS_FALLBACK_EXEC
492 flags |= DIRSTATE_V2_HAS_FALLBACK_EXEC
488 if self._fallback_exec:
493 if self._fallback_exec:
489 flags |= DIRSTATE_V2_FALLBACK_EXEC
494 flags |= DIRSTATE_V2_FALLBACK_EXEC
490
495
491 if self._fallback_symlink is not None:
496 if self._fallback_symlink is not None:
492 flags |= DIRSTATE_V2_HAS_FALLBACK_SYMLINK
497 flags |= DIRSTATE_V2_HAS_FALLBACK_SYMLINK
493 if self._fallback_symlink:
498 if self._fallback_symlink:
494 flags |= DIRSTATE_V2_FALLBACK_SYMLINK
499 flags |= DIRSTATE_V2_FALLBACK_SYMLINK
495
500
496 # Note: we do not need to do anything regarding
501 # Note: we do not need to do anything regarding
497 # DIRSTATE_V2_ALL_UNKNOWN_RECORDED and DIRSTATE_V2_ALL_IGNORED_RECORDED
502 # DIRSTATE_V2_ALL_UNKNOWN_RECORDED and DIRSTATE_V2_ALL_IGNORED_RECORDED
498 # since we never set _DIRSTATE_V2_HAS_DIRCTORY_MTIME
503 # since we never set _DIRSTATE_V2_HAS_DIRCTORY_MTIME
499 return (flags, self._size or 0, self._mtime_s or 0, self._mtime_ns or 0)
504 return (flags, self._size or 0, self._mtime_s or 0, self._mtime_ns or 0)
500
505
501 def _v1_state(self):
506 def _v1_state(self):
502 """return a "state" suitable for v1 serialization"""
507 """return a "state" suitable for v1 serialization"""
503 if not self.any_tracked:
508 if not self.any_tracked:
504 # the object has no state to record, this is -currently-
509 # the object has no state to record, this is -currently-
505 # unsupported
510 # unsupported
506 raise RuntimeError('untracked item')
511 raise RuntimeError('untracked item')
507 elif self.removed:
512 elif self.removed:
508 return b'r'
513 return b'r'
509 elif self._p1_tracked and self._p2_info:
514 elif self._p1_tracked and self._p2_info:
510 return b'm'
515 return b'm'
511 elif self.added:
516 elif self.added:
512 return b'a'
517 return b'a'
513 else:
518 else:
514 return b'n'
519 return b'n'
515
520
516 def _v1_mode(self):
521 def _v1_mode(self):
517 """return a "mode" suitable for v1 serialization"""
522 """return a "mode" suitable for v1 serialization"""
518 return self._mode if self._mode is not None else 0
523 return self._mode if self._mode is not None else 0
519
524
520 def _v1_size(self):
525 def _v1_size(self):
521 """return a "size" suitable for v1 serialization"""
526 """return a "size" suitable for v1 serialization"""
522 if not self.any_tracked:
527 if not self.any_tracked:
523 # the object has no state to record, this is -currently-
528 # the object has no state to record, this is -currently-
524 # unsupported
529 # unsupported
525 raise RuntimeError('untracked item')
530 raise RuntimeError('untracked item')
526 elif self.removed and self._p1_tracked and self._p2_info:
531 elif self.removed and self._p1_tracked and self._p2_info:
527 return NONNORMAL
532 return NONNORMAL
528 elif self._p2_info:
533 elif self._p2_info:
529 return FROM_P2
534 return FROM_P2
530 elif self.removed:
535 elif self.removed:
531 return 0
536 return 0
532 elif self.added:
537 elif self.added:
533 return NONNORMAL
538 return NONNORMAL
534 elif self._size is None:
539 elif self._size is None:
535 return NONNORMAL
540 return NONNORMAL
536 else:
541 else:
537 return self._size
542 return self._size
538
543
539 def _v1_mtime(self):
544 def _v1_mtime(self):
540 """return a "mtime" suitable for v1 serialization"""
545 """return a "mtime" suitable for v1 serialization"""
541 if not self.any_tracked:
546 if not self.any_tracked:
542 # the object has no state to record, this is -currently-
547 # the object has no state to record, this is -currently-
543 # unsupported
548 # unsupported
544 raise RuntimeError('untracked item')
549 raise RuntimeError('untracked item')
545 elif self.removed:
550 elif self.removed:
546 return 0
551 return 0
547 elif self._mtime_s is None:
552 elif self._mtime_s is None:
548 return AMBIGUOUS_TIME
553 return AMBIGUOUS_TIME
549 elif self._p2_info:
554 elif self._p2_info:
550 return AMBIGUOUS_TIME
555 return AMBIGUOUS_TIME
551 elif not self._p1_tracked:
556 elif not self._p1_tracked:
552 return AMBIGUOUS_TIME
557 return AMBIGUOUS_TIME
553 elif self._mtime_second_ambiguous:
558 elif self._mtime_second_ambiguous:
554 return AMBIGUOUS_TIME
559 return AMBIGUOUS_TIME
555 else:
560 else:
556 return self._mtime_s
561 return self._mtime_s
557
562
558
563
559 def gettype(q):
564 def gettype(q):
560 return int(q & 0xFFFF)
565 return int(q & 0xFFFF)
561
566
562
567
563 class BaseIndexObject:
568 class BaseIndexObject:
564 # Can I be passed to an algorithme implemented in Rust ?
569 # Can I be passed to an algorithme implemented in Rust ?
565 rust_ext_compat = 0
570 rust_ext_compat = 0
566 # Format of an index entry according to Python's `struct` language
571 # Format of an index entry according to Python's `struct` language
567 index_format = revlog_constants.INDEX_ENTRY_V1
572 index_format = revlog_constants.INDEX_ENTRY_V1
568 # Size of a C unsigned long long int, platform independent
573 # Size of a C unsigned long long int, platform independent
569 big_int_size = struct.calcsize(b'>Q')
574 big_int_size = struct.calcsize(b'>Q')
570 # Size of a C long int, platform independent
575 # Size of a C long int, platform independent
571 int_size = struct.calcsize(b'>i')
576 int_size = struct.calcsize(b'>i')
572 # An empty index entry, used as a default value to be overridden, or nullrev
577 # An empty index entry, used as a default value to be overridden, or nullrev
573 null_item = (
578 null_item = (
574 0,
579 0,
575 0,
580 0,
576 0,
581 0,
577 -1,
582 -1,
578 -1,
583 -1,
579 -1,
584 -1,
580 -1,
585 -1,
581 sha1nodeconstants.nullid,
586 sha1nodeconstants.nullid,
582 0,
587 0,
583 0,
588 0,
584 revlog_constants.COMP_MODE_INLINE,
589 revlog_constants.COMP_MODE_INLINE,
585 revlog_constants.COMP_MODE_INLINE,
590 revlog_constants.COMP_MODE_INLINE,
586 revlog_constants.RANK_UNKNOWN,
591 revlog_constants.RANK_UNKNOWN,
587 )
592 )
588
593
589 @util.propertycache
594 @util.propertycache
590 def entry_size(self):
595 def entry_size(self):
591 return self.index_format.size
596 return self.index_format.size
592
597
593 @util.propertycache
598 @util.propertycache
594 def _nodemap(self):
599 def _nodemap(self):
595 nodemap = nodemaputil.NodeMap({sha1nodeconstants.nullid: nullrev})
600 nodemap = nodemaputil.NodeMap({sha1nodeconstants.nullid: nullrev})
596 for r in range(0, len(self)):
601 for r in range(0, len(self)):
597 n = self[r][7]
602 n = self[r][7]
598 nodemap[n] = r
603 nodemap[n] = r
599 return nodemap
604 return nodemap
600
605
601 def has_node(self, node):
606 def has_node(self, node):
602 """return True if the node exist in the index"""
607 """return True if the node exist in the index"""
603 return node in self._nodemap
608 return node in self._nodemap
604
609
605 def rev(self, node):
610 def rev(self, node):
606 """return a revision for a node
611 """return a revision for a node
607
612
608 If the node is unknown, raise a RevlogError"""
613 If the node is unknown, raise a RevlogError"""
609 return self._nodemap[node]
614 return self._nodemap[node]
610
615
611 def get_rev(self, node):
616 def get_rev(self, node):
612 """return a revision for a node
617 """return a revision for a node
613
618
614 If the node is unknown, return None"""
619 If the node is unknown, return None"""
615 return self._nodemap.get(node)
620 return self._nodemap.get(node)
616
621
617 def _stripnodes(self, start):
622 def _stripnodes(self, start):
618 if '_nodemap' in vars(self):
623 if '_nodemap' in vars(self):
619 for r in range(start, len(self)):
624 for r in range(start, len(self)):
620 n = self[r][7]
625 n = self[r][7]
621 del self._nodemap[n]
626 del self._nodemap[n]
622
627
623 def clearcaches(self):
628 def clearcaches(self):
624 self.__dict__.pop('_nodemap', None)
629 self.__dict__.pop('_nodemap', None)
625
630
626 def __len__(self):
631 def __len__(self):
627 return self._lgt + len(self._extra)
632 return self._lgt + len(self._extra)
628
633
629 def append(self, tup):
634 def append(self, tup):
630 if '_nodemap' in vars(self):
635 if '_nodemap' in vars(self):
631 self._nodemap[tup[7]] = len(self)
636 self._nodemap[tup[7]] = len(self)
632 data = self._pack_entry(len(self), tup)
637 data = self._pack_entry(len(self), tup)
633 self._extra.append(data)
638 self._extra.append(data)
634
639
635 def _pack_entry(self, rev, entry):
640 def _pack_entry(self, rev, entry):
636 assert entry[8] == 0
641 assert entry[8] == 0
637 assert entry[9] == 0
642 assert entry[9] == 0
638 return self.index_format.pack(*entry[:8])
643 return self.index_format.pack(*entry[:8])
639
644
640 def _check_index(self, i):
645 def _check_index(self, i):
641 if not isinstance(i, int):
646 if not isinstance(i, int):
642 raise TypeError(b"expecting int indexes")
647 raise TypeError(b"expecting int indexes")
643 if i < 0 or i >= len(self):
648 if i < 0 or i >= len(self):
644 raise IndexError(i)
649 raise IndexError(i)
645
650
646 def __getitem__(self, i):
651 def __getitem__(self, i):
647 if i == -1:
652 if i == -1:
648 return self.null_item
653 return self.null_item
649 self._check_index(i)
654 self._check_index(i)
650 if i >= self._lgt:
655 if i >= self._lgt:
651 data = self._extra[i - self._lgt]
656 data = self._extra[i - self._lgt]
652 else:
657 else:
653 index = self._calculate_index(i)
658 index = self._calculate_index(i)
654 data = self._data[index : index + self.entry_size]
659 data = self._data[index : index + self.entry_size]
655 r = self._unpack_entry(i, data)
660 r = self._unpack_entry(i, data)
656 if self._lgt and i == 0:
661 if self._lgt and i == 0:
657 offset = revlogutils.offset_type(0, gettype(r[0]))
662 offset = revlogutils.offset_type(0, gettype(r[0]))
658 r = (offset,) + r[1:]
663 r = (offset,) + r[1:]
659 return r
664 return r
660
665
661 def _unpack_entry(self, rev, data):
666 def _unpack_entry(self, rev, data):
662 r = self.index_format.unpack(data)
667 r = self.index_format.unpack(data)
663 r = r + (
668 r = r + (
664 0,
669 0,
665 0,
670 0,
666 revlog_constants.COMP_MODE_INLINE,
671 revlog_constants.COMP_MODE_INLINE,
667 revlog_constants.COMP_MODE_INLINE,
672 revlog_constants.COMP_MODE_INLINE,
668 revlog_constants.RANK_UNKNOWN,
673 revlog_constants.RANK_UNKNOWN,
669 )
674 )
670 return r
675 return r
671
676
672 def pack_header(self, header):
677 def pack_header(self, header):
673 """pack header information as binary"""
678 """pack header information as binary"""
674 v_fmt = revlog_constants.INDEX_HEADER
679 v_fmt = revlog_constants.INDEX_HEADER
675 return v_fmt.pack(header)
680 return v_fmt.pack(header)
676
681
677 def entry_binary(self, rev):
682 def entry_binary(self, rev):
678 """return the raw binary string representing a revision"""
683 """return the raw binary string representing a revision"""
679 entry = self[rev]
684 entry = self[rev]
680 p = revlog_constants.INDEX_ENTRY_V1.pack(*entry[:8])
685 p = revlog_constants.INDEX_ENTRY_V1.pack(*entry[:8])
681 if rev == 0:
686 if rev == 0:
682 p = p[revlog_constants.INDEX_HEADER.size :]
687 p = p[revlog_constants.INDEX_HEADER.size :]
683 return p
688 return p
684
689
685
690
686 class IndexObject(BaseIndexObject):
691 class IndexObject(BaseIndexObject):
687 def __init__(self, data):
692 def __init__(self, data):
688 assert len(data) % self.entry_size == 0, (
693 assert len(data) % self.entry_size == 0, (
689 len(data),
694 len(data),
690 self.entry_size,
695 self.entry_size,
691 len(data) % self.entry_size,
696 len(data) % self.entry_size,
692 )
697 )
693 self._data = data
698 self._data = data
694 self._lgt = len(data) // self.entry_size
699 self._lgt = len(data) // self.entry_size
695 self._extra = []
700 self._extra = []
696
701
697 def _calculate_index(self, i):
702 def _calculate_index(self, i):
698 return i * self.entry_size
703 return i * self.entry_size
699
704
700 def __delitem__(self, i):
705 def __delitem__(self, i):
701 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
706 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
702 raise ValueError(b"deleting slices only supports a:-1 with step 1")
707 raise ValueError(b"deleting slices only supports a:-1 with step 1")
703 i = i.start
708 i = i.start
704 self._check_index(i)
709 self._check_index(i)
705 self._stripnodes(i)
710 self._stripnodes(i)
706 if i < self._lgt:
711 if i < self._lgt:
707 self._data = self._data[: i * self.entry_size]
712 self._data = self._data[: i * self.entry_size]
708 self._lgt = i
713 self._lgt = i
709 self._extra = []
714 self._extra = []
710 else:
715 else:
711 self._extra = self._extra[: i - self._lgt]
716 self._extra = self._extra[: i - self._lgt]
712
717
713
718
714 class PersistentNodeMapIndexObject(IndexObject):
719 class PersistentNodeMapIndexObject(IndexObject):
715 """a Debug oriented class to test persistent nodemap
720 """a Debug oriented class to test persistent nodemap
716
721
717 We need a simple python object to test API and higher level behavior. See
722 We need a simple python object to test API and higher level behavior. See
718 the Rust implementation for more serious usage. This should be used only
723 the Rust implementation for more serious usage. This should be used only
719 through the dedicated `devel.persistent-nodemap` config.
724 through the dedicated `devel.persistent-nodemap` config.
720 """
725 """
721
726
722 def nodemap_data_all(self):
727 def nodemap_data_all(self):
723 """Return bytes containing a full serialization of a nodemap
728 """Return bytes containing a full serialization of a nodemap
724
729
725 The nodemap should be valid for the full set of revisions in the
730 The nodemap should be valid for the full set of revisions in the
726 index."""
731 index."""
727 return nodemaputil.persistent_data(self)
732 return nodemaputil.persistent_data(self)
728
733
729 def nodemap_data_incremental(self):
734 def nodemap_data_incremental(self):
730 """Return bytes containing a incremental update to persistent nodemap
735 """Return bytes containing a incremental update to persistent nodemap
731
736
732 This containst the data for an append-only update of the data provided
737 This containst the data for an append-only update of the data provided
733 in the last call to `update_nodemap_data`.
738 in the last call to `update_nodemap_data`.
734 """
739 """
735 if self._nm_root is None:
740 if self._nm_root is None:
736 return None
741 return None
737 docket = self._nm_docket
742 docket = self._nm_docket
738 changed, data = nodemaputil.update_persistent_data(
743 changed, data = nodemaputil.update_persistent_data(
739 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
744 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
740 )
745 )
741
746
742 self._nm_root = self._nm_max_idx = self._nm_docket = None
747 self._nm_root = self._nm_max_idx = self._nm_docket = None
743 return docket, changed, data
748 return docket, changed, data
744
749
745 def update_nodemap_data(self, docket, nm_data):
750 def update_nodemap_data(self, docket, nm_data):
746 """provide full block of persisted binary data for a nodemap
751 """provide full block of persisted binary data for a nodemap
747
752
748 The data are expected to come from disk. See `nodemap_data_all` for a
753 The data are expected to come from disk. See `nodemap_data_all` for a
749 produceur of such data."""
754 produceur of such data."""
750 if nm_data is not None:
755 if nm_data is not None:
751 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
756 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
752 if self._nm_root:
757 if self._nm_root:
753 self._nm_docket = docket
758 self._nm_docket = docket
754 else:
759 else:
755 self._nm_root = self._nm_max_idx = self._nm_docket = None
760 self._nm_root = self._nm_max_idx = self._nm_docket = None
756
761
757
762
758 class InlinedIndexObject(BaseIndexObject):
763 class InlinedIndexObject(BaseIndexObject):
759 def __init__(self, data, inline=0):
764 def __init__(self, data, inline=0):
760 self._data = data
765 self._data = data
761 self._lgt = self._inline_scan(None)
766 self._lgt = self._inline_scan(None)
762 self._inline_scan(self._lgt)
767 self._inline_scan(self._lgt)
763 self._extra = []
768 self._extra = []
764
769
765 def _inline_scan(self, lgt):
770 def _inline_scan(self, lgt):
766 off = 0
771 off = 0
767 if lgt is not None:
772 if lgt is not None:
768 self._offsets = [0] * lgt
773 self._offsets = [0] * lgt
769 count = 0
774 count = 0
770 while off <= len(self._data) - self.entry_size:
775 while off <= len(self._data) - self.entry_size:
771 start = off + self.big_int_size
776 start = off + self.big_int_size
772 (s,) = struct.unpack(
777 (s,) = struct.unpack(
773 b'>i',
778 b'>i',
774 self._data[start : start + self.int_size],
779 self._data[start : start + self.int_size],
775 )
780 )
776 if lgt is not None:
781 if lgt is not None:
777 self._offsets[count] = off
782 self._offsets[count] = off
778 count += 1
783 count += 1
779 off += self.entry_size + s
784 off += self.entry_size + s
780 if off != len(self._data):
785 if off != len(self._data):
781 raise ValueError(b"corrupted data")
786 raise ValueError(b"corrupted data")
782 return count
787 return count
783
788
784 def __delitem__(self, i):
789 def __delitem__(self, i):
785 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
790 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
786 raise ValueError(b"deleting slices only supports a:-1 with step 1")
791 raise ValueError(b"deleting slices only supports a:-1 with step 1")
787 i = i.start
792 i = i.start
788 self._check_index(i)
793 self._check_index(i)
789 self._stripnodes(i)
794 self._stripnodes(i)
790 if i < self._lgt:
795 if i < self._lgt:
791 self._offsets = self._offsets[:i]
796 self._offsets = self._offsets[:i]
792 self._lgt = i
797 self._lgt = i
793 self._extra = []
798 self._extra = []
794 else:
799 else:
795 self._extra = self._extra[: i - self._lgt]
800 self._extra = self._extra[: i - self._lgt]
796
801
797 def _calculate_index(self, i):
802 def _calculate_index(self, i):
798 return self._offsets[i]
803 return self._offsets[i]
799
804
800
805
801 def parse_index2(data, inline, format=revlog_constants.REVLOGV1):
806 def parse_index2(data, inline, format=revlog_constants.REVLOGV1):
802 if format == revlog_constants.CHANGELOGV2:
807 if format == revlog_constants.CHANGELOGV2:
803 return parse_index_cl_v2(data)
808 return parse_index_cl_v2(data)
804 if not inline:
809 if not inline:
805 if format == revlog_constants.REVLOGV2:
810 if format == revlog_constants.REVLOGV2:
806 cls = IndexObject2
811 cls = IndexObject2
807 else:
812 else:
808 cls = IndexObject
813 cls = IndexObject
809 return cls(data), None
814 return cls(data), None
810 cls = InlinedIndexObject
815 cls = InlinedIndexObject
811 return cls(data, inline), (0, data)
816 return cls(data, inline), (0, data)
812
817
813
818
814 def parse_index_cl_v2(data):
819 def parse_index_cl_v2(data):
815 return IndexChangelogV2(data), None
820 return IndexChangelogV2(data), None
816
821
817
822
818 class IndexObject2(IndexObject):
823 class IndexObject2(IndexObject):
819 index_format = revlog_constants.INDEX_ENTRY_V2
824 index_format = revlog_constants.INDEX_ENTRY_V2
820
825
821 def replace_sidedata_info(
826 def replace_sidedata_info(
822 self,
827 self,
823 rev,
828 rev,
824 sidedata_offset,
829 sidedata_offset,
825 sidedata_length,
830 sidedata_length,
826 offset_flags,
831 offset_flags,
827 compression_mode,
832 compression_mode,
828 ):
833 ):
829 """
834 """
830 Replace an existing index entry's sidedata offset and length with new
835 Replace an existing index entry's sidedata offset and length with new
831 ones.
836 ones.
832 This cannot be used outside of the context of sidedata rewriting,
837 This cannot be used outside of the context of sidedata rewriting,
833 inside the transaction that creates the revision `rev`.
838 inside the transaction that creates the revision `rev`.
834 """
839 """
835 if rev < 0:
840 if rev < 0:
836 raise KeyError
841 raise KeyError
837 self._check_index(rev)
842 self._check_index(rev)
838 if rev < self._lgt:
843 if rev < self._lgt:
839 msg = b"cannot rewrite entries outside of this transaction"
844 msg = b"cannot rewrite entries outside of this transaction"
840 raise KeyError(msg)
845 raise KeyError(msg)
841 else:
846 else:
842 entry = list(self[rev])
847 entry = list(self[rev])
843 entry[0] = offset_flags
848 entry[0] = offset_flags
844 entry[8] = sidedata_offset
849 entry[8] = sidedata_offset
845 entry[9] = sidedata_length
850 entry[9] = sidedata_length
846 entry[11] = compression_mode
851 entry[11] = compression_mode
847 entry = tuple(entry)
852 entry = tuple(entry)
848 new = self._pack_entry(rev, entry)
853 new = self._pack_entry(rev, entry)
849 self._extra[rev - self._lgt] = new
854 self._extra[rev - self._lgt] = new
850
855
851 def _unpack_entry(self, rev, data):
856 def _unpack_entry(self, rev, data):
852 data = self.index_format.unpack(data)
857 data = self.index_format.unpack(data)
853 entry = data[:10]
858 entry = data[:10]
854 data_comp = data[10] & 3
859 data_comp = data[10] & 3
855 sidedata_comp = (data[10] & (3 << 2)) >> 2
860 sidedata_comp = (data[10] & (3 << 2)) >> 2
856 return entry + (data_comp, sidedata_comp, revlog_constants.RANK_UNKNOWN)
861 return entry + (data_comp, sidedata_comp, revlog_constants.RANK_UNKNOWN)
857
862
858 def _pack_entry(self, rev, entry):
863 def _pack_entry(self, rev, entry):
859 data = entry[:10]
864 data = entry[:10]
860 data_comp = entry[10] & 3
865 data_comp = entry[10] & 3
861 sidedata_comp = (entry[11] & 3) << 2
866 sidedata_comp = (entry[11] & 3) << 2
862 data += (data_comp | sidedata_comp,)
867 data += (data_comp | sidedata_comp,)
863
868
864 return self.index_format.pack(*data)
869 return self.index_format.pack(*data)
865
870
866 def entry_binary(self, rev):
871 def entry_binary(self, rev):
867 """return the raw binary string representing a revision"""
872 """return the raw binary string representing a revision"""
868 entry = self[rev]
873 entry = self[rev]
869 return self._pack_entry(rev, entry)
874 return self._pack_entry(rev, entry)
870
875
871 def pack_header(self, header):
876 def pack_header(self, header):
872 """pack header information as binary"""
877 """pack header information as binary"""
873 msg = 'version header should go in the docket, not the index: %d'
878 msg = 'version header should go in the docket, not the index: %d'
874 msg %= header
879 msg %= header
875 raise error.ProgrammingError(msg)
880 raise error.ProgrammingError(msg)
876
881
877
882
878 class IndexChangelogV2(IndexObject2):
883 class IndexChangelogV2(IndexObject2):
879 index_format = revlog_constants.INDEX_ENTRY_CL_V2
884 index_format = revlog_constants.INDEX_ENTRY_CL_V2
880
885
881 null_item = (
886 null_item = (
882 IndexObject2.null_item[: revlog_constants.ENTRY_RANK]
887 IndexObject2.null_item[: revlog_constants.ENTRY_RANK]
883 + (0,) # rank of null is 0
888 + (0,) # rank of null is 0
884 + IndexObject2.null_item[revlog_constants.ENTRY_RANK :]
889 + IndexObject2.null_item[revlog_constants.ENTRY_RANK :]
885 )
890 )
886
891
887 def _unpack_entry(self, rev, data, r=True):
892 def _unpack_entry(self, rev, data, r=True):
888 items = self.index_format.unpack(data)
893 items = self.index_format.unpack(data)
889 return (
894 return (
890 items[revlog_constants.INDEX_ENTRY_V2_IDX_OFFSET],
895 items[revlog_constants.INDEX_ENTRY_V2_IDX_OFFSET],
891 items[revlog_constants.INDEX_ENTRY_V2_IDX_COMPRESSED_LENGTH],
896 items[revlog_constants.INDEX_ENTRY_V2_IDX_COMPRESSED_LENGTH],
892 items[revlog_constants.INDEX_ENTRY_V2_IDX_UNCOMPRESSED_LENGTH],
897 items[revlog_constants.INDEX_ENTRY_V2_IDX_UNCOMPRESSED_LENGTH],
893 rev,
898 rev,
894 rev,
899 rev,
895 items[revlog_constants.INDEX_ENTRY_V2_IDX_PARENT_1],
900 items[revlog_constants.INDEX_ENTRY_V2_IDX_PARENT_1],
896 items[revlog_constants.INDEX_ENTRY_V2_IDX_PARENT_2],
901 items[revlog_constants.INDEX_ENTRY_V2_IDX_PARENT_2],
897 items[revlog_constants.INDEX_ENTRY_V2_IDX_NODEID],
902 items[revlog_constants.INDEX_ENTRY_V2_IDX_NODEID],
898 items[revlog_constants.INDEX_ENTRY_V2_IDX_SIDEDATA_OFFSET],
903 items[revlog_constants.INDEX_ENTRY_V2_IDX_SIDEDATA_OFFSET],
899 items[
904 items[
900 revlog_constants.INDEX_ENTRY_V2_IDX_SIDEDATA_COMPRESSED_LENGTH
905 revlog_constants.INDEX_ENTRY_V2_IDX_SIDEDATA_COMPRESSED_LENGTH
901 ],
906 ],
902 items[revlog_constants.INDEX_ENTRY_V2_IDX_COMPRESSION_MODE] & 3,
907 items[revlog_constants.INDEX_ENTRY_V2_IDX_COMPRESSION_MODE] & 3,
903 (items[revlog_constants.INDEX_ENTRY_V2_IDX_COMPRESSION_MODE] >> 2)
908 (items[revlog_constants.INDEX_ENTRY_V2_IDX_COMPRESSION_MODE] >> 2)
904 & 3,
909 & 3,
905 items[revlog_constants.INDEX_ENTRY_V2_IDX_RANK],
910 items[revlog_constants.INDEX_ENTRY_V2_IDX_RANK],
906 )
911 )
907
912
908 def _pack_entry(self, rev, entry):
913 def _pack_entry(self, rev, entry):
909
914
910 base = entry[revlog_constants.ENTRY_DELTA_BASE]
915 base = entry[revlog_constants.ENTRY_DELTA_BASE]
911 link_rev = entry[revlog_constants.ENTRY_LINK_REV]
916 link_rev = entry[revlog_constants.ENTRY_LINK_REV]
912 assert base == rev, (base, rev)
917 assert base == rev, (base, rev)
913 assert link_rev == rev, (link_rev, rev)
918 assert link_rev == rev, (link_rev, rev)
914 data = (
919 data = (
915 entry[revlog_constants.ENTRY_DATA_OFFSET],
920 entry[revlog_constants.ENTRY_DATA_OFFSET],
916 entry[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH],
921 entry[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH],
917 entry[revlog_constants.ENTRY_DATA_UNCOMPRESSED_LENGTH],
922 entry[revlog_constants.ENTRY_DATA_UNCOMPRESSED_LENGTH],
918 entry[revlog_constants.ENTRY_PARENT_1],
923 entry[revlog_constants.ENTRY_PARENT_1],
919 entry[revlog_constants.ENTRY_PARENT_2],
924 entry[revlog_constants.ENTRY_PARENT_2],
920 entry[revlog_constants.ENTRY_NODE_ID],
925 entry[revlog_constants.ENTRY_NODE_ID],
921 entry[revlog_constants.ENTRY_SIDEDATA_OFFSET],
926 entry[revlog_constants.ENTRY_SIDEDATA_OFFSET],
922 entry[revlog_constants.ENTRY_SIDEDATA_COMPRESSED_LENGTH],
927 entry[revlog_constants.ENTRY_SIDEDATA_COMPRESSED_LENGTH],
923 entry[revlog_constants.ENTRY_DATA_COMPRESSION_MODE] & 3
928 entry[revlog_constants.ENTRY_DATA_COMPRESSION_MODE] & 3
924 | (entry[revlog_constants.ENTRY_SIDEDATA_COMPRESSION_MODE] & 3)
929 | (entry[revlog_constants.ENTRY_SIDEDATA_COMPRESSION_MODE] & 3)
925 << 2,
930 << 2,
926 entry[revlog_constants.ENTRY_RANK],
931 entry[revlog_constants.ENTRY_RANK],
927 )
932 )
928 return self.index_format.pack(*data)
933 return self.index_format.pack(*data)
929
934
930
935
931 def parse_index_devel_nodemap(data, inline):
936 def parse_index_devel_nodemap(data, inline):
932 """like parse_index2, but alway return a PersistentNodeMapIndexObject"""
937 """like parse_index2, but alway return a PersistentNodeMapIndexObject"""
933 return PersistentNodeMapIndexObject(data), None
938 return PersistentNodeMapIndexObject(data), None
934
939
935
940
936 def parse_dirstate(dmap, copymap, st):
941 def parse_dirstate(dmap, copymap, st):
937 parents = [st[:20], st[20:40]]
942 parents = [st[:20], st[20:40]]
938 # dereference fields so they will be local in loop
943 # dereference fields so they will be local in loop
939 format = b">cllll"
944 format = b">cllll"
940 e_size = struct.calcsize(format)
945 e_size = struct.calcsize(format)
941 pos1 = 40
946 pos1 = 40
942 l = len(st)
947 l = len(st)
943
948
944 # the inner loop
949 # the inner loop
945 while pos1 < l:
950 while pos1 < l:
946 pos2 = pos1 + e_size
951 pos2 = pos1 + e_size
947 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
952 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
948 pos1 = pos2 + e[4]
953 pos1 = pos2 + e[4]
949 f = st[pos2:pos1]
954 f = st[pos2:pos1]
950 if b'\0' in f:
955 if b'\0' in f:
951 f, c = f.split(b'\0')
956 f, c = f.split(b'\0')
952 copymap[f] = c
957 copymap[f] = c
953 dmap[f] = DirstateItem.from_v1_data(*e[:4])
958 dmap[f] = DirstateItem.from_v1_data(*e[:4])
954 return parents
959 return parents
955
960
956
961
957 def pack_dirstate(dmap, copymap, pl):
962 def pack_dirstate(dmap, copymap, pl):
958 cs = stringio()
963 cs = stringio()
959 write = cs.write
964 write = cs.write
960 write(b"".join(pl))
965 write(b"".join(pl))
961 for f, e in dmap.items():
966 for f, e in dmap.items():
962 if f in copymap:
967 if f in copymap:
963 f = b"%s\0%s" % (f, copymap[f])
968 f = b"%s\0%s" % (f, copymap[f])
964 e = _pack(
969 e = _pack(
965 b">cllll",
970 b">cllll",
966 e._v1_state(),
971 e._v1_state(),
967 e._v1_mode(),
972 e._v1_mode(),
968 e._v1_size(),
973 e._v1_size(),
969 e._v1_mtime(),
974 e._v1_mtime(),
970 len(f),
975 len(f),
971 )
976 )
972 write(e)
977 write(e)
973 write(f)
978 write(f)
974 return cs.getvalue()
979 return cs.getvalue()
@@ -1,247 +1,251 b''
1 use cpython::exc;
1 use cpython::exc;
2 use cpython::ObjectProtocol;
2 use cpython::ObjectProtocol;
3 use cpython::PyBytes;
3 use cpython::PyBytes;
4 use cpython::PyErr;
4 use cpython::PyErr;
5 use cpython::PyNone;
5 use cpython::PyNone;
6 use cpython::PyObject;
6 use cpython::PyObject;
7 use cpython::PyResult;
7 use cpython::PyResult;
8 use cpython::Python;
8 use cpython::Python;
9 use cpython::PythonObject;
9 use cpython::PythonObject;
10 use hg::dirstate::DirstateEntry;
10 use hg::dirstate::DirstateEntry;
11 use hg::dirstate::DirstateV2Data;
11 use hg::dirstate::DirstateV2Data;
12 use hg::dirstate::TruncatedTimestamp;
12 use hg::dirstate::TruncatedTimestamp;
13 use std::cell::Cell;
13 use std::cell::Cell;
14
14
15 py_class!(pub class DirstateItem |py| {
15 py_class!(pub class DirstateItem |py| {
16 data entry: Cell<DirstateEntry>;
16 data entry: Cell<DirstateEntry>;
17
17
18 def __new__(
18 def __new__(
19 _cls,
19 _cls,
20 wc_tracked: bool = false,
20 wc_tracked: bool = false,
21 p1_tracked: bool = false,
21 p1_tracked: bool = false,
22 p2_info: bool = false,
22 p2_info: bool = false,
23 has_meaningful_data: bool = true,
23 has_meaningful_data: bool = true,
24 has_meaningful_mtime: bool = true,
24 has_meaningful_mtime: bool = true,
25 parentfiledata: Option<(u32, u32, Option<(u32, u32, bool)>)> = None,
25 parentfiledata: Option<(u32, u32, Option<(u32, u32, bool)>)> = None,
26 fallback_exec: Option<bool> = None,
26 fallback_exec: Option<bool> = None,
27 fallback_symlink: Option<bool> = None,
27 fallback_symlink: Option<bool> = None,
28
28
29 ) -> PyResult<DirstateItem> {
29 ) -> PyResult<DirstateItem> {
30 let mut mode_size_opt = None;
30 let mut mode_size_opt = None;
31 let mut mtime_opt = None;
31 let mut mtime_opt = None;
32 if let Some((mode, size, mtime)) = parentfiledata {
32 if let Some((mode, size, mtime)) = parentfiledata {
33 if has_meaningful_data {
33 if has_meaningful_data {
34 mode_size_opt = Some((mode, size))
34 mode_size_opt = Some((mode, size))
35 }
35 }
36 if has_meaningful_mtime {
36 if has_meaningful_mtime {
37 if let Some(m) = mtime {
37 if let Some(m) = mtime {
38 mtime_opt = Some(timestamp(py, m)?);
38 mtime_opt = Some(timestamp(py, m)?);
39 }
39 }
40 }
40 }
41 }
41 }
42 let entry = DirstateEntry::from_v2_data(DirstateV2Data {
42 let entry = DirstateEntry::from_v2_data(DirstateV2Data {
43 wc_tracked: wc_tracked,
43 wc_tracked: wc_tracked,
44 p1_tracked,
44 p1_tracked,
45 p2_info,
45 p2_info,
46 mode_size: mode_size_opt,
46 mode_size: mode_size_opt,
47 mtime: mtime_opt,
47 mtime: mtime_opt,
48 fallback_exec,
48 fallback_exec,
49 fallback_symlink,
49 fallback_symlink,
50 });
50 });
51 DirstateItem::create_instance(py, Cell::new(entry))
51 DirstateItem::create_instance(py, Cell::new(entry))
52 }
52 }
53
53
54 @property
54 @property
55 def state(&self) -> PyResult<PyBytes> {
55 def state(&self) -> PyResult<PyBytes> {
56 let state_byte: u8 = self.entry(py).get().state().into();
56 let state_byte: u8 = self.entry(py).get().state().into();
57 Ok(PyBytes::new(py, &[state_byte]))
57 Ok(PyBytes::new(py, &[state_byte]))
58 }
58 }
59
59
60 @property
60 @property
61 def mode(&self) -> PyResult<i32> {
61 def mode(&self) -> PyResult<i32> {
62 Ok(self.entry(py).get().mode())
62 Ok(self.entry(py).get().mode())
63 }
63 }
64
64
65 @property
65 @property
66 def size(&self) -> PyResult<i32> {
66 def size(&self) -> PyResult<i32> {
67 Ok(self.entry(py).get().size())
67 Ok(self.entry(py).get().size())
68 }
68 }
69
69
70 @property
70 @property
71 def mtime(&self) -> PyResult<i32> {
71 def mtime(&self) -> PyResult<i32> {
72 Ok(self.entry(py).get().mtime())
72 Ok(self.entry(py).get().mtime())
73 }
73 }
74
74
75 @property
75 @property
76 def has_fallback_exec(&self) -> PyResult<bool> {
76 def has_fallback_exec(&self) -> PyResult<bool> {
77 match self.entry(py).get().get_fallback_exec() {
77 match self.entry(py).get().get_fallback_exec() {
78 Some(_) => Ok(true),
78 Some(_) => Ok(true),
79 None => Ok(false),
79 None => Ok(false),
80 }
80 }
81 }
81 }
82
82
83 @property
83 @property
84 def fallback_exec(&self) -> PyResult<Option<bool>> {
84 def fallback_exec(&self) -> PyResult<Option<bool>> {
85 match self.entry(py).get().get_fallback_exec() {
85 match self.entry(py).get().get_fallback_exec() {
86 Some(exec) => Ok(Some(exec)),
86 Some(exec) => Ok(Some(exec)),
87 None => Ok(None),
87 None => Ok(None),
88 }
88 }
89 }
89 }
90
90
91 @fallback_exec.setter
91 @fallback_exec.setter
92 def set_fallback_exec(&self, value: Option<PyObject>) -> PyResult<()> {
92 def set_fallback_exec(&self, value: Option<PyObject>) -> PyResult<()> {
93 match value {
93 match value {
94 None => {self.entry(py).get().set_fallback_exec(None);},
94 None => {self.entry(py).get().set_fallback_exec(None);},
95 Some(value) => {
95 Some(value) => {
96 if value.is_none(py) {
96 if value.is_none(py) {
97 self.entry(py).get().set_fallback_exec(None);
97 self.entry(py).get().set_fallback_exec(None);
98 } else {
98 } else {
99 self.entry(py).get().set_fallback_exec(
99 self.entry(py).get().set_fallback_exec(
100 Some(value.is_true(py)?)
100 Some(value.is_true(py)?)
101 );
101 );
102 }},
102 }},
103 }
103 }
104 Ok(())
104 Ok(())
105 }
105 }
106
106
107 @property
107 @property
108 def has_fallback_symlink(&self) -> PyResult<bool> {
108 def has_fallback_symlink(&self) -> PyResult<bool> {
109 match self.entry(py).get().get_fallback_symlink() {
109 match self.entry(py).get().get_fallback_symlink() {
110 Some(_) => Ok(true),
110 Some(_) => Ok(true),
111 None => Ok(false),
111 None => Ok(false),
112 }
112 }
113 }
113 }
114
114
115 @property
115 @property
116 def fallback_symlink(&self) -> PyResult<Option<bool>> {
116 def fallback_symlink(&self) -> PyResult<Option<bool>> {
117 match self.entry(py).get().get_fallback_symlink() {
117 match self.entry(py).get().get_fallback_symlink() {
118 Some(symlink) => Ok(Some(symlink)),
118 Some(symlink) => Ok(Some(symlink)),
119 None => Ok(None),
119 None => Ok(None),
120 }
120 }
121 }
121 }
122
122
123 @fallback_symlink.setter
123 @fallback_symlink.setter
124 def set_fallback_symlink(&self, value: Option<PyObject>) -> PyResult<()> {
124 def set_fallback_symlink(&self, value: Option<PyObject>) -> PyResult<()> {
125 match value {
125 match value {
126 None => {self.entry(py).get().set_fallback_symlink(None);},
126 None => {self.entry(py).get().set_fallback_symlink(None);},
127 Some(value) => {
127 Some(value) => {
128 if value.is_none(py) {
128 if value.is_none(py) {
129 self.entry(py).get().set_fallback_symlink(None);
129 self.entry(py).get().set_fallback_symlink(None);
130 } else {
130 } else {
131 self.entry(py).get().set_fallback_symlink(
131 self.entry(py).get().set_fallback_symlink(
132 Some(value.is_true(py)?)
132 Some(value.is_true(py)?)
133 );
133 );
134 }},
134 }},
135 }
135 }
136 Ok(())
136 Ok(())
137 }
137 }
138
138
139 @property
139 @property
140 def tracked(&self) -> PyResult<bool> {
140 def tracked(&self) -> PyResult<bool> {
141 Ok(self.entry(py).get().tracked())
141 Ok(self.entry(py).get().tracked())
142 }
142 }
143
143
144 @property
144 @property
145 def p1_tracked(&self) -> PyResult<bool> {
145 def p1_tracked(&self) -> PyResult<bool> {
146 Ok(self.entry(py).get().p1_tracked())
146 Ok(self.entry(py).get().p1_tracked())
147 }
147 }
148
148
149 @property
149 @property
150 def added(&self) -> PyResult<bool> {
150 def added(&self) -> PyResult<bool> {
151 Ok(self.entry(py).get().added())
151 Ok(self.entry(py).get().added())
152 }
152 }
153
153
154 @property
155 def modified(&self) -> PyResult<bool> {
156 Ok(self.entry(py).get().modified())
157 }
154
158
155 @property
159 @property
156 def p2_info(&self) -> PyResult<bool> {
160 def p2_info(&self) -> PyResult<bool> {
157 Ok(self.entry(py).get().p2_info())
161 Ok(self.entry(py).get().p2_info())
158 }
162 }
159
163
160 @property
164 @property
161 def removed(&self) -> PyResult<bool> {
165 def removed(&self) -> PyResult<bool> {
162 Ok(self.entry(py).get().removed())
166 Ok(self.entry(py).get().removed())
163 }
167 }
164
168
165 @property
169 @property
166 def maybe_clean(&self) -> PyResult<bool> {
170 def maybe_clean(&self) -> PyResult<bool> {
167 Ok(self.entry(py).get().maybe_clean())
171 Ok(self.entry(py).get().maybe_clean())
168 }
172 }
169
173
170 @property
174 @property
171 def any_tracked(&self) -> PyResult<bool> {
175 def any_tracked(&self) -> PyResult<bool> {
172 Ok(self.entry(py).get().any_tracked())
176 Ok(self.entry(py).get().any_tracked())
173 }
177 }
174
178
175 def mtime_likely_equal_to(&self, other: (u32, u32, bool))
179 def mtime_likely_equal_to(&self, other: (u32, u32, bool))
176 -> PyResult<bool> {
180 -> PyResult<bool> {
177 if let Some(mtime) = self.entry(py).get().truncated_mtime() {
181 if let Some(mtime) = self.entry(py).get().truncated_mtime() {
178 Ok(mtime.likely_equal(timestamp(py, other)?))
182 Ok(mtime.likely_equal(timestamp(py, other)?))
179 } else {
183 } else {
180 Ok(false)
184 Ok(false)
181 }
185 }
182 }
186 }
183
187
184 def drop_merge_data(&self) -> PyResult<PyNone> {
188 def drop_merge_data(&self) -> PyResult<PyNone> {
185 self.update(py, |entry| entry.drop_merge_data());
189 self.update(py, |entry| entry.drop_merge_data());
186 Ok(PyNone)
190 Ok(PyNone)
187 }
191 }
188
192
189 def set_clean(
193 def set_clean(
190 &self,
194 &self,
191 mode: u32,
195 mode: u32,
192 size: u32,
196 size: u32,
193 mtime: (u32, u32, bool),
197 mtime: (u32, u32, bool),
194 ) -> PyResult<PyNone> {
198 ) -> PyResult<PyNone> {
195 let mtime = timestamp(py, mtime)?;
199 let mtime = timestamp(py, mtime)?;
196 self.update(py, |entry| entry.set_clean(mode, size, mtime));
200 self.update(py, |entry| entry.set_clean(mode, size, mtime));
197 Ok(PyNone)
201 Ok(PyNone)
198 }
202 }
199
203
200 def set_possibly_dirty(&self) -> PyResult<PyNone> {
204 def set_possibly_dirty(&self) -> PyResult<PyNone> {
201 self.update(py, |entry| entry.set_possibly_dirty());
205 self.update(py, |entry| entry.set_possibly_dirty());
202 Ok(PyNone)
206 Ok(PyNone)
203 }
207 }
204
208
205 def set_tracked(&self) -> PyResult<PyNone> {
209 def set_tracked(&self) -> PyResult<PyNone> {
206 self.update(py, |entry| entry.set_tracked());
210 self.update(py, |entry| entry.set_tracked());
207 Ok(PyNone)
211 Ok(PyNone)
208 }
212 }
209
213
210 def set_untracked(&self) -> PyResult<PyNone> {
214 def set_untracked(&self) -> PyResult<PyNone> {
211 self.update(py, |entry| entry.set_untracked());
215 self.update(py, |entry| entry.set_untracked());
212 Ok(PyNone)
216 Ok(PyNone)
213 }
217 }
214 });
218 });
215
219
216 impl DirstateItem {
220 impl DirstateItem {
217 pub fn new_as_pyobject(
221 pub fn new_as_pyobject(
218 py: Python<'_>,
222 py: Python<'_>,
219 entry: DirstateEntry,
223 entry: DirstateEntry,
220 ) -> PyResult<PyObject> {
224 ) -> PyResult<PyObject> {
221 Ok(DirstateItem::create_instance(py, Cell::new(entry))?.into_object())
225 Ok(DirstateItem::create_instance(py, Cell::new(entry))?.into_object())
222 }
226 }
223
227
224 pub fn get_entry(&self, py: Python<'_>) -> DirstateEntry {
228 pub fn get_entry(&self, py: Python<'_>) -> DirstateEntry {
225 self.entry(py).get()
229 self.entry(py).get()
226 }
230 }
227
231
228 // TODO: Use https://doc.rust-lang.org/std/cell/struct.Cell.html#method.update instead when it’s stable
232 // TODO: Use https://doc.rust-lang.org/std/cell/struct.Cell.html#method.update instead when it’s stable
229 pub fn update(&self, py: Python<'_>, f: impl FnOnce(&mut DirstateEntry)) {
233 pub fn update(&self, py: Python<'_>, f: impl FnOnce(&mut DirstateEntry)) {
230 let mut entry = self.entry(py).get();
234 let mut entry = self.entry(py).get();
231 f(&mut entry);
235 f(&mut entry);
232 self.entry(py).set(entry)
236 self.entry(py).set(entry)
233 }
237 }
234 }
238 }
235
239
236 pub(crate) fn timestamp(
240 pub(crate) fn timestamp(
237 py: Python<'_>,
241 py: Python<'_>,
238 (s, ns, second_ambiguous): (u32, u32, bool),
242 (s, ns, second_ambiguous): (u32, u32, bool),
239 ) -> PyResult<TruncatedTimestamp> {
243 ) -> PyResult<TruncatedTimestamp> {
240 TruncatedTimestamp::from_already_truncated(s, ns, second_ambiguous)
244 TruncatedTimestamp::from_already_truncated(s, ns, second_ambiguous)
241 .map_err(|_| {
245 .map_err(|_| {
242 PyErr::new::<exc::ValueError, _>(
246 PyErr::new::<exc::ValueError, _>(
243 py,
247 py,
244 "expected mtime truncated to 31 bits",
248 "expected mtime truncated to 31 bits",
245 )
249 )
246 })
250 })
247 }
251 }
General Comments 0
You need to be logged in to leave comments. Login now