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