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