60 self._other = other |
60 self._other = other |
61 shutil.rmtree(self._repo.join("merge"), True) |
61 shutil.rmtree(self._repo.join("merge"), True) |
62 self._dirty = False |
62 self._dirty = False |
63 |
63 |
64 def _read(self): |
64 def _read(self): |
|
65 """Analyse each record content to restore a serialized state from disk |
|
66 |
|
67 This function process "record" entry produced by the de-serialization |
|
68 of on disk file. |
|
69 """ |
65 self._state = {} |
70 self._state = {} |
66 records = self._readrecords() |
71 records = self._readrecords() |
67 for rtype, record in records: |
72 for rtype, record in records: |
68 if rtype == 'L': |
73 if rtype == 'L': |
69 self._local = bin(record) |
74 self._local = bin(record) |
76 raise util.Abort(_('unsupported merge state record:' |
81 raise util.Abort(_('unsupported merge state record:' |
77 % rtype)) |
82 % rtype)) |
78 self._dirty = False |
83 self._dirty = False |
79 |
84 |
80 def _readrecords(self): |
85 def _readrecords(self): |
|
86 """Read merge state from disk and return a list of record (TYPE, data) |
|
87 |
|
88 We read data from both V1 and Ve files decide which on to use. |
|
89 |
|
90 V1 have been used by version prior to 2.9.1 and contains less data than |
|
91 v2. We read both version and check if no data in v2 contradict one in |
|
92 v1. If there is not contradiction we can safely assume that both v1 |
|
93 and v2 were written at the same time and use the extract data in v2. If |
|
94 there is contradiction we ignore v2 content as we assume an old version |
|
95 of Mercurial have over written the mergstate file and left an old v2 |
|
96 file around. |
|
97 |
|
98 returns list of record [(TYPE, data), ...]""" |
81 v1records = self._readrecordsv1() |
99 v1records = self._readrecordsv1() |
82 v2records = self._readrecordsv2() |
100 v2records = self._readrecordsv2() |
83 oldv2 = set() # old format version of v2 record |
101 oldv2 = set() # old format version of v2 record |
84 for rec in v2records: |
102 for rec in v2records: |
85 if rec[0] == 'L': |
103 if rec[0] == 'L': |
105 return v1records |
123 return v1records |
106 else: |
124 else: |
107 return v2records |
125 return v2records |
108 |
126 |
109 def _readrecordsv1(self): |
127 def _readrecordsv1(self): |
|
128 """read on disk merge state for version 1 file |
|
129 |
|
130 returns list of record [(TYPE, data), ...] |
|
131 |
|
132 Note: the "F" data from this file are one entry short |
|
133 (no "other file node" entry) |
|
134 """ |
110 records = [] |
135 records = [] |
111 try: |
136 try: |
112 f = self._repo.opener(self.statepathv1) |
137 f = self._repo.opener(self.statepathv1) |
113 for i, l in enumerate(f): |
138 for i, l in enumerate(f): |
114 if i == 0: |
139 if i == 0: |
141 if err.errno != errno.ENOENT: |
170 if err.errno != errno.ENOENT: |
142 raise |
171 raise |
143 return records |
172 return records |
144 |
173 |
145 def commit(self): |
174 def commit(self): |
|
175 """Write current state on disk (if necessary)""" |
146 if self._dirty: |
176 if self._dirty: |
147 records = [] |
177 records = [] |
148 records.append(("L", hex(self._local))) |
178 records.append(("L", hex(self._local))) |
149 records.append(("O", hex(self._other))) |
179 records.append(("O", hex(self._other))) |
150 for d, v in self._state.iteritems(): |
180 for d, v in self._state.iteritems(): |
151 records.append(("F", "\0".join([d] + v))) |
181 records.append(("F", "\0".join([d] + v))) |
152 self._writerecords(records) |
182 self._writerecords(records) |
153 self._dirty = False |
183 self._dirty = False |
154 |
184 |
155 def _writerecords(self, records): |
185 def _writerecords(self, records): |
|
186 """Write current state on disk (both v1 and v2)""" |
156 self._writerecordsv1(records) |
187 self._writerecordsv1(records) |
157 self._writerecordsv2(records) |
188 self._writerecordsv2(records) |
158 |
189 |
159 def _writerecordsv1(self, records): |
190 def _writerecordsv1(self, records): |
|
191 """Write current state on disk in a version 1 file""" |
160 f = self._repo.opener(self.statepathv1, "w") |
192 f = self._repo.opener(self.statepathv1, "w") |
161 irecords = iter(records) |
193 irecords = iter(records) |
162 lrecords = irecords.next() |
194 lrecords = irecords.next() |
163 assert lrecords[0] == 'L' |
195 assert lrecords[0] == 'L' |
164 f.write(hex(self._local) + "\n") |
196 f.write(hex(self._local) + "\n") |
166 if rtype == "F": |
198 if rtype == "F": |
167 f.write("%s\n" % _droponode(data)) |
199 f.write("%s\n" % _droponode(data)) |
168 f.close() |
200 f.close() |
169 |
201 |
170 def _writerecordsv2(self, records): |
202 def _writerecordsv2(self, records): |
|
203 """Write current state on disk in a version 2 file""" |
171 f = self._repo.opener(self.statepathv2, "w") |
204 f = self._repo.opener(self.statepathv2, "w") |
172 for key, data in records: |
205 for key, data in records: |
173 assert len(key) == 1 |
206 assert len(key) == 1 |
174 format = ">sI%is" % len(data) |
207 format = ">sI%is" % len(data) |
175 f.write(_pack(format, key, len(data), data)) |
208 f.write(_pack(format, key, len(data), data)) |
176 f.close() |
209 f.close() |
177 |
210 |
178 def add(self, fcl, fco, fca, fd): |
211 def add(self, fcl, fco, fca, fd): |
|
212 """add a new (potentially?) conflicting file the merge state |
|
213 fcl: file context for local, |
|
214 fco: file context for remote, |
|
215 fca: file context for ancestors, |
|
216 fd: file path of the resulting merge. |
|
217 |
|
218 note: also write the local version to the `.hg/merge` directory. |
|
219 """ |
179 hash = util.sha1(fcl.path()).hexdigest() |
220 hash = util.sha1(fcl.path()).hexdigest() |
180 self._repo.opener.write("merge/" + hash, fcl.data()) |
221 self._repo.opener.write("merge/" + hash, fcl.data()) |
181 self._state[fd] = ['u', hash, fcl.path(), |
222 self._state[fd] = ['u', hash, fcl.path(), |
182 fca.path(), hex(fca.filenode()), |
223 fca.path(), hex(fca.filenode()), |
183 fco.path(), hex(fco.filenode()), |
224 fco.path(), hex(fco.filenode()), |
202 def mark(self, dfile, state): |
243 def mark(self, dfile, state): |
203 self._state[dfile][0] = state |
244 self._state[dfile][0] = state |
204 self._dirty = True |
245 self._dirty = True |
205 |
246 |
206 def resolve(self, dfile, wctx): |
247 def resolve(self, dfile, wctx): |
|
248 """rerun merge process for file path `dfile`""" |
207 if self[dfile] == 'r': |
249 if self[dfile] == 'r': |
208 return 0 |
250 return 0 |
209 stateentry = self._state[dfile] |
251 stateentry = self._state[dfile] |
210 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry |
252 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry |
211 octx = self._repo[self._other] |
253 octx = self._repo[self._other] |