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