##// END OF EJS Templates
mergestate: move _read() call to read constructor...
Siddharth Agarwal -
r27005:3185c01c default
parent child Browse files
Show More
@@ -1,1366 +1,1369 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
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 errno
10 import errno
11 import os
11 import os
12 import shutil
12 import shutil
13 import struct
13 import struct
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 bin,
17 bin,
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 )
21 )
22 from . import (
22 from . import (
23 copies,
23 copies,
24 destutil,
24 destutil,
25 error,
25 error,
26 filemerge,
26 filemerge,
27 obsolete,
27 obsolete,
28 subrepo,
28 subrepo,
29 util,
29 util,
30 worker,
30 worker,
31 )
31 )
32
32
33 _pack = struct.pack
33 _pack = struct.pack
34 _unpack = struct.unpack
34 _unpack = struct.unpack
35
35
36 def _droponode(data):
36 def _droponode(data):
37 # used for compatibility for v1
37 # used for compatibility for v1
38 bits = data.split('\0')
38 bits = data.split('\0')
39 bits = bits[:-2] + bits[-1:]
39 bits = bits[:-2] + bits[-1:]
40 return '\0'.join(bits)
40 return '\0'.join(bits)
41
41
42 class mergestate(object):
42 class mergestate(object):
43 '''track 3-way merge state of individual files
43 '''track 3-way merge state of individual files
44
44
45 it is stored on disk when needed. Two file are used, one with an old
45 it is stored on disk when needed. Two file are used, one with an old
46 format, one with a new format. Both contains similar data, but the new
46 format, one with a new format. Both contains similar data, but the new
47 format can store new kinds of field.
47 format can store new kinds of field.
48
48
49 Current new format is a list of arbitrary record of the form:
49 Current new format is a list of arbitrary record of the form:
50
50
51 [type][length][content]
51 [type][length][content]
52
52
53 Type is a single character, length is a 4 bytes integer, content is an
53 Type is a single character, length is a 4 bytes integer, content is an
54 arbitrary suites of bytes of length `length`.
54 arbitrary suites of bytes of length `length`.
55
55
56 Type should be a letter. Capital letter are mandatory record, Mercurial
56 Type should be a letter. Capital letter are mandatory record, Mercurial
57 should abort if they are unknown. lower case record can be safely ignored.
57 should abort if they are unknown. lower case record can be safely ignored.
58
58
59 Currently known record:
59 Currently known record:
60
60
61 L: the node of the "local" part of the merge (hexified version)
61 L: the node of the "local" part of the merge (hexified version)
62 O: the node of the "other" part of the merge (hexified version)
62 O: the node of the "other" part of the merge (hexified version)
63 F: a file to be merged entry
63 F: a file to be merged entry
64 D: a file that the external merge driver will merge internally
64 D: a file that the external merge driver will merge internally
65 (experimental)
65 (experimental)
66 m: the external merge driver defined for this merge plus its run state
66 m: the external merge driver defined for this merge plus its run state
67 (experimental)
67 (experimental)
68
68
69 Merge driver run states (experimental):
69 Merge driver run states (experimental):
70 u: driver-resolved files unmarked -- needs to be run next time we're about
70 u: driver-resolved files unmarked -- needs to be run next time we're about
71 to resolve or commit
71 to resolve or commit
72 m: driver-resolved files marked -- only needs to be run before commit
72 m: driver-resolved files marked -- only needs to be run before commit
73 s: success/skipped -- does not need to be run any more
73 s: success/skipped -- does not need to be run any more
74 '''
74 '''
75 statepathv1 = 'merge/state'
75 statepathv1 = 'merge/state'
76 statepathv2 = 'merge/state2'
76 statepathv2 = 'merge/state2'
77
77
78 @staticmethod
78 @staticmethod
79 def clean(repo, node=None, other=None):
79 def clean(repo, node=None, other=None):
80 """Initialize a brand new merge state, removing any existing state on
80 """Initialize a brand new merge state, removing any existing state on
81 disk."""
81 disk."""
82 ms = mergestate(repo)
82 ms = mergestate(repo)
83 ms.reset(node, other)
83 ms.reset(node, other)
84 return ms
84 return ms
85
85
86 @staticmethod
86 @staticmethod
87 def read(repo):
87 def read(repo):
88 """Initialize the merge state, reading it from disk."""
88 """Initialize the merge state, reading it from disk."""
89 ms = mergestate(repo)
89 ms = mergestate(repo)
90 ms._read()
90 return ms
91 return ms
91
92
92 def __init__(self, repo):
93 def __init__(self, repo):
94 """Initialize the merge state.
95
96 Do not use this directly! Instead call read() or clean()."""
93 self._repo = repo
97 self._repo = repo
94 self._dirty = False
98 self._dirty = False
95 self._read()
96
99
97 def reset(self, node=None, other=None):
100 def reset(self, node=None, other=None):
98 self._state = {}
101 self._state = {}
99 self._local = None
102 self._local = None
100 self._other = None
103 self._other = None
101 if 'otherctx' in vars(self):
104 if 'otherctx' in vars(self):
102 del self.otherctx
105 del self.otherctx
103 if node:
106 if node:
104 self._local = node
107 self._local = node
105 self._other = other
108 self._other = other
106 self._readmergedriver = None
109 self._readmergedriver = None
107 if self.mergedriver:
110 if self.mergedriver:
108 self._mdstate = 's'
111 self._mdstate = 's'
109 else:
112 else:
110 self._mdstate = 'u'
113 self._mdstate = 'u'
111 shutil.rmtree(self._repo.join('merge'), True)
114 shutil.rmtree(self._repo.join('merge'), True)
112 self._dirty = False
115 self._dirty = False
113
116
114 def _read(self):
117 def _read(self):
115 """Analyse each record content to restore a serialized state from disk
118 """Analyse each record content to restore a serialized state from disk
116
119
117 This function process "record" entry produced by the de-serialization
120 This function process "record" entry produced by the de-serialization
118 of on disk file.
121 of on disk file.
119 """
122 """
120 self._state = {}
123 self._state = {}
121 self._local = None
124 self._local = None
122 self._other = None
125 self._other = None
123 if 'otherctx' in vars(self):
126 if 'otherctx' in vars(self):
124 del self.otherctx
127 del self.otherctx
125 self._readmergedriver = None
128 self._readmergedriver = None
126 self._mdstate = 's'
129 self._mdstate = 's'
127 unsupported = set()
130 unsupported = set()
128 records = self._readrecords()
131 records = self._readrecords()
129 for rtype, record in records:
132 for rtype, record in records:
130 if rtype == 'L':
133 if rtype == 'L':
131 self._local = bin(record)
134 self._local = bin(record)
132 elif rtype == 'O':
135 elif rtype == 'O':
133 self._other = bin(record)
136 self._other = bin(record)
134 elif rtype == 'm':
137 elif rtype == 'm':
135 bits = record.split('\0', 1)
138 bits = record.split('\0', 1)
136 mdstate = bits[1]
139 mdstate = bits[1]
137 if len(mdstate) != 1 or mdstate not in 'ums':
140 if len(mdstate) != 1 or mdstate not in 'ums':
138 # the merge driver should be idempotent, so just rerun it
141 # the merge driver should be idempotent, so just rerun it
139 mdstate = 'u'
142 mdstate = 'u'
140
143
141 self._readmergedriver = bits[0]
144 self._readmergedriver = bits[0]
142 self._mdstate = mdstate
145 self._mdstate = mdstate
143 elif rtype in 'FD':
146 elif rtype in 'FD':
144 bits = record.split('\0')
147 bits = record.split('\0')
145 self._state[bits[0]] = bits[1:]
148 self._state[bits[0]] = bits[1:]
146 elif not rtype.islower():
149 elif not rtype.islower():
147 unsupported.add(rtype)
150 unsupported.add(rtype)
148 self._dirty = False
151 self._dirty = False
149
152
150 if unsupported:
153 if unsupported:
151 raise error.UnsupportedMergeRecords(unsupported)
154 raise error.UnsupportedMergeRecords(unsupported)
152
155
153 def _readrecords(self):
156 def _readrecords(self):
154 """Read merge state from disk and return a list of record (TYPE, data)
157 """Read merge state from disk and return a list of record (TYPE, data)
155
158
156 We read data from both v1 and v2 files and decide which one to use.
159 We read data from both v1 and v2 files and decide which one to use.
157
160
158 V1 has been used by version prior to 2.9.1 and contains less data than
161 V1 has been used by version prior to 2.9.1 and contains less data than
159 v2. We read both versions and check if no data in v2 contradicts
162 v2. We read both versions and check if no data in v2 contradicts
160 v1. If there is not contradiction we can safely assume that both v1
163 v1. If there is not contradiction we can safely assume that both v1
161 and v2 were written at the same time and use the extract data in v2. If
164 and v2 were written at the same time and use the extract data in v2. If
162 there is contradiction we ignore v2 content as we assume an old version
165 there is contradiction we ignore v2 content as we assume an old version
163 of Mercurial has overwritten the mergestate file and left an old v2
166 of Mercurial has overwritten the mergestate file and left an old v2
164 file around.
167 file around.
165
168
166 returns list of record [(TYPE, data), ...]"""
169 returns list of record [(TYPE, data), ...]"""
167 v1records = self._readrecordsv1()
170 v1records = self._readrecordsv1()
168 v2records = self._readrecordsv2()
171 v2records = self._readrecordsv2()
169 if self._v1v2match(v1records, v2records):
172 if self._v1v2match(v1records, v2records):
170 return v2records
173 return v2records
171 else:
174 else:
172 # v1 file is newer than v2 file, use it
175 # v1 file is newer than v2 file, use it
173 # we have to infer the "other" changeset of the merge
176 # we have to infer the "other" changeset of the merge
174 # we cannot do better than that with v1 of the format
177 # we cannot do better than that with v1 of the format
175 mctx = self._repo[None].parents()[-1]
178 mctx = self._repo[None].parents()[-1]
176 v1records.append(('O', mctx.hex()))
179 v1records.append(('O', mctx.hex()))
177 # add place holder "other" file node information
180 # add place holder "other" file node information
178 # nobody is using it yet so we do no need to fetch the data
181 # nobody is using it yet so we do no need to fetch the data
179 # if mctx was wrong `mctx[bits[-2]]` may fails.
182 # if mctx was wrong `mctx[bits[-2]]` may fails.
180 for idx, r in enumerate(v1records):
183 for idx, r in enumerate(v1records):
181 if r[0] == 'F':
184 if r[0] == 'F':
182 bits = r[1].split('\0')
185 bits = r[1].split('\0')
183 bits.insert(-2, '')
186 bits.insert(-2, '')
184 v1records[idx] = (r[0], '\0'.join(bits))
187 v1records[idx] = (r[0], '\0'.join(bits))
185 return v1records
188 return v1records
186
189
187 def _v1v2match(self, v1records, v2records):
190 def _v1v2match(self, v1records, v2records):
188 oldv2 = set() # old format version of v2 record
191 oldv2 = set() # old format version of v2 record
189 for rec in v2records:
192 for rec in v2records:
190 if rec[0] == 'L':
193 if rec[0] == 'L':
191 oldv2.add(rec)
194 oldv2.add(rec)
192 elif rec[0] == 'F':
195 elif rec[0] == 'F':
193 # drop the onode data (not contained in v1)
196 # drop the onode data (not contained in v1)
194 oldv2.add(('F', _droponode(rec[1])))
197 oldv2.add(('F', _droponode(rec[1])))
195 for rec in v1records:
198 for rec in v1records:
196 if rec not in oldv2:
199 if rec not in oldv2:
197 return False
200 return False
198 else:
201 else:
199 return True
202 return True
200
203
201 def _readrecordsv1(self):
204 def _readrecordsv1(self):
202 """read on disk merge state for version 1 file
205 """read on disk merge state for version 1 file
203
206
204 returns list of record [(TYPE, data), ...]
207 returns list of record [(TYPE, data), ...]
205
208
206 Note: the "F" data from this file are one entry short
209 Note: the "F" data from this file are one entry short
207 (no "other file node" entry)
210 (no "other file node" entry)
208 """
211 """
209 records = []
212 records = []
210 try:
213 try:
211 f = self._repo.vfs(self.statepathv1)
214 f = self._repo.vfs(self.statepathv1)
212 for i, l in enumerate(f):
215 for i, l in enumerate(f):
213 if i == 0:
216 if i == 0:
214 records.append(('L', l[:-1]))
217 records.append(('L', l[:-1]))
215 else:
218 else:
216 records.append(('F', l[:-1]))
219 records.append(('F', l[:-1]))
217 f.close()
220 f.close()
218 except IOError as err:
221 except IOError as err:
219 if err.errno != errno.ENOENT:
222 if err.errno != errno.ENOENT:
220 raise
223 raise
221 return records
224 return records
222
225
223 def _readrecordsv2(self):
226 def _readrecordsv2(self):
224 """read on disk merge state for version 2 file
227 """read on disk merge state for version 2 file
225
228
226 returns list of record [(TYPE, data), ...]
229 returns list of record [(TYPE, data), ...]
227 """
230 """
228 records = []
231 records = []
229 try:
232 try:
230 f = self._repo.vfs(self.statepathv2)
233 f = self._repo.vfs(self.statepathv2)
231 data = f.read()
234 data = f.read()
232 off = 0
235 off = 0
233 end = len(data)
236 end = len(data)
234 while off < end:
237 while off < end:
235 rtype = data[off]
238 rtype = data[off]
236 off += 1
239 off += 1
237 length = _unpack('>I', data[off:(off + 4)])[0]
240 length = _unpack('>I', data[off:(off + 4)])[0]
238 off += 4
241 off += 4
239 record = data[off:(off + length)]
242 record = data[off:(off + length)]
240 off += length
243 off += length
241 records.append((rtype, record))
244 records.append((rtype, record))
242 f.close()
245 f.close()
243 except IOError as err:
246 except IOError as err:
244 if err.errno != errno.ENOENT:
247 if err.errno != errno.ENOENT:
245 raise
248 raise
246 return records
249 return records
247
250
248 @util.propertycache
251 @util.propertycache
249 def mergedriver(self):
252 def mergedriver(self):
250 # protect against the following:
253 # protect against the following:
251 # - A configures a malicious merge driver in their hgrc, then
254 # - A configures a malicious merge driver in their hgrc, then
252 # pauses the merge
255 # pauses the merge
253 # - A edits their hgrc to remove references to the merge driver
256 # - A edits their hgrc to remove references to the merge driver
254 # - A gives a copy of their entire repo, including .hg, to B
257 # - A gives a copy of their entire repo, including .hg, to B
255 # - B inspects .hgrc and finds it to be clean
258 # - B inspects .hgrc and finds it to be clean
256 # - B then continues the merge and the malicious merge driver
259 # - B then continues the merge and the malicious merge driver
257 # gets invoked
260 # gets invoked
258 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
261 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
259 if (self._readmergedriver is not None
262 if (self._readmergedriver is not None
260 and self._readmergedriver != configmergedriver):
263 and self._readmergedriver != configmergedriver):
261 raise error.ConfigError(
264 raise error.ConfigError(
262 _("merge driver changed since merge started"),
265 _("merge driver changed since merge started"),
263 hint=_("revert merge driver change or abort merge"))
266 hint=_("revert merge driver change or abort merge"))
264
267
265 return configmergedriver
268 return configmergedriver
266
269
267 @util.propertycache
270 @util.propertycache
268 def otherctx(self):
271 def otherctx(self):
269 return self._repo[self._other]
272 return self._repo[self._other]
270
273
271 def active(self):
274 def active(self):
272 """Whether mergestate is active.
275 """Whether mergestate is active.
273
276
274 Returns True if there appears to be mergestate. This is a rough proxy
277 Returns True if there appears to be mergestate. This is a rough proxy
275 for "is a merge in progress."
278 for "is a merge in progress."
276 """
279 """
277 # Check local variables before looking at filesystem for performance
280 # Check local variables before looking at filesystem for performance
278 # reasons.
281 # reasons.
279 return bool(self._local) or bool(self._state) or \
282 return bool(self._local) or bool(self._state) or \
280 self._repo.vfs.exists(self.statepathv1) or \
283 self._repo.vfs.exists(self.statepathv1) or \
281 self._repo.vfs.exists(self.statepathv2)
284 self._repo.vfs.exists(self.statepathv2)
282
285
283 def commit(self):
286 def commit(self):
284 """Write current state on disk (if necessary)"""
287 """Write current state on disk (if necessary)"""
285 if self._dirty:
288 if self._dirty:
286 records = []
289 records = []
287 records.append(('L', hex(self._local)))
290 records.append(('L', hex(self._local)))
288 records.append(('O', hex(self._other)))
291 records.append(('O', hex(self._other)))
289 if self.mergedriver:
292 if self.mergedriver:
290 records.append(('m', '\0'.join([
293 records.append(('m', '\0'.join([
291 self.mergedriver, self._mdstate])))
294 self.mergedriver, self._mdstate])))
292 for d, v in self._state.iteritems():
295 for d, v in self._state.iteritems():
293 if v[0] == 'd':
296 if v[0] == 'd':
294 records.append(('D', '\0'.join([d] + v)))
297 records.append(('D', '\0'.join([d] + v)))
295 else:
298 else:
296 records.append(('F', '\0'.join([d] + v)))
299 records.append(('F', '\0'.join([d] + v)))
297 self._writerecords(records)
300 self._writerecords(records)
298 self._dirty = False
301 self._dirty = False
299
302
300 def _writerecords(self, records):
303 def _writerecords(self, records):
301 """Write current state on disk (both v1 and v2)"""
304 """Write current state on disk (both v1 and v2)"""
302 self._writerecordsv1(records)
305 self._writerecordsv1(records)
303 self._writerecordsv2(records)
306 self._writerecordsv2(records)
304
307
305 def _writerecordsv1(self, records):
308 def _writerecordsv1(self, records):
306 """Write current state on disk in a version 1 file"""
309 """Write current state on disk in a version 1 file"""
307 f = self._repo.vfs(self.statepathv1, 'w')
310 f = self._repo.vfs(self.statepathv1, 'w')
308 irecords = iter(records)
311 irecords = iter(records)
309 lrecords = irecords.next()
312 lrecords = irecords.next()
310 assert lrecords[0] == 'L'
313 assert lrecords[0] == 'L'
311 f.write(hex(self._local) + '\n')
314 f.write(hex(self._local) + '\n')
312 for rtype, data in irecords:
315 for rtype, data in irecords:
313 if rtype == 'F':
316 if rtype == 'F':
314 f.write('%s\n' % _droponode(data))
317 f.write('%s\n' % _droponode(data))
315 f.close()
318 f.close()
316
319
317 def _writerecordsv2(self, records):
320 def _writerecordsv2(self, records):
318 """Write current state on disk in a version 2 file"""
321 """Write current state on disk in a version 2 file"""
319 f = self._repo.vfs(self.statepathv2, 'w')
322 f = self._repo.vfs(self.statepathv2, 'w')
320 for key, data in records:
323 for key, data in records:
321 assert len(key) == 1
324 assert len(key) == 1
322 format = '>sI%is' % len(data)
325 format = '>sI%is' % len(data)
323 f.write(_pack(format, key, len(data), data))
326 f.write(_pack(format, key, len(data), data))
324 f.close()
327 f.close()
325
328
326 def add(self, fcl, fco, fca, fd):
329 def add(self, fcl, fco, fca, fd):
327 """add a new (potentially?) conflicting file the merge state
330 """add a new (potentially?) conflicting file the merge state
328 fcl: file context for local,
331 fcl: file context for local,
329 fco: file context for remote,
332 fco: file context for remote,
330 fca: file context for ancestors,
333 fca: file context for ancestors,
331 fd: file path of the resulting merge.
334 fd: file path of the resulting merge.
332
335
333 note: also write the local version to the `.hg/merge` directory.
336 note: also write the local version to the `.hg/merge` directory.
334 """
337 """
335 hash = util.sha1(fcl.path()).hexdigest()
338 hash = util.sha1(fcl.path()).hexdigest()
336 self._repo.vfs.write('merge/' + hash, fcl.data())
339 self._repo.vfs.write('merge/' + hash, fcl.data())
337 self._state[fd] = ['u', hash, fcl.path(),
340 self._state[fd] = ['u', hash, fcl.path(),
338 fca.path(), hex(fca.filenode()),
341 fca.path(), hex(fca.filenode()),
339 fco.path(), hex(fco.filenode()),
342 fco.path(), hex(fco.filenode()),
340 fcl.flags()]
343 fcl.flags()]
341 self._dirty = True
344 self._dirty = True
342
345
343 def __contains__(self, dfile):
346 def __contains__(self, dfile):
344 return dfile in self._state
347 return dfile in self._state
345
348
346 def __getitem__(self, dfile):
349 def __getitem__(self, dfile):
347 return self._state[dfile][0]
350 return self._state[dfile][0]
348
351
349 def __iter__(self):
352 def __iter__(self):
350 return iter(sorted(self._state))
353 return iter(sorted(self._state))
351
354
352 def files(self):
355 def files(self):
353 return self._state.keys()
356 return self._state.keys()
354
357
355 def mark(self, dfile, state):
358 def mark(self, dfile, state):
356 self._state[dfile][0] = state
359 self._state[dfile][0] = state
357 self._dirty = True
360 self._dirty = True
358
361
359 def mdstate(self):
362 def mdstate(self):
360 return self._mdstate
363 return self._mdstate
361
364
362 def unresolved(self):
365 def unresolved(self):
363 """Obtain the paths of unresolved files."""
366 """Obtain the paths of unresolved files."""
364
367
365 for f, entry in self._state.items():
368 for f, entry in self._state.items():
366 if entry[0] == 'u':
369 if entry[0] == 'u':
367 yield f
370 yield f
368
371
369 def driverresolved(self):
372 def driverresolved(self):
370 """Obtain the paths of driver-resolved files."""
373 """Obtain the paths of driver-resolved files."""
371
374
372 for f, entry in self._state.items():
375 for f, entry in self._state.items():
373 if entry[0] == 'd':
376 if entry[0] == 'd':
374 yield f
377 yield f
375
378
376 def _resolve(self, preresolve, dfile, wctx, labels=None):
379 def _resolve(self, preresolve, dfile, wctx, labels=None):
377 """rerun merge process for file path `dfile`"""
380 """rerun merge process for file path `dfile`"""
378 if self[dfile] in 'rd':
381 if self[dfile] in 'rd':
379 return True, 0
382 return True, 0
380 stateentry = self._state[dfile]
383 stateentry = self._state[dfile]
381 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
384 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
382 octx = self._repo[self._other]
385 octx = self._repo[self._other]
383 fcd = wctx[dfile]
386 fcd = wctx[dfile]
384 fco = octx[ofile]
387 fco = octx[ofile]
385 fca = self._repo.filectx(afile, fileid=anode)
388 fca = self._repo.filectx(afile, fileid=anode)
386 # "premerge" x flags
389 # "premerge" x flags
387 flo = fco.flags()
390 flo = fco.flags()
388 fla = fca.flags()
391 fla = fca.flags()
389 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
392 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
390 if fca.node() == nullid:
393 if fca.node() == nullid:
391 if preresolve:
394 if preresolve:
392 self._repo.ui.warn(
395 self._repo.ui.warn(
393 _('warning: cannot merge flags for %s\n') % afile)
396 _('warning: cannot merge flags for %s\n') % afile)
394 elif flags == fla:
397 elif flags == fla:
395 flags = flo
398 flags = flo
396 if preresolve:
399 if preresolve:
397 # restore local
400 # restore local
398 f = self._repo.vfs('merge/' + hash)
401 f = self._repo.vfs('merge/' + hash)
399 self._repo.wwrite(dfile, f.read(), flags)
402 self._repo.wwrite(dfile, f.read(), flags)
400 f.close()
403 f.close()
401 complete, r = filemerge.premerge(self._repo, self._local, lfile,
404 complete, r = filemerge.premerge(self._repo, self._local, lfile,
402 fcd, fco, fca, labels=labels)
405 fcd, fco, fca, labels=labels)
403 else:
406 else:
404 complete, r = filemerge.filemerge(self._repo, self._local, lfile,
407 complete, r = filemerge.filemerge(self._repo, self._local, lfile,
405 fcd, fco, fca, labels=labels)
408 fcd, fco, fca, labels=labels)
406 if r is None:
409 if r is None:
407 # no real conflict
410 # no real conflict
408 del self._state[dfile]
411 del self._state[dfile]
409 self._dirty = True
412 self._dirty = True
410 elif not r:
413 elif not r:
411 self.mark(dfile, 'r')
414 self.mark(dfile, 'r')
412 return complete, r
415 return complete, r
413
416
414 def preresolve(self, dfile, wctx, labels=None):
417 def preresolve(self, dfile, wctx, labels=None):
415 """run premerge process for dfile
418 """run premerge process for dfile
416
419
417 Returns whether the merge is complete, and the exit code."""
420 Returns whether the merge is complete, and the exit code."""
418 return self._resolve(True, dfile, wctx, labels=labels)
421 return self._resolve(True, dfile, wctx, labels=labels)
419
422
420 def resolve(self, dfile, wctx, labels=None):
423 def resolve(self, dfile, wctx, labels=None):
421 """run merge process (assuming premerge was run) for dfile
424 """run merge process (assuming premerge was run) for dfile
422
425
423 Returns the exit code of the merge."""
426 Returns the exit code of the merge."""
424 return self._resolve(False, dfile, wctx, labels=labels)[1]
427 return self._resolve(False, dfile, wctx, labels=labels)[1]
425
428
426 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
429 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
427 if f2 is None:
430 if f2 is None:
428 f2 = f
431 f2 = f
429 return (os.path.isfile(repo.wjoin(f))
432 return (os.path.isfile(repo.wjoin(f))
430 and repo.wvfs.audit.check(f)
433 and repo.wvfs.audit.check(f)
431 and repo.dirstate.normalize(f) not in repo.dirstate
434 and repo.dirstate.normalize(f) not in repo.dirstate
432 and mctx[f2].cmp(wctx[f]))
435 and mctx[f2].cmp(wctx[f]))
433
436
434 def _checkunknownfiles(repo, wctx, mctx, force, actions):
437 def _checkunknownfiles(repo, wctx, mctx, force, actions):
435 """
438 """
436 Considers any actions that care about the presence of conflicting unknown
439 Considers any actions that care about the presence of conflicting unknown
437 files. For some actions, the result is to abort; for others, it is to
440 files. For some actions, the result is to abort; for others, it is to
438 choose a different action.
441 choose a different action.
439 """
442 """
440 aborts = []
443 aborts = []
441 if not force:
444 if not force:
442 for f, (m, args, msg) in actions.iteritems():
445 for f, (m, args, msg) in actions.iteritems():
443 if m in ('c', 'dc'):
446 if m in ('c', 'dc'):
444 if _checkunknownfile(repo, wctx, mctx, f):
447 if _checkunknownfile(repo, wctx, mctx, f):
445 aborts.append(f)
448 aborts.append(f)
446 elif m == 'dg':
449 elif m == 'dg':
447 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
450 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
448 aborts.append(f)
451 aborts.append(f)
449
452
450 for f in sorted(aborts):
453 for f in sorted(aborts):
451 repo.ui.warn(_("%s: untracked file differs\n") % f)
454 repo.ui.warn(_("%s: untracked file differs\n") % f)
452 if aborts:
455 if aborts:
453 raise error.Abort(_("untracked files in working directory differ "
456 raise error.Abort(_("untracked files in working directory differ "
454 "from files in requested revision"))
457 "from files in requested revision"))
455
458
456 for f, (m, args, msg) in actions.iteritems():
459 for f, (m, args, msg) in actions.iteritems():
457 if m == 'c':
460 if m == 'c':
458 actions[f] = ('g', args, msg)
461 actions[f] = ('g', args, msg)
459 elif m == 'cm':
462 elif m == 'cm':
460 fl2, anc = args
463 fl2, anc = args
461 different = _checkunknownfile(repo, wctx, mctx, f)
464 different = _checkunknownfile(repo, wctx, mctx, f)
462 if different:
465 if different:
463 actions[f] = ('m', (f, f, None, False, anc),
466 actions[f] = ('m', (f, f, None, False, anc),
464 "remote differs from untracked local")
467 "remote differs from untracked local")
465 else:
468 else:
466 actions[f] = ('g', (fl2,), "remote created")
469 actions[f] = ('g', (fl2,), "remote created")
467
470
468 def _forgetremoved(wctx, mctx, branchmerge):
471 def _forgetremoved(wctx, mctx, branchmerge):
469 """
472 """
470 Forget removed files
473 Forget removed files
471
474
472 If we're jumping between revisions (as opposed to merging), and if
475 If we're jumping between revisions (as opposed to merging), and if
473 neither the working directory nor the target rev has the file,
476 neither the working directory nor the target rev has the file,
474 then we need to remove it from the dirstate, to prevent the
477 then we need to remove it from the dirstate, to prevent the
475 dirstate from listing the file when it is no longer in the
478 dirstate from listing the file when it is no longer in the
476 manifest.
479 manifest.
477
480
478 If we're merging, and the other revision has removed a file
481 If we're merging, and the other revision has removed a file
479 that is not present in the working directory, we need to mark it
482 that is not present in the working directory, we need to mark it
480 as removed.
483 as removed.
481 """
484 """
482
485
483 actions = {}
486 actions = {}
484 m = 'f'
487 m = 'f'
485 if branchmerge:
488 if branchmerge:
486 m = 'r'
489 m = 'r'
487 for f in wctx.deleted():
490 for f in wctx.deleted():
488 if f not in mctx:
491 if f not in mctx:
489 actions[f] = m, None, "forget deleted"
492 actions[f] = m, None, "forget deleted"
490
493
491 if not branchmerge:
494 if not branchmerge:
492 for f in wctx.removed():
495 for f in wctx.removed():
493 if f not in mctx:
496 if f not in mctx:
494 actions[f] = 'f', None, "forget removed"
497 actions[f] = 'f', None, "forget removed"
495
498
496 return actions
499 return actions
497
500
498 def _checkcollision(repo, wmf, actions):
501 def _checkcollision(repo, wmf, actions):
499 # build provisional merged manifest up
502 # build provisional merged manifest up
500 pmmf = set(wmf)
503 pmmf = set(wmf)
501
504
502 if actions:
505 if actions:
503 # k, dr, e and rd are no-op
506 # k, dr, e and rd are no-op
504 for m in 'a', 'f', 'g', 'cd', 'dc':
507 for m in 'a', 'f', 'g', 'cd', 'dc':
505 for f, args, msg in actions[m]:
508 for f, args, msg in actions[m]:
506 pmmf.add(f)
509 pmmf.add(f)
507 for f, args, msg in actions['r']:
510 for f, args, msg in actions['r']:
508 pmmf.discard(f)
511 pmmf.discard(f)
509 for f, args, msg in actions['dm']:
512 for f, args, msg in actions['dm']:
510 f2, flags = args
513 f2, flags = args
511 pmmf.discard(f2)
514 pmmf.discard(f2)
512 pmmf.add(f)
515 pmmf.add(f)
513 for f, args, msg in actions['dg']:
516 for f, args, msg in actions['dg']:
514 pmmf.add(f)
517 pmmf.add(f)
515 for f, args, msg in actions['m']:
518 for f, args, msg in actions['m']:
516 f1, f2, fa, move, anc = args
519 f1, f2, fa, move, anc = args
517 if move:
520 if move:
518 pmmf.discard(f1)
521 pmmf.discard(f1)
519 pmmf.add(f)
522 pmmf.add(f)
520
523
521 # check case-folding collision in provisional merged manifest
524 # check case-folding collision in provisional merged manifest
522 foldmap = {}
525 foldmap = {}
523 for f in sorted(pmmf):
526 for f in sorted(pmmf):
524 fold = util.normcase(f)
527 fold = util.normcase(f)
525 if fold in foldmap:
528 if fold in foldmap:
526 raise error.Abort(_("case-folding collision between %s and %s")
529 raise error.Abort(_("case-folding collision between %s and %s")
527 % (f, foldmap[fold]))
530 % (f, foldmap[fold]))
528 foldmap[fold] = f
531 foldmap[fold] = f
529
532
530 # check case-folding of directories
533 # check case-folding of directories
531 foldprefix = unfoldprefix = lastfull = ''
534 foldprefix = unfoldprefix = lastfull = ''
532 for fold, f in sorted(foldmap.items()):
535 for fold, f in sorted(foldmap.items()):
533 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
536 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
534 # the folded prefix matches but actual casing is different
537 # the folded prefix matches but actual casing is different
535 raise error.Abort(_("case-folding collision between "
538 raise error.Abort(_("case-folding collision between "
536 "%s and directory of %s") % (lastfull, f))
539 "%s and directory of %s") % (lastfull, f))
537 foldprefix = fold + '/'
540 foldprefix = fold + '/'
538 unfoldprefix = f + '/'
541 unfoldprefix = f + '/'
539 lastfull = f
542 lastfull = f
540
543
541 def driverpreprocess(repo, ms, wctx, labels=None):
544 def driverpreprocess(repo, ms, wctx, labels=None):
542 """run the preprocess step of the merge driver, if any
545 """run the preprocess step of the merge driver, if any
543
546
544 This is currently not implemented -- it's an extension point."""
547 This is currently not implemented -- it's an extension point."""
545 return True
548 return True
546
549
547 def driverconclude(repo, ms, wctx, labels=None):
550 def driverconclude(repo, ms, wctx, labels=None):
548 """run the conclude step of the merge driver, if any
551 """run the conclude step of the merge driver, if any
549
552
550 This is currently not implemented -- it's an extension point."""
553 This is currently not implemented -- it's an extension point."""
551 return True
554 return True
552
555
553 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
556 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
554 acceptremote, followcopies):
557 acceptremote, followcopies):
555 """
558 """
556 Merge p1 and p2 with ancestor pa and generate merge action list
559 Merge p1 and p2 with ancestor pa and generate merge action list
557
560
558 branchmerge and force are as passed in to update
561 branchmerge and force are as passed in to update
559 partial = function to filter file lists
562 partial = function to filter file lists
560 acceptremote = accept the incoming changes without prompting
563 acceptremote = accept the incoming changes without prompting
561 """
564 """
562
565
563 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
566 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
564
567
565 # manifests fetched in order are going to be faster, so prime the caches
568 # manifests fetched in order are going to be faster, so prime the caches
566 [x.manifest() for x in
569 [x.manifest() for x in
567 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
570 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
568
571
569 if followcopies:
572 if followcopies:
570 ret = copies.mergecopies(repo, wctx, p2, pa)
573 ret = copies.mergecopies(repo, wctx, p2, pa)
571 copy, movewithdir, diverge, renamedelete = ret
574 copy, movewithdir, diverge, renamedelete = ret
572
575
573 repo.ui.note(_("resolving manifests\n"))
576 repo.ui.note(_("resolving manifests\n"))
574 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
577 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
575 % (bool(branchmerge), bool(force), bool(partial)))
578 % (bool(branchmerge), bool(force), bool(partial)))
576 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
579 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
577
580
578 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
581 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
579 copied = set(copy.values())
582 copied = set(copy.values())
580 copied.update(movewithdir.values())
583 copied.update(movewithdir.values())
581
584
582 if '.hgsubstate' in m1:
585 if '.hgsubstate' in m1:
583 # check whether sub state is modified
586 # check whether sub state is modified
584 for s in sorted(wctx.substate):
587 for s in sorted(wctx.substate):
585 if wctx.sub(s).dirty():
588 if wctx.sub(s).dirty():
586 m1['.hgsubstate'] += '+'
589 m1['.hgsubstate'] += '+'
587 break
590 break
588
591
589 # Compare manifests
592 # Compare manifests
590 diff = m1.diff(m2)
593 diff = m1.diff(m2)
591
594
592 actions = {}
595 actions = {}
593 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
596 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
594 if partial and not partial(f):
597 if partial and not partial(f):
595 continue
598 continue
596 if n1 and n2: # file exists on both local and remote side
599 if n1 and n2: # file exists on both local and remote side
597 if f not in ma:
600 if f not in ma:
598 fa = copy.get(f, None)
601 fa = copy.get(f, None)
599 if fa is not None:
602 if fa is not None:
600 actions[f] = ('m', (f, f, fa, False, pa.node()),
603 actions[f] = ('m', (f, f, fa, False, pa.node()),
601 "both renamed from " + fa)
604 "both renamed from " + fa)
602 else:
605 else:
603 actions[f] = ('m', (f, f, None, False, pa.node()),
606 actions[f] = ('m', (f, f, None, False, pa.node()),
604 "both created")
607 "both created")
605 else:
608 else:
606 a = ma[f]
609 a = ma[f]
607 fla = ma.flags(f)
610 fla = ma.flags(f)
608 nol = 'l' not in fl1 + fl2 + fla
611 nol = 'l' not in fl1 + fl2 + fla
609 if n2 == a and fl2 == fla:
612 if n2 == a and fl2 == fla:
610 actions[f] = ('k' , (), "remote unchanged")
613 actions[f] = ('k' , (), "remote unchanged")
611 elif n1 == a and fl1 == fla: # local unchanged - use remote
614 elif n1 == a and fl1 == fla: # local unchanged - use remote
612 if n1 == n2: # optimization: keep local content
615 if n1 == n2: # optimization: keep local content
613 actions[f] = ('e', (fl2,), "update permissions")
616 actions[f] = ('e', (fl2,), "update permissions")
614 else:
617 else:
615 actions[f] = ('g', (fl2,), "remote is newer")
618 actions[f] = ('g', (fl2,), "remote is newer")
616 elif nol and n2 == a: # remote only changed 'x'
619 elif nol and n2 == a: # remote only changed 'x'
617 actions[f] = ('e', (fl2,), "update permissions")
620 actions[f] = ('e', (fl2,), "update permissions")
618 elif nol and n1 == a: # local only changed 'x'
621 elif nol and n1 == a: # local only changed 'x'
619 actions[f] = ('g', (fl1,), "remote is newer")
622 actions[f] = ('g', (fl1,), "remote is newer")
620 else: # both changed something
623 else: # both changed something
621 actions[f] = ('m', (f, f, f, False, pa.node()),
624 actions[f] = ('m', (f, f, f, False, pa.node()),
622 "versions differ")
625 "versions differ")
623 elif n1: # file exists only on local side
626 elif n1: # file exists only on local side
624 if f in copied:
627 if f in copied:
625 pass # we'll deal with it on m2 side
628 pass # we'll deal with it on m2 side
626 elif f in movewithdir: # directory rename, move local
629 elif f in movewithdir: # directory rename, move local
627 f2 = movewithdir[f]
630 f2 = movewithdir[f]
628 if f2 in m2:
631 if f2 in m2:
629 actions[f2] = ('m', (f, f2, None, True, pa.node()),
632 actions[f2] = ('m', (f, f2, None, True, pa.node()),
630 "remote directory rename, both created")
633 "remote directory rename, both created")
631 else:
634 else:
632 actions[f2] = ('dm', (f, fl1),
635 actions[f2] = ('dm', (f, fl1),
633 "remote directory rename - move from " + f)
636 "remote directory rename - move from " + f)
634 elif f in copy:
637 elif f in copy:
635 f2 = copy[f]
638 f2 = copy[f]
636 actions[f] = ('m', (f, f2, f2, False, pa.node()),
639 actions[f] = ('m', (f, f2, f2, False, pa.node()),
637 "local copied/moved from " + f2)
640 "local copied/moved from " + f2)
638 elif f in ma: # clean, a different, no remote
641 elif f in ma: # clean, a different, no remote
639 if n1 != ma[f]:
642 if n1 != ma[f]:
640 if acceptremote:
643 if acceptremote:
641 actions[f] = ('r', None, "remote delete")
644 actions[f] = ('r', None, "remote delete")
642 else:
645 else:
643 actions[f] = ('cd', (f, None, f, False, pa.node()),
646 actions[f] = ('cd', (f, None, f, False, pa.node()),
644 "prompt changed/deleted")
647 "prompt changed/deleted")
645 elif n1[20:] == 'a':
648 elif n1[20:] == 'a':
646 # This extra 'a' is added by working copy manifest to mark
649 # This extra 'a' is added by working copy manifest to mark
647 # the file as locally added. We should forget it instead of
650 # the file as locally added. We should forget it instead of
648 # deleting it.
651 # deleting it.
649 actions[f] = ('f', None, "remote deleted")
652 actions[f] = ('f', None, "remote deleted")
650 else:
653 else:
651 actions[f] = ('r', None, "other deleted")
654 actions[f] = ('r', None, "other deleted")
652 elif n2: # file exists only on remote side
655 elif n2: # file exists only on remote side
653 if f in copied:
656 if f in copied:
654 pass # we'll deal with it on m1 side
657 pass # we'll deal with it on m1 side
655 elif f in movewithdir:
658 elif f in movewithdir:
656 f2 = movewithdir[f]
659 f2 = movewithdir[f]
657 if f2 in m1:
660 if f2 in m1:
658 actions[f2] = ('m', (f2, f, None, False, pa.node()),
661 actions[f2] = ('m', (f2, f, None, False, pa.node()),
659 "local directory rename, both created")
662 "local directory rename, both created")
660 else:
663 else:
661 actions[f2] = ('dg', (f, fl2),
664 actions[f2] = ('dg', (f, fl2),
662 "local directory rename - get from " + f)
665 "local directory rename - get from " + f)
663 elif f in copy:
666 elif f in copy:
664 f2 = copy[f]
667 f2 = copy[f]
665 if f2 in m2:
668 if f2 in m2:
666 actions[f] = ('m', (f2, f, f2, False, pa.node()),
669 actions[f] = ('m', (f2, f, f2, False, pa.node()),
667 "remote copied from " + f2)
670 "remote copied from " + f2)
668 else:
671 else:
669 actions[f] = ('m', (f2, f, f2, True, pa.node()),
672 actions[f] = ('m', (f2, f, f2, True, pa.node()),
670 "remote moved from " + f2)
673 "remote moved from " + f2)
671 elif f not in ma:
674 elif f not in ma:
672 # local unknown, remote created: the logic is described by the
675 # local unknown, remote created: the logic is described by the
673 # following table:
676 # following table:
674 #
677 #
675 # force branchmerge different | action
678 # force branchmerge different | action
676 # n * * | create
679 # n * * | create
677 # y n * | create
680 # y n * | create
678 # y y n | create
681 # y y n | create
679 # y y y | merge
682 # y y y | merge
680 #
683 #
681 # Checking whether the files are different is expensive, so we
684 # Checking whether the files are different is expensive, so we
682 # don't do that when we can avoid it.
685 # don't do that when we can avoid it.
683 if not force:
686 if not force:
684 actions[f] = ('c', (fl2,), "remote created")
687 actions[f] = ('c', (fl2,), "remote created")
685 elif not branchmerge:
688 elif not branchmerge:
686 actions[f] = ('c', (fl2,), "remote created")
689 actions[f] = ('c', (fl2,), "remote created")
687 else:
690 else:
688 actions[f] = ('cm', (fl2, pa.node()),
691 actions[f] = ('cm', (fl2, pa.node()),
689 "remote created, get or merge")
692 "remote created, get or merge")
690 elif n2 != ma[f]:
693 elif n2 != ma[f]:
691 if acceptremote:
694 if acceptremote:
692 actions[f] = ('c', (fl2,), "remote recreating")
695 actions[f] = ('c', (fl2,), "remote recreating")
693 else:
696 else:
694 actions[f] = ('dc', (None, f, f, False, pa.node()),
697 actions[f] = ('dc', (None, f, f, False, pa.node()),
695 "prompt deleted/changed")
698 "prompt deleted/changed")
696
699
697 return actions, diverge, renamedelete
700 return actions, diverge, renamedelete
698
701
699 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
702 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
700 """Resolves false conflicts where the nodeid changed but the content
703 """Resolves false conflicts where the nodeid changed but the content
701 remained the same."""
704 remained the same."""
702
705
703 for f, (m, args, msg) in actions.items():
706 for f, (m, args, msg) in actions.items():
704 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
707 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
705 # local did change but ended up with same content
708 # local did change but ended up with same content
706 actions[f] = 'r', None, "prompt same"
709 actions[f] = 'r', None, "prompt same"
707 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
710 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
708 # remote did change but ended up with same content
711 # remote did change but ended up with same content
709 del actions[f] # don't get = keep local deleted
712 del actions[f] # don't get = keep local deleted
710
713
711 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
714 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
712 acceptremote, followcopies):
715 acceptremote, followcopies):
713 "Calculate the actions needed to merge mctx into wctx using ancestors"
716 "Calculate the actions needed to merge mctx into wctx using ancestors"
714
717
715 if len(ancestors) == 1: # default
718 if len(ancestors) == 1: # default
716 actions, diverge, renamedelete = manifestmerge(
719 actions, diverge, renamedelete = manifestmerge(
717 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
720 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
718 acceptremote, followcopies)
721 acceptremote, followcopies)
719 _checkunknownfiles(repo, wctx, mctx, force, actions)
722 _checkunknownfiles(repo, wctx, mctx, force, actions)
720
723
721 else: # only when merge.preferancestor=* - the default
724 else: # only when merge.preferancestor=* - the default
722 repo.ui.note(
725 repo.ui.note(
723 _("note: merging %s and %s using bids from ancestors %s\n") %
726 _("note: merging %s and %s using bids from ancestors %s\n") %
724 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
727 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
725
728
726 # Call for bids
729 # Call for bids
727 fbids = {} # mapping filename to bids (action method to list af actions)
730 fbids = {} # mapping filename to bids (action method to list af actions)
728 diverge, renamedelete = None, None
731 diverge, renamedelete = None, None
729 for ancestor in ancestors:
732 for ancestor in ancestors:
730 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
733 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
731 actions, diverge1, renamedelete1 = manifestmerge(
734 actions, diverge1, renamedelete1 = manifestmerge(
732 repo, wctx, mctx, ancestor, branchmerge, force, partial,
735 repo, wctx, mctx, ancestor, branchmerge, force, partial,
733 acceptremote, followcopies)
736 acceptremote, followcopies)
734 _checkunknownfiles(repo, wctx, mctx, force, actions)
737 _checkunknownfiles(repo, wctx, mctx, force, actions)
735
738
736 # Track the shortest set of warning on the theory that bid
739 # Track the shortest set of warning on the theory that bid
737 # merge will correctly incorporate more information
740 # merge will correctly incorporate more information
738 if diverge is None or len(diverge1) < len(diverge):
741 if diverge is None or len(diverge1) < len(diverge):
739 diverge = diverge1
742 diverge = diverge1
740 if renamedelete is None or len(renamedelete) < len(renamedelete1):
743 if renamedelete is None or len(renamedelete) < len(renamedelete1):
741 renamedelete = renamedelete1
744 renamedelete = renamedelete1
742
745
743 for f, a in sorted(actions.iteritems()):
746 for f, a in sorted(actions.iteritems()):
744 m, args, msg = a
747 m, args, msg = a
745 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
748 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
746 if f in fbids:
749 if f in fbids:
747 d = fbids[f]
750 d = fbids[f]
748 if m in d:
751 if m in d:
749 d[m].append(a)
752 d[m].append(a)
750 else:
753 else:
751 d[m] = [a]
754 d[m] = [a]
752 else:
755 else:
753 fbids[f] = {m: [a]}
756 fbids[f] = {m: [a]}
754
757
755 # Pick the best bid for each file
758 # Pick the best bid for each file
756 repo.ui.note(_('\nauction for merging merge bids\n'))
759 repo.ui.note(_('\nauction for merging merge bids\n'))
757 actions = {}
760 actions = {}
758 for f, bids in sorted(fbids.items()):
761 for f, bids in sorted(fbids.items()):
759 # bids is a mapping from action method to list af actions
762 # bids is a mapping from action method to list af actions
760 # Consensus?
763 # Consensus?
761 if len(bids) == 1: # all bids are the same kind of method
764 if len(bids) == 1: # all bids are the same kind of method
762 m, l = bids.items()[0]
765 m, l = bids.items()[0]
763 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
766 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
764 repo.ui.note(" %s: consensus for %s\n" % (f, m))
767 repo.ui.note(" %s: consensus for %s\n" % (f, m))
765 actions[f] = l[0]
768 actions[f] = l[0]
766 continue
769 continue
767 # If keep is an option, just do it.
770 # If keep is an option, just do it.
768 if 'k' in bids:
771 if 'k' in bids:
769 repo.ui.note(" %s: picking 'keep' action\n" % f)
772 repo.ui.note(" %s: picking 'keep' action\n" % f)
770 actions[f] = bids['k'][0]
773 actions[f] = bids['k'][0]
771 continue
774 continue
772 # If there are gets and they all agree [how could they not?], do it.
775 # If there are gets and they all agree [how could they not?], do it.
773 if 'g' in bids:
776 if 'g' in bids:
774 ga0 = bids['g'][0]
777 ga0 = bids['g'][0]
775 if all(a == ga0 for a in bids['g'][1:]):
778 if all(a == ga0 for a in bids['g'][1:]):
776 repo.ui.note(" %s: picking 'get' action\n" % f)
779 repo.ui.note(" %s: picking 'get' action\n" % f)
777 actions[f] = ga0
780 actions[f] = ga0
778 continue
781 continue
779 # TODO: Consider other simple actions such as mode changes
782 # TODO: Consider other simple actions such as mode changes
780 # Handle inefficient democrazy.
783 # Handle inefficient democrazy.
781 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
784 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
782 for m, l in sorted(bids.items()):
785 for m, l in sorted(bids.items()):
783 for _f, args, msg in l:
786 for _f, args, msg in l:
784 repo.ui.note(' %s -> %s\n' % (msg, m))
787 repo.ui.note(' %s -> %s\n' % (msg, m))
785 # Pick random action. TODO: Instead, prompt user when resolving
788 # Pick random action. TODO: Instead, prompt user when resolving
786 m, l = bids.items()[0]
789 m, l = bids.items()[0]
787 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
790 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
788 (f, m))
791 (f, m))
789 actions[f] = l[0]
792 actions[f] = l[0]
790 continue
793 continue
791 repo.ui.note(_('end of auction\n\n'))
794 repo.ui.note(_('end of auction\n\n'))
792
795
793 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
796 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
794
797
795 if wctx.rev() is None:
798 if wctx.rev() is None:
796 fractions = _forgetremoved(wctx, mctx, branchmerge)
799 fractions = _forgetremoved(wctx, mctx, branchmerge)
797 actions.update(fractions)
800 actions.update(fractions)
798
801
799 return actions, diverge, renamedelete
802 return actions, diverge, renamedelete
800
803
801 def batchremove(repo, actions):
804 def batchremove(repo, actions):
802 """apply removes to the working directory
805 """apply removes to the working directory
803
806
804 yields tuples for progress updates
807 yields tuples for progress updates
805 """
808 """
806 verbose = repo.ui.verbose
809 verbose = repo.ui.verbose
807 unlink = util.unlinkpath
810 unlink = util.unlinkpath
808 wjoin = repo.wjoin
811 wjoin = repo.wjoin
809 audit = repo.wvfs.audit
812 audit = repo.wvfs.audit
810 i = 0
813 i = 0
811 for f, args, msg in actions:
814 for f, args, msg in actions:
812 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
815 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
813 if verbose:
816 if verbose:
814 repo.ui.note(_("removing %s\n") % f)
817 repo.ui.note(_("removing %s\n") % f)
815 audit(f)
818 audit(f)
816 try:
819 try:
817 unlink(wjoin(f), ignoremissing=True)
820 unlink(wjoin(f), ignoremissing=True)
818 except OSError as inst:
821 except OSError as inst:
819 repo.ui.warn(_("update failed to remove %s: %s!\n") %
822 repo.ui.warn(_("update failed to remove %s: %s!\n") %
820 (f, inst.strerror))
823 (f, inst.strerror))
821 if i == 100:
824 if i == 100:
822 yield i, f
825 yield i, f
823 i = 0
826 i = 0
824 i += 1
827 i += 1
825 if i > 0:
828 if i > 0:
826 yield i, f
829 yield i, f
827
830
828 def batchget(repo, mctx, actions):
831 def batchget(repo, mctx, actions):
829 """apply gets to the working directory
832 """apply gets to the working directory
830
833
831 mctx is the context to get from
834 mctx is the context to get from
832
835
833 yields tuples for progress updates
836 yields tuples for progress updates
834 """
837 """
835 verbose = repo.ui.verbose
838 verbose = repo.ui.verbose
836 fctx = mctx.filectx
839 fctx = mctx.filectx
837 wwrite = repo.wwrite
840 wwrite = repo.wwrite
838 i = 0
841 i = 0
839 for f, args, msg in actions:
842 for f, args, msg in actions:
840 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
843 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
841 if verbose:
844 if verbose:
842 repo.ui.note(_("getting %s\n") % f)
845 repo.ui.note(_("getting %s\n") % f)
843 wwrite(f, fctx(f).data(), args[0])
846 wwrite(f, fctx(f).data(), args[0])
844 if i == 100:
847 if i == 100:
845 yield i, f
848 yield i, f
846 i = 0
849 i = 0
847 i += 1
850 i += 1
848 if i > 0:
851 if i > 0:
849 yield i, f
852 yield i, f
850
853
851 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
854 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
852 """apply the merge action list to the working directory
855 """apply the merge action list to the working directory
853
856
854 wctx is the working copy context
857 wctx is the working copy context
855 mctx is the context to be merged into the working copy
858 mctx is the context to be merged into the working copy
856
859
857 Return a tuple of counts (updated, merged, removed, unresolved) that
860 Return a tuple of counts (updated, merged, removed, unresolved) that
858 describes how many files were affected by the update.
861 describes how many files were affected by the update.
859 """
862 """
860
863
861 updated, merged, removed, unresolved = 0, 0, 0, 0
864 updated, merged, removed, unresolved = 0, 0, 0, 0
862 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
865 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
863 moves = []
866 moves = []
864 for m, l in actions.items():
867 for m, l in actions.items():
865 l.sort()
868 l.sort()
866
869
867 # prescan for merges
870 # prescan for merges
868 for f, args, msg in actions['m']:
871 for f, args, msg in actions['m']:
869 f1, f2, fa, move, anc = args
872 f1, f2, fa, move, anc = args
870 if f == '.hgsubstate': # merged internally
873 if f == '.hgsubstate': # merged internally
871 continue
874 continue
872 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
875 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
873 fcl = wctx[f1]
876 fcl = wctx[f1]
874 fco = mctx[f2]
877 fco = mctx[f2]
875 actx = repo[anc]
878 actx = repo[anc]
876 if fa in actx:
879 if fa in actx:
877 fca = actx[fa]
880 fca = actx[fa]
878 else:
881 else:
879 fca = repo.filectx(f1, fileid=nullrev)
882 fca = repo.filectx(f1, fileid=nullrev)
880 ms.add(fcl, fco, fca, f)
883 ms.add(fcl, fco, fca, f)
881 if f1 != f and move:
884 if f1 != f and move:
882 moves.append(f1)
885 moves.append(f1)
883
886
884 audit = repo.wvfs.audit
887 audit = repo.wvfs.audit
885 _updating = _('updating')
888 _updating = _('updating')
886 _files = _('files')
889 _files = _('files')
887 progress = repo.ui.progress
890 progress = repo.ui.progress
888
891
889 # remove renamed files after safely stored
892 # remove renamed files after safely stored
890 for f in moves:
893 for f in moves:
891 if os.path.lexists(repo.wjoin(f)):
894 if os.path.lexists(repo.wjoin(f)):
892 repo.ui.debug("removing %s\n" % f)
895 repo.ui.debug("removing %s\n" % f)
893 audit(f)
896 audit(f)
894 util.unlinkpath(repo.wjoin(f))
897 util.unlinkpath(repo.wjoin(f))
895
898
896 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
899 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
897
900
898 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
901 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
899 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
902 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
900
903
901 # remove in parallel (must come first)
904 # remove in parallel (must come first)
902 z = 0
905 z = 0
903 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
906 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
904 for i, item in prog:
907 for i, item in prog:
905 z += i
908 z += i
906 progress(_updating, z, item=item, total=numupdates, unit=_files)
909 progress(_updating, z, item=item, total=numupdates, unit=_files)
907 removed = len(actions['r'])
910 removed = len(actions['r'])
908
911
909 # get in parallel
912 # get in parallel
910 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
913 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
911 for i, item in prog:
914 for i, item in prog:
912 z += i
915 z += i
913 progress(_updating, z, item=item, total=numupdates, unit=_files)
916 progress(_updating, z, item=item, total=numupdates, unit=_files)
914 updated = len(actions['g'])
917 updated = len(actions['g'])
915
918
916 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
919 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
917 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
920 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
918
921
919 # forget (manifest only, just log it) (must come first)
922 # forget (manifest only, just log it) (must come first)
920 for f, args, msg in actions['f']:
923 for f, args, msg in actions['f']:
921 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
924 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
922 z += 1
925 z += 1
923 progress(_updating, z, item=f, total=numupdates, unit=_files)
926 progress(_updating, z, item=f, total=numupdates, unit=_files)
924
927
925 # re-add (manifest only, just log it)
928 # re-add (manifest only, just log it)
926 for f, args, msg in actions['a']:
929 for f, args, msg in actions['a']:
927 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
930 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
928 z += 1
931 z += 1
929 progress(_updating, z, item=f, total=numupdates, unit=_files)
932 progress(_updating, z, item=f, total=numupdates, unit=_files)
930
933
931 # keep (noop, just log it)
934 # keep (noop, just log it)
932 for f, args, msg in actions['k']:
935 for f, args, msg in actions['k']:
933 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
936 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
934 # no progress
937 # no progress
935
938
936 # directory rename, move local
939 # directory rename, move local
937 for f, args, msg in actions['dm']:
940 for f, args, msg in actions['dm']:
938 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
941 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
939 z += 1
942 z += 1
940 progress(_updating, z, item=f, total=numupdates, unit=_files)
943 progress(_updating, z, item=f, total=numupdates, unit=_files)
941 f0, flags = args
944 f0, flags = args
942 repo.ui.note(_("moving %s to %s\n") % (f0, f))
945 repo.ui.note(_("moving %s to %s\n") % (f0, f))
943 audit(f)
946 audit(f)
944 repo.wwrite(f, wctx.filectx(f0).data(), flags)
947 repo.wwrite(f, wctx.filectx(f0).data(), flags)
945 util.unlinkpath(repo.wjoin(f0))
948 util.unlinkpath(repo.wjoin(f0))
946 updated += 1
949 updated += 1
947
950
948 # local directory rename, get
951 # local directory rename, get
949 for f, args, msg in actions['dg']:
952 for f, args, msg in actions['dg']:
950 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
953 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
951 z += 1
954 z += 1
952 progress(_updating, z, item=f, total=numupdates, unit=_files)
955 progress(_updating, z, item=f, total=numupdates, unit=_files)
953 f0, flags = args
956 f0, flags = args
954 repo.ui.note(_("getting %s to %s\n") % (f0, f))
957 repo.ui.note(_("getting %s to %s\n") % (f0, f))
955 repo.wwrite(f, mctx.filectx(f0).data(), flags)
958 repo.wwrite(f, mctx.filectx(f0).data(), flags)
956 updated += 1
959 updated += 1
957
960
958 # exec
961 # exec
959 for f, args, msg in actions['e']:
962 for f, args, msg in actions['e']:
960 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
963 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
961 z += 1
964 z += 1
962 progress(_updating, z, item=f, total=numupdates, unit=_files)
965 progress(_updating, z, item=f, total=numupdates, unit=_files)
963 flags, = args
966 flags, = args
964 audit(f)
967 audit(f)
965 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
968 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
966 updated += 1
969 updated += 1
967
970
968 mergeactions = actions['m']
971 mergeactions = actions['m']
969 # the ordering is important here -- ms.mergedriver will raise if the merge
972 # the ordering is important here -- ms.mergedriver will raise if the merge
970 # driver has changed, and we want to be able to bypass it when overwrite is
973 # driver has changed, and we want to be able to bypass it when overwrite is
971 # True
974 # True
972 usemergedriver = not overwrite and mergeactions and ms.mergedriver
975 usemergedriver = not overwrite and mergeactions and ms.mergedriver
973
976
974 if usemergedriver:
977 if usemergedriver:
975 ms.commit()
978 ms.commit()
976 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
979 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
977 # the driver might leave some files unresolved
980 # the driver might leave some files unresolved
978 unresolvedf = set(ms.unresolved())
981 unresolvedf = set(ms.unresolved())
979 if not proceed:
982 if not proceed:
980 # XXX setting unresolved to at least 1 is a hack to make sure we
983 # XXX setting unresolved to at least 1 is a hack to make sure we
981 # error out
984 # error out
982 return updated, merged, removed, max(len(unresolvedf), 1)
985 return updated, merged, removed, max(len(unresolvedf), 1)
983 newactions = []
986 newactions = []
984 for f, args, msg in mergeactions:
987 for f, args, msg in mergeactions:
985 if f in unresolvedf:
988 if f in unresolvedf:
986 newactions.append((f, args, msg))
989 newactions.append((f, args, msg))
987 mergeactions = newactions
990 mergeactions = newactions
988
991
989 # premerge
992 # premerge
990 tocomplete = []
993 tocomplete = []
991 for f, args, msg in mergeactions:
994 for f, args, msg in mergeactions:
992 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
995 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
993 z += 1
996 z += 1
994 progress(_updating, z, item=f, total=numupdates, unit=_files)
997 progress(_updating, z, item=f, total=numupdates, unit=_files)
995 if f == '.hgsubstate': # subrepo states need updating
998 if f == '.hgsubstate': # subrepo states need updating
996 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
999 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
997 overwrite)
1000 overwrite)
998 continue
1001 continue
999 audit(f)
1002 audit(f)
1000 complete, r = ms.preresolve(f, wctx, labels=labels)
1003 complete, r = ms.preresolve(f, wctx, labels=labels)
1001 if complete:
1004 if complete:
1002 if r is not None and r > 0:
1005 if r is not None and r > 0:
1003 unresolved += 1
1006 unresolved += 1
1004 else:
1007 else:
1005 if r is None:
1008 if r is None:
1006 updated += 1
1009 updated += 1
1007 else:
1010 else:
1008 merged += 1
1011 merged += 1
1009 else:
1012 else:
1010 numupdates += 1
1013 numupdates += 1
1011 tocomplete.append((f, args, msg))
1014 tocomplete.append((f, args, msg))
1012
1015
1013 # merge
1016 # merge
1014 for f, args, msg in tocomplete:
1017 for f, args, msg in tocomplete:
1015 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1018 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1016 z += 1
1019 z += 1
1017 progress(_updating, z, item=f, total=numupdates, unit=_files)
1020 progress(_updating, z, item=f, total=numupdates, unit=_files)
1018 r = ms.resolve(f, wctx, labels=labels)
1021 r = ms.resolve(f, wctx, labels=labels)
1019 if r is not None and r > 0:
1022 if r is not None and r > 0:
1020 unresolved += 1
1023 unresolved += 1
1021 else:
1024 else:
1022 if r is None:
1025 if r is None:
1023 updated += 1
1026 updated += 1
1024 else:
1027 else:
1025 merged += 1
1028 merged += 1
1026
1029
1027 ms.commit()
1030 ms.commit()
1028
1031
1029 if usemergedriver and not unresolved and ms.mdstate() != 's':
1032 if usemergedriver and not unresolved and ms.mdstate() != 's':
1030 if not driverconclude(repo, ms, wctx, labels=labels):
1033 if not driverconclude(repo, ms, wctx, labels=labels):
1031 # XXX setting unresolved to at least 1 is a hack to make sure we
1034 # XXX setting unresolved to at least 1 is a hack to make sure we
1032 # error out
1035 # error out
1033 unresolved = max(unresolved, 1)
1036 unresolved = max(unresolved, 1)
1034
1037
1035 ms.commit()
1038 ms.commit()
1036
1039
1037 progress(_updating, None, total=numupdates, unit=_files)
1040 progress(_updating, None, total=numupdates, unit=_files)
1038
1041
1039 return updated, merged, removed, unresolved
1042 return updated, merged, removed, unresolved
1040
1043
1041 def recordupdates(repo, actions, branchmerge):
1044 def recordupdates(repo, actions, branchmerge):
1042 "record merge actions to the dirstate"
1045 "record merge actions to the dirstate"
1043 # remove (must come first)
1046 # remove (must come first)
1044 for f, args, msg in actions['r']:
1047 for f, args, msg in actions['r']:
1045 if branchmerge:
1048 if branchmerge:
1046 repo.dirstate.remove(f)
1049 repo.dirstate.remove(f)
1047 else:
1050 else:
1048 repo.dirstate.drop(f)
1051 repo.dirstate.drop(f)
1049
1052
1050 # forget (must come first)
1053 # forget (must come first)
1051 for f, args, msg in actions['f']:
1054 for f, args, msg in actions['f']:
1052 repo.dirstate.drop(f)
1055 repo.dirstate.drop(f)
1053
1056
1054 # re-add
1057 # re-add
1055 for f, args, msg in actions['a']:
1058 for f, args, msg in actions['a']:
1056 if not branchmerge:
1059 if not branchmerge:
1057 repo.dirstate.add(f)
1060 repo.dirstate.add(f)
1058
1061
1059 # exec change
1062 # exec change
1060 for f, args, msg in actions['e']:
1063 for f, args, msg in actions['e']:
1061 repo.dirstate.normallookup(f)
1064 repo.dirstate.normallookup(f)
1062
1065
1063 # keep
1066 # keep
1064 for f, args, msg in actions['k']:
1067 for f, args, msg in actions['k']:
1065 pass
1068 pass
1066
1069
1067 # get
1070 # get
1068 for f, args, msg in actions['g']:
1071 for f, args, msg in actions['g']:
1069 if branchmerge:
1072 if branchmerge:
1070 repo.dirstate.otherparent(f)
1073 repo.dirstate.otherparent(f)
1071 else:
1074 else:
1072 repo.dirstate.normal(f)
1075 repo.dirstate.normal(f)
1073
1076
1074 # merge
1077 # merge
1075 for f, args, msg in actions['m']:
1078 for f, args, msg in actions['m']:
1076 f1, f2, fa, move, anc = args
1079 f1, f2, fa, move, anc = args
1077 if branchmerge:
1080 if branchmerge:
1078 # We've done a branch merge, mark this file as merged
1081 # We've done a branch merge, mark this file as merged
1079 # so that we properly record the merger later
1082 # so that we properly record the merger later
1080 repo.dirstate.merge(f)
1083 repo.dirstate.merge(f)
1081 if f1 != f2: # copy/rename
1084 if f1 != f2: # copy/rename
1082 if move:
1085 if move:
1083 repo.dirstate.remove(f1)
1086 repo.dirstate.remove(f1)
1084 if f1 != f:
1087 if f1 != f:
1085 repo.dirstate.copy(f1, f)
1088 repo.dirstate.copy(f1, f)
1086 else:
1089 else:
1087 repo.dirstate.copy(f2, f)
1090 repo.dirstate.copy(f2, f)
1088 else:
1091 else:
1089 # We've update-merged a locally modified file, so
1092 # We've update-merged a locally modified file, so
1090 # we set the dirstate to emulate a normal checkout
1093 # we set the dirstate to emulate a normal checkout
1091 # of that file some time in the past. Thus our
1094 # of that file some time in the past. Thus our
1092 # merge will appear as a normal local file
1095 # merge will appear as a normal local file
1093 # modification.
1096 # modification.
1094 if f2 == f: # file not locally copied/moved
1097 if f2 == f: # file not locally copied/moved
1095 repo.dirstate.normallookup(f)
1098 repo.dirstate.normallookup(f)
1096 if move:
1099 if move:
1097 repo.dirstate.drop(f1)
1100 repo.dirstate.drop(f1)
1098
1101
1099 # directory rename, move local
1102 # directory rename, move local
1100 for f, args, msg in actions['dm']:
1103 for f, args, msg in actions['dm']:
1101 f0, flag = args
1104 f0, flag = args
1102 if branchmerge:
1105 if branchmerge:
1103 repo.dirstate.add(f)
1106 repo.dirstate.add(f)
1104 repo.dirstate.remove(f0)
1107 repo.dirstate.remove(f0)
1105 repo.dirstate.copy(f0, f)
1108 repo.dirstate.copy(f0, f)
1106 else:
1109 else:
1107 repo.dirstate.normal(f)
1110 repo.dirstate.normal(f)
1108 repo.dirstate.drop(f0)
1111 repo.dirstate.drop(f0)
1109
1112
1110 # directory rename, get
1113 # directory rename, get
1111 for f, args, msg in actions['dg']:
1114 for f, args, msg in actions['dg']:
1112 f0, flag = args
1115 f0, flag = args
1113 if branchmerge:
1116 if branchmerge:
1114 repo.dirstate.add(f)
1117 repo.dirstate.add(f)
1115 repo.dirstate.copy(f0, f)
1118 repo.dirstate.copy(f0, f)
1116 else:
1119 else:
1117 repo.dirstate.normal(f)
1120 repo.dirstate.normal(f)
1118
1121
1119 def update(repo, node, branchmerge, force, partial, ancestor=None,
1122 def update(repo, node, branchmerge, force, partial, ancestor=None,
1120 mergeancestor=False, labels=None):
1123 mergeancestor=False, labels=None):
1121 """
1124 """
1122 Perform a merge between the working directory and the given node
1125 Perform a merge between the working directory and the given node
1123
1126
1124 node = the node to update to, or None if unspecified
1127 node = the node to update to, or None if unspecified
1125 branchmerge = whether to merge between branches
1128 branchmerge = whether to merge between branches
1126 force = whether to force branch merging or file overwriting
1129 force = whether to force branch merging or file overwriting
1127 partial = a function to filter file lists (dirstate not updated)
1130 partial = a function to filter file lists (dirstate not updated)
1128 mergeancestor = whether it is merging with an ancestor. If true,
1131 mergeancestor = whether it is merging with an ancestor. If true,
1129 we should accept the incoming changes for any prompts that occur.
1132 we should accept the incoming changes for any prompts that occur.
1130 If false, merging with an ancestor (fast-forward) is only allowed
1133 If false, merging with an ancestor (fast-forward) is only allowed
1131 between different named branches. This flag is used by rebase extension
1134 between different named branches. This flag is used by rebase extension
1132 as a temporary fix and should be avoided in general.
1135 as a temporary fix and should be avoided in general.
1133
1136
1134 The table below shows all the behaviors of the update command
1137 The table below shows all the behaviors of the update command
1135 given the -c and -C or no options, whether the working directory
1138 given the -c and -C or no options, whether the working directory
1136 is dirty, whether a revision is specified, and the relationship of
1139 is dirty, whether a revision is specified, and the relationship of
1137 the parent rev to the target rev (linear, on the same named
1140 the parent rev to the target rev (linear, on the same named
1138 branch, or on another named branch).
1141 branch, or on another named branch).
1139
1142
1140 This logic is tested by test-update-branches.t.
1143 This logic is tested by test-update-branches.t.
1141
1144
1142 -c -C dirty rev | linear same cross
1145 -c -C dirty rev | linear same cross
1143 n n n n | ok (1) x
1146 n n n n | ok (1) x
1144 n n n y | ok ok ok
1147 n n n y | ok ok ok
1145 n n y n | merge (2) (2)
1148 n n y n | merge (2) (2)
1146 n n y y | merge (3) (3)
1149 n n y y | merge (3) (3)
1147 n y * * | discard discard discard
1150 n y * * | discard discard discard
1148 y n y * | (4) (4) (4)
1151 y n y * | (4) (4) (4)
1149 y n n * | ok ok ok
1152 y n n * | ok ok ok
1150 y y * * | (5) (5) (5)
1153 y y * * | (5) (5) (5)
1151
1154
1152 x = can't happen
1155 x = can't happen
1153 * = don't-care
1156 * = don't-care
1154 1 = abort: not a linear update (merge or update --check to force update)
1157 1 = abort: not a linear update (merge or update --check to force update)
1155 2 = abort: uncommitted changes (commit and merge, or update --clean to
1158 2 = abort: uncommitted changes (commit and merge, or update --clean to
1156 discard changes)
1159 discard changes)
1157 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1160 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1158 4 = abort: uncommitted changes (checked in commands.py)
1161 4 = abort: uncommitted changes (checked in commands.py)
1159 5 = incompatible options (checked in commands.py)
1162 5 = incompatible options (checked in commands.py)
1160
1163
1161 Return the same tuple as applyupdates().
1164 Return the same tuple as applyupdates().
1162 """
1165 """
1163
1166
1164 onode = node
1167 onode = node
1165 wlock = repo.wlock()
1168 wlock = repo.wlock()
1166 try:
1169 try:
1167 wc = repo[None]
1170 wc = repo[None]
1168 pl = wc.parents()
1171 pl = wc.parents()
1169 p1 = pl[0]
1172 p1 = pl[0]
1170 pas = [None]
1173 pas = [None]
1171 if ancestor is not None:
1174 if ancestor is not None:
1172 pas = [repo[ancestor]]
1175 pas = [repo[ancestor]]
1173
1176
1174 if node is None:
1177 if node is None:
1175 if (repo.ui.configbool('devel', 'all-warnings')
1178 if (repo.ui.configbool('devel', 'all-warnings')
1176 or repo.ui.configbool('devel', 'oldapi')):
1179 or repo.ui.configbool('devel', 'oldapi')):
1177 repo.ui.develwarn('update with no target')
1180 repo.ui.develwarn('update with no target')
1178 rev, _mark, _act = destutil.destupdate(repo)
1181 rev, _mark, _act = destutil.destupdate(repo)
1179 node = repo[rev].node()
1182 node = repo[rev].node()
1180
1183
1181 overwrite = force and not branchmerge
1184 overwrite = force and not branchmerge
1182
1185
1183 p2 = repo[node]
1186 p2 = repo[node]
1184 if pas[0] is None:
1187 if pas[0] is None:
1185 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1188 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1186 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1189 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1187 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1190 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1188 else:
1191 else:
1189 pas = [p1.ancestor(p2, warn=branchmerge)]
1192 pas = [p1.ancestor(p2, warn=branchmerge)]
1190
1193
1191 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1194 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1192
1195
1193 ### check phase
1196 ### check phase
1194 if not overwrite and len(pl) > 1:
1197 if not overwrite and len(pl) > 1:
1195 raise error.Abort(_("outstanding uncommitted merge"))
1198 raise error.Abort(_("outstanding uncommitted merge"))
1196 if branchmerge:
1199 if branchmerge:
1197 if pas == [p2]:
1200 if pas == [p2]:
1198 raise error.Abort(_("merging with a working directory ancestor"
1201 raise error.Abort(_("merging with a working directory ancestor"
1199 " has no effect"))
1202 " has no effect"))
1200 elif pas == [p1]:
1203 elif pas == [p1]:
1201 if not mergeancestor and p1.branch() == p2.branch():
1204 if not mergeancestor and p1.branch() == p2.branch():
1202 raise error.Abort(_("nothing to merge"),
1205 raise error.Abort(_("nothing to merge"),
1203 hint=_("use 'hg update' "
1206 hint=_("use 'hg update' "
1204 "or check 'hg heads'"))
1207 "or check 'hg heads'"))
1205 if not force and (wc.files() or wc.deleted()):
1208 if not force and (wc.files() or wc.deleted()):
1206 raise error.Abort(_("uncommitted changes"),
1209 raise error.Abort(_("uncommitted changes"),
1207 hint=_("use 'hg status' to list changes"))
1210 hint=_("use 'hg status' to list changes"))
1208 for s in sorted(wc.substate):
1211 for s in sorted(wc.substate):
1209 wc.sub(s).bailifchanged()
1212 wc.sub(s).bailifchanged()
1210
1213
1211 elif not overwrite:
1214 elif not overwrite:
1212 if p1 == p2: # no-op update
1215 if p1 == p2: # no-op update
1213 # call the hooks and exit early
1216 # call the hooks and exit early
1214 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1217 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1215 repo.hook('update', parent1=xp2, parent2='', error=0)
1218 repo.hook('update', parent1=xp2, parent2='', error=0)
1216 return 0, 0, 0, 0
1219 return 0, 0, 0, 0
1217
1220
1218 if pas not in ([p1], [p2]): # nonlinear
1221 if pas not in ([p1], [p2]): # nonlinear
1219 dirty = wc.dirty(missing=True)
1222 dirty = wc.dirty(missing=True)
1220 if dirty or onode is None:
1223 if dirty or onode is None:
1221 # Branching is a bit strange to ensure we do the minimal
1224 # Branching is a bit strange to ensure we do the minimal
1222 # amount of call to obsolete.background.
1225 # amount of call to obsolete.background.
1223 foreground = obsolete.foreground(repo, [p1.node()])
1226 foreground = obsolete.foreground(repo, [p1.node()])
1224 # note: the <node> variable contains a random identifier
1227 # note: the <node> variable contains a random identifier
1225 if repo[node].node() in foreground:
1228 if repo[node].node() in foreground:
1226 pas = [p1] # allow updating to successors
1229 pas = [p1] # allow updating to successors
1227 elif dirty:
1230 elif dirty:
1228 msg = _("uncommitted changes")
1231 msg = _("uncommitted changes")
1229 if onode is None:
1232 if onode is None:
1230 hint = _("commit and merge, or update --clean to"
1233 hint = _("commit and merge, or update --clean to"
1231 " discard changes")
1234 " discard changes")
1232 else:
1235 else:
1233 hint = _("commit or update --clean to discard"
1236 hint = _("commit or update --clean to discard"
1234 " changes")
1237 " changes")
1235 raise error.Abort(msg, hint=hint)
1238 raise error.Abort(msg, hint=hint)
1236 else: # node is none
1239 else: # node is none
1237 msg = _("not a linear update")
1240 msg = _("not a linear update")
1238 hint = _("merge or update --check to force update")
1241 hint = _("merge or update --check to force update")
1239 raise error.Abort(msg, hint=hint)
1242 raise error.Abort(msg, hint=hint)
1240 else:
1243 else:
1241 # Allow jumping branches if clean and specific rev given
1244 # Allow jumping branches if clean and specific rev given
1242 pas = [p1]
1245 pas = [p1]
1243
1246
1244 # deprecated config: merge.followcopies
1247 # deprecated config: merge.followcopies
1245 followcopies = False
1248 followcopies = False
1246 if overwrite:
1249 if overwrite:
1247 pas = [wc]
1250 pas = [wc]
1248 elif pas == [p2]: # backwards
1251 elif pas == [p2]: # backwards
1249 pas = [wc.p1()]
1252 pas = [wc.p1()]
1250 elif not branchmerge and not wc.dirty(missing=True):
1253 elif not branchmerge and not wc.dirty(missing=True):
1251 pass
1254 pass
1252 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1255 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1253 followcopies = True
1256 followcopies = True
1254
1257
1255 ### calculate phase
1258 ### calculate phase
1256 actionbyfile, diverge, renamedelete = calculateupdates(
1259 actionbyfile, diverge, renamedelete = calculateupdates(
1257 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1260 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1258 followcopies)
1261 followcopies)
1259 # Convert to dictionary-of-lists format
1262 # Convert to dictionary-of-lists format
1260 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1263 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1261 for f, (m, args, msg) in actionbyfile.iteritems():
1264 for f, (m, args, msg) in actionbyfile.iteritems():
1262 if m not in actions:
1265 if m not in actions:
1263 actions[m] = []
1266 actions[m] = []
1264 actions[m].append((f, args, msg))
1267 actions[m].append((f, args, msg))
1265
1268
1266 if not util.checkcase(repo.path):
1269 if not util.checkcase(repo.path):
1267 # check collision between files only in p2 for clean update
1270 # check collision between files only in p2 for clean update
1268 if (not branchmerge and
1271 if (not branchmerge and
1269 (force or not wc.dirty(missing=True, branch=False))):
1272 (force or not wc.dirty(missing=True, branch=False))):
1270 _checkcollision(repo, p2.manifest(), None)
1273 _checkcollision(repo, p2.manifest(), None)
1271 else:
1274 else:
1272 _checkcollision(repo, wc.manifest(), actions)
1275 _checkcollision(repo, wc.manifest(), actions)
1273
1276
1274 # Prompt and create actions. TODO: Move this towards resolve phase.
1277 # Prompt and create actions. TODO: Move this towards resolve phase.
1275 for f, args, msg in sorted(actions['cd']):
1278 for f, args, msg in sorted(actions['cd']):
1276 if repo.ui.promptchoice(
1279 if repo.ui.promptchoice(
1277 _("local changed %s which remote deleted\n"
1280 _("local changed %s which remote deleted\n"
1278 "use (c)hanged version or (d)elete?"
1281 "use (c)hanged version or (d)elete?"
1279 "$$ &Changed $$ &Delete") % f, 0):
1282 "$$ &Changed $$ &Delete") % f, 0):
1280 actions['r'].append((f, None, "prompt delete"))
1283 actions['r'].append((f, None, "prompt delete"))
1281 else:
1284 else:
1282 actions['a'].append((f, None, "prompt keep"))
1285 actions['a'].append((f, None, "prompt keep"))
1283
1286
1284 for f, args, msg in sorted(actions['dc']):
1287 for f, args, msg in sorted(actions['dc']):
1285 f1, f2, fa, move, anc = args
1288 f1, f2, fa, move, anc = args
1286 flags = p2[f2].flags()
1289 flags = p2[f2].flags()
1287 if repo.ui.promptchoice(
1290 if repo.ui.promptchoice(
1288 _("remote changed %s which local deleted\n"
1291 _("remote changed %s which local deleted\n"
1289 "use (c)hanged version or leave (d)eleted?"
1292 "use (c)hanged version or leave (d)eleted?"
1290 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1293 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1291 actions['g'].append((f, (flags,), "prompt recreating"))
1294 actions['g'].append((f, (flags,), "prompt recreating"))
1292
1295
1293 # divergent renames
1296 # divergent renames
1294 for f, fl in sorted(diverge.iteritems()):
1297 for f, fl in sorted(diverge.iteritems()):
1295 repo.ui.warn(_("note: possible conflict - %s was renamed "
1298 repo.ui.warn(_("note: possible conflict - %s was renamed "
1296 "multiple times to:\n") % f)
1299 "multiple times to:\n") % f)
1297 for nf in fl:
1300 for nf in fl:
1298 repo.ui.warn(" %s\n" % nf)
1301 repo.ui.warn(" %s\n" % nf)
1299
1302
1300 # rename and delete
1303 # rename and delete
1301 for f, fl in sorted(renamedelete.iteritems()):
1304 for f, fl in sorted(renamedelete.iteritems()):
1302 repo.ui.warn(_("note: possible conflict - %s was deleted "
1305 repo.ui.warn(_("note: possible conflict - %s was deleted "
1303 "and renamed to:\n") % f)
1306 "and renamed to:\n") % f)
1304 for nf in fl:
1307 for nf in fl:
1305 repo.ui.warn(" %s\n" % nf)
1308 repo.ui.warn(" %s\n" % nf)
1306
1309
1307 ### apply phase
1310 ### apply phase
1308 if not branchmerge: # just jump to the new rev
1311 if not branchmerge: # just jump to the new rev
1309 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1312 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1310 if not partial:
1313 if not partial:
1311 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1314 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1312 # note that we're in the middle of an update
1315 # note that we're in the middle of an update
1313 repo.vfs.write('updatestate', p2.hex())
1316 repo.vfs.write('updatestate', p2.hex())
1314
1317
1315 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1318 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1316
1319
1317 if not partial:
1320 if not partial:
1318 repo.dirstate.beginparentchange()
1321 repo.dirstate.beginparentchange()
1319 repo.setparents(fp1, fp2)
1322 repo.setparents(fp1, fp2)
1320 recordupdates(repo, actions, branchmerge)
1323 recordupdates(repo, actions, branchmerge)
1321 # update completed, clear state
1324 # update completed, clear state
1322 util.unlink(repo.join('updatestate'))
1325 util.unlink(repo.join('updatestate'))
1323
1326
1324 if not branchmerge:
1327 if not branchmerge:
1325 repo.dirstate.setbranch(p2.branch())
1328 repo.dirstate.setbranch(p2.branch())
1326 repo.dirstate.endparentchange()
1329 repo.dirstate.endparentchange()
1327 finally:
1330 finally:
1328 wlock.release()
1331 wlock.release()
1329
1332
1330 if not partial:
1333 if not partial:
1331 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1334 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1332 return stats
1335 return stats
1333
1336
1334 def graft(repo, ctx, pctx, labels):
1337 def graft(repo, ctx, pctx, labels):
1335 """Do a graft-like merge.
1338 """Do a graft-like merge.
1336
1339
1337 This is a merge where the merge ancestor is chosen such that one
1340 This is a merge where the merge ancestor is chosen such that one
1338 or more changesets are grafted onto the current changeset. In
1341 or more changesets are grafted onto the current changeset. In
1339 addition to the merge, this fixes up the dirstate to include only
1342 addition to the merge, this fixes up the dirstate to include only
1340 a single parent and tries to duplicate any renames/copies
1343 a single parent and tries to duplicate any renames/copies
1341 appropriately.
1344 appropriately.
1342
1345
1343 ctx - changeset to rebase
1346 ctx - changeset to rebase
1344 pctx - merge base, usually ctx.p1()
1347 pctx - merge base, usually ctx.p1()
1345 labels - merge labels eg ['local', 'graft']
1348 labels - merge labels eg ['local', 'graft']
1346
1349
1347 """
1350 """
1348 # If we're grafting a descendant onto an ancestor, be sure to pass
1351 # If we're grafting a descendant onto an ancestor, be sure to pass
1349 # mergeancestor=True to update. This does two things: 1) allows the merge if
1352 # mergeancestor=True to update. This does two things: 1) allows the merge if
1350 # the destination is the same as the parent of the ctx (so we can use graft
1353 # the destination is the same as the parent of the ctx (so we can use graft
1351 # to copy commits), and 2) informs update that the incoming changes are
1354 # to copy commits), and 2) informs update that the incoming changes are
1352 # newer than the destination so it doesn't prompt about "remote changed foo
1355 # newer than the destination so it doesn't prompt about "remote changed foo
1353 # which local deleted".
1356 # which local deleted".
1354 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1357 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1355
1358
1356 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1359 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1357 mergeancestor=mergeancestor, labels=labels)
1360 mergeancestor=mergeancestor, labels=labels)
1358
1361
1359 # drop the second merge parent
1362 # drop the second merge parent
1360 repo.dirstate.beginparentchange()
1363 repo.dirstate.beginparentchange()
1361 repo.setparents(repo['.'].node(), nullid)
1364 repo.setparents(repo['.'].node(), nullid)
1362 repo.dirstate.write(repo.currenttransaction())
1365 repo.dirstate.write(repo.currenttransaction())
1363 # fix up dirstate for copies and renames
1366 # fix up dirstate for copies and renames
1364 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1367 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1365 repo.dirstate.endparentchange()
1368 repo.dirstate.endparentchange()
1366 return stats
1369 return stats
General Comments 0
You need to be logged in to leave comments. Login now