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