17 |
17 |
18 from __future__ import absolute_import |
18 from __future__ import absolute_import |
19 |
19 |
20 import struct |
20 import struct |
21 |
21 |
|
22 from .. import ( |
|
23 error, |
|
24 ) |
|
25 |
22 from . import ( |
26 from . import ( |
23 constants, |
27 constants, |
24 ) |
28 ) |
25 |
29 |
26 # Docket format |
30 # Docket format |
27 # |
31 # |
28 # * 4 bytes: revlog version |
32 # * 4 bytes: revlog version |
29 # | This is mandatory as docket must be compatible with the previous |
33 # | This is mandatory as docket must be compatible with the previous |
30 # | revlog index header. |
34 # | revlog index header. |
31 # * 8 bytes: size of index data |
35 # * 8 bytes: size of index data |
32 S_HEADER = struct.Struct(constants.INDEX_HEADER.format + 'L') |
36 # * 8 bytes: pending size of index data |
|
37 S_HEADER = struct.Struct(constants.INDEX_HEADER.format + 'LL') |
33 |
38 |
34 |
39 |
35 class RevlogDocket(object): |
40 class RevlogDocket(object): |
36 """metadata associated with revlog""" |
41 """metadata associated with revlog""" |
37 |
42 |
38 def __init__(self, revlog, version_header=None, index_end=0): |
43 def __init__( |
|
44 self, |
|
45 revlog, |
|
46 use_pending=False, |
|
47 version_header=None, |
|
48 index_end=0, |
|
49 pending_index_end=0, |
|
50 ): |
39 self._version_header = version_header |
51 self._version_header = version_header |
|
52 self._read_only = bool(use_pending) |
40 self._dirty = False |
53 self._dirty = False |
41 self._radix = revlog.radix |
54 self._radix = revlog.radix |
42 self._path = revlog._docket_file |
55 self._path = revlog._docket_file |
43 self._opener = revlog.opener |
56 self._opener = revlog.opener |
44 self._index_end = index_end |
57 # this assert should be True as long as we have a single index filename |
|
58 assert index_end <= pending_index_end |
|
59 self._initial_index_end = index_end |
|
60 self._pending_index_end = pending_index_end |
|
61 if use_pending: |
|
62 self._index_end = self._pending_index_end |
|
63 else: |
|
64 self._index_end = self._initial_index_end |
45 |
65 |
46 def index_filepath(self): |
66 def index_filepath(self): |
47 """file path to the current index file associated to this docket""" |
67 """file path to the current index file associated to this docket""" |
48 # very simplistic version at first |
68 # very simplistic version at first |
49 return b"%s.idx" % self._radix |
69 return b"%s.idx" % self._radix |
56 def index_end(self, new_size): |
76 def index_end(self, new_size): |
57 if new_size != self._index_end: |
77 if new_size != self._index_end: |
58 self._index_end = new_size |
78 self._index_end = new_size |
59 self._dirty = True |
79 self._dirty = True |
60 |
80 |
61 def write(self, transaction, stripping=False): |
81 def write(self, transaction, pending=False, stripping=False): |
62 """write the modification of disk if any |
82 """write the modification of disk if any |
63 |
83 |
64 This make the new content visible to all process""" |
84 This make the new content visible to all process""" |
65 if self._dirty: |
85 if not self._dirty: |
|
86 return False |
|
87 else: |
|
88 if self._read_only: |
|
89 msg = b'writing read-only docket: %s' |
|
90 msg %= self._path |
|
91 raise error.ProgrammingError(msg) |
66 if not stripping: |
92 if not stripping: |
67 # XXX we could, leverage the docket while stripping. However it |
93 # XXX we could, leverage the docket while stripping. However it |
68 # is not powerfull enough at the time of this comment |
94 # is not powerfull enough at the time of this comment |
69 transaction.addbackup(self._path, location=b'store') |
95 transaction.addbackup(self._path, location=b'store') |
70 with self._opener(self._path, mode=b'w', atomictemp=True) as f: |
96 with self._opener(self._path, mode=b'w', atomictemp=True) as f: |
71 f.write(self._serialize()) |
97 f.write(self._serialize(pending=pending)) |
72 self._dirty = False |
98 # if pending we still need to the write final data eventually |
|
99 self._dirty = pending |
|
100 return True |
73 |
101 |
74 def _serialize(self): |
102 def _serialize(self, pending=False): |
|
103 if pending: |
|
104 official_index_end = self._initial_index_end |
|
105 else: |
|
106 official_index_end = self._index_end |
|
107 |
|
108 # this assert should be True as long as we have a single index filename |
|
109 assert official_index_end <= self._index_end |
75 data = ( |
110 data = ( |
76 self._version_header, |
111 self._version_header, |
|
112 official_index_end, |
77 self._index_end, |
113 self._index_end, |
78 ) |
114 ) |
79 return S_HEADER.pack(*data) |
115 return S_HEADER.pack(*data) |
80 |
116 |
81 |
117 |
86 docket = RevlogDocket(revlog, version_header=version_header) |
122 docket = RevlogDocket(revlog, version_header=version_header) |
87 docket._dirty = True |
123 docket._dirty = True |
88 return docket |
124 return docket |
89 |
125 |
90 |
126 |
91 def parse_docket(revlog, data): |
127 def parse_docket(revlog, data, use_pending=False): |
92 """given some docket data return a docket object for the given revlog""" |
128 """given some docket data return a docket object for the given revlog""" |
93 header = S_HEADER.unpack(data[: S_HEADER.size]) |
129 header = S_HEADER.unpack(data[: S_HEADER.size]) |
94 version_header, index_size = header |
130 version_header, index_size, pending_index_size = header |
95 docket = RevlogDocket( |
131 docket = RevlogDocket( |
96 revlog, |
132 revlog, |
|
133 use_pending=use_pending, |
97 version_header=version_header, |
134 version_header=version_header, |
98 index_end=index_size, |
135 index_end=index_size, |
|
136 pending_index_end=pending_index_size, |
99 ) |
137 ) |
100 return docket |
138 return docket |