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