##// END OF EJS Templates
merge: add missing doc for 'labels' parameter
Siddharth Agarwal -
r28019:e81d1179 default
parent child Browse files
Show More
@@ -1,1616 +1,1617 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()
613 warnconflicts = set()
614 abortconflicts = set()
614 abortconflicts = set()
615 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
615 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
616 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
616 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
617 if not force:
617 if not force:
618 def collectconflicts(conflicts, config):
618 def collectconflicts(conflicts, config):
619 if config == 'abort':
619 if config == 'abort':
620 abortconflicts.update(conflicts)
620 abortconflicts.update(conflicts)
621 elif config == 'warn':
621 elif config == 'warn':
622 warnconflicts.update(conflicts)
622 warnconflicts.update(conflicts)
623
623
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
637
638 for f in sorted(abortconflicts):
638 for f in sorted(abortconflicts):
639 repo.ui.warn(_("%s: untracked file differs\n") % f)
639 repo.ui.warn(_("%s: untracked file differs\n") % f)
640 if abortconflicts:
640 if abortconflicts:
641 raise error.Abort(_("untracked files in working directory "
641 raise error.Abort(_("untracked files in working directory "
642 "differ from files in requested revision"))
642 "differ from files in requested revision"))
643
643
644 for f in sorted(warnconflicts):
644 for f in sorted(warnconflicts):
645 repo.ui.warn(_("%s: replacing untracked file\n") % f)
645 repo.ui.warn(_("%s: replacing untracked file\n") % f)
646
646
647 for f, (m, args, msg) in actions.iteritems():
647 for f, (m, args, msg) in actions.iteritems():
648 backup = f in conflicts
648 backup = f in conflicts
649 if m == 'c':
649 if m == 'c':
650 flags, = args
650 flags, = args
651 actions[f] = ('g', (flags, backup), msg)
651 actions[f] = ('g', (flags, backup), msg)
652 elif m == 'cm':
652 elif m == 'cm':
653 fl2, anc = args
653 fl2, anc = args
654 different = _checkunknownfile(repo, wctx, mctx, f)
654 different = _checkunknownfile(repo, wctx, mctx, f)
655 if different:
655 if different:
656 actions[f] = ('m', (f, f, None, False, anc),
656 actions[f] = ('m', (f, f, None, False, anc),
657 "remote differs from untracked local")
657 "remote differs from untracked local")
658 else:
658 else:
659 actions[f] = ('g', (fl2, backup), "remote created")
659 actions[f] = ('g', (fl2, backup), "remote created")
660
660
661 def _forgetremoved(wctx, mctx, branchmerge):
661 def _forgetremoved(wctx, mctx, branchmerge):
662 """
662 """
663 Forget removed files
663 Forget removed files
664
664
665 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
666 neither the working directory nor the target rev has the file,
666 neither the working directory nor the target rev has the file,
667 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
668 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
669 manifest.
669 manifest.
670
670
671 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
672 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
673 as removed.
673 as removed.
674 """
674 """
675
675
676 actions = {}
676 actions = {}
677 m = 'f'
677 m = 'f'
678 if branchmerge:
678 if branchmerge:
679 m = 'r'
679 m = 'r'
680 for f in wctx.deleted():
680 for f in wctx.deleted():
681 if f not in mctx:
681 if f not in mctx:
682 actions[f] = m, None, "forget deleted"
682 actions[f] = m, None, "forget deleted"
683
683
684 if not branchmerge:
684 if not branchmerge:
685 for f in wctx.removed():
685 for f in wctx.removed():
686 if f not in mctx:
686 if f not in mctx:
687 actions[f] = 'f', None, "forget removed"
687 actions[f] = 'f', None, "forget removed"
688
688
689 return actions
689 return actions
690
690
691 def _checkcollision(repo, wmf, actions):
691 def _checkcollision(repo, wmf, actions):
692 # build provisional merged manifest up
692 # build provisional merged manifest up
693 pmmf = set(wmf)
693 pmmf = set(wmf)
694
694
695 if actions:
695 if actions:
696 # k, dr, e and rd are no-op
696 # k, dr, e and rd are no-op
697 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
697 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
698 for f, args, msg in actions[m]:
698 for f, args, msg in actions[m]:
699 pmmf.add(f)
699 pmmf.add(f)
700 for f, args, msg in actions['r']:
700 for f, args, msg in actions['r']:
701 pmmf.discard(f)
701 pmmf.discard(f)
702 for f, args, msg in actions['dm']:
702 for f, args, msg in actions['dm']:
703 f2, flags = args
703 f2, flags = args
704 pmmf.discard(f2)
704 pmmf.discard(f2)
705 pmmf.add(f)
705 pmmf.add(f)
706 for f, args, msg in actions['dg']:
706 for f, args, msg in actions['dg']:
707 pmmf.add(f)
707 pmmf.add(f)
708 for f, args, msg in actions['m']:
708 for f, args, msg in actions['m']:
709 f1, f2, fa, move, anc = args
709 f1, f2, fa, move, anc = args
710 if move:
710 if move:
711 pmmf.discard(f1)
711 pmmf.discard(f1)
712 pmmf.add(f)
712 pmmf.add(f)
713
713
714 # check case-folding collision in provisional merged manifest
714 # check case-folding collision in provisional merged manifest
715 foldmap = {}
715 foldmap = {}
716 for f in sorted(pmmf):
716 for f in sorted(pmmf):
717 fold = util.normcase(f)
717 fold = util.normcase(f)
718 if fold in foldmap:
718 if fold in foldmap:
719 raise error.Abort(_("case-folding collision between %s and %s")
719 raise error.Abort(_("case-folding collision between %s and %s")
720 % (f, foldmap[fold]))
720 % (f, foldmap[fold]))
721 foldmap[fold] = f
721 foldmap[fold] = f
722
722
723 # check case-folding of directories
723 # check case-folding of directories
724 foldprefix = unfoldprefix = lastfull = ''
724 foldprefix = unfoldprefix = lastfull = ''
725 for fold, f in sorted(foldmap.items()):
725 for fold, f in sorted(foldmap.items()):
726 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
726 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
727 # the folded prefix matches but actual casing is different
727 # the folded prefix matches but actual casing is different
728 raise error.Abort(_("case-folding collision between "
728 raise error.Abort(_("case-folding collision between "
729 "%s and directory of %s") % (lastfull, f))
729 "%s and directory of %s") % (lastfull, f))
730 foldprefix = fold + '/'
730 foldprefix = fold + '/'
731 unfoldprefix = f + '/'
731 unfoldprefix = f + '/'
732 lastfull = f
732 lastfull = f
733
733
734 def driverpreprocess(repo, ms, wctx, labels=None):
734 def driverpreprocess(repo, ms, wctx, labels=None):
735 """run the preprocess step of the merge driver, if any
735 """run the preprocess step of the merge driver, if any
736
736
737 This is currently not implemented -- it's an extension point."""
737 This is currently not implemented -- it's an extension point."""
738 return True
738 return True
739
739
740 def driverconclude(repo, ms, wctx, labels=None):
740 def driverconclude(repo, ms, wctx, labels=None):
741 """run the conclude step of the merge driver, if any
741 """run the conclude step of the merge driver, if any
742
742
743 This is currently not implemented -- it's an extension point."""
743 This is currently not implemented -- it's an extension point."""
744 return True
744 return True
745
745
746 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
746 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
747 acceptremote, followcopies):
747 acceptremote, followcopies):
748 """
748 """
749 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
750
750
751 branchmerge and force are as passed in to update
751 branchmerge and force are as passed in to update
752 matcher = matcher to filter file lists
752 matcher = matcher to filter file lists
753 acceptremote = accept the incoming changes without prompting
753 acceptremote = accept the incoming changes without prompting
754 """
754 """
755 if matcher is not None and matcher.always():
755 if matcher is not None and matcher.always():
756 matcher = None
756 matcher = None
757
757
758 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
758 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
759
759
760 # 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
761 [x.manifest() for x in
761 [x.manifest() for x in
762 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
762 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
763
763
764 if followcopies:
764 if followcopies:
765 ret = copies.mergecopies(repo, wctx, p2, pa)
765 ret = copies.mergecopies(repo, wctx, p2, pa)
766 copy, movewithdir, diverge, renamedelete = ret
766 copy, movewithdir, diverge, renamedelete = ret
767
767
768 repo.ui.note(_("resolving manifests\n"))
768 repo.ui.note(_("resolving manifests\n"))
769 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
769 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
770 % (bool(branchmerge), bool(force), bool(matcher)))
770 % (bool(branchmerge), bool(force), bool(matcher)))
771 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))
772
772
773 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
773 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
774 copied = set(copy.values())
774 copied = set(copy.values())
775 copied.update(movewithdir.values())
775 copied.update(movewithdir.values())
776
776
777 if '.hgsubstate' in m1:
777 if '.hgsubstate' in m1:
778 # check whether sub state is modified
778 # check whether sub state is modified
779 for s in sorted(wctx.substate):
779 for s in sorted(wctx.substate):
780 if wctx.sub(s).dirty():
780 if wctx.sub(s).dirty():
781 m1['.hgsubstate'] += '+'
781 m1['.hgsubstate'] += '+'
782 break
782 break
783
783
784 # Compare manifests
784 # Compare manifests
785 if matcher is not None:
785 if matcher is not None:
786 m1 = m1.matches(matcher)
786 m1 = m1.matches(matcher)
787 m2 = m2.matches(matcher)
787 m2 = m2.matches(matcher)
788 diff = m1.diff(m2)
788 diff = m1.diff(m2)
789
789
790 actions = {}
790 actions = {}
791 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
791 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
792 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
793 if f not in ma:
793 if f not in ma:
794 fa = copy.get(f, None)
794 fa = copy.get(f, None)
795 if fa is not None:
795 if fa is not None:
796 actions[f] = ('m', (f, f, fa, False, pa.node()),
796 actions[f] = ('m', (f, f, fa, False, pa.node()),
797 "both renamed from " + fa)
797 "both renamed from " + fa)
798 else:
798 else:
799 actions[f] = ('m', (f, f, None, False, pa.node()),
799 actions[f] = ('m', (f, f, None, False, pa.node()),
800 "both created")
800 "both created")
801 else:
801 else:
802 a = ma[f]
802 a = ma[f]
803 fla = ma.flags(f)
803 fla = ma.flags(f)
804 nol = 'l' not in fl1 + fl2 + fla
804 nol = 'l' not in fl1 + fl2 + fla
805 if n2 == a and fl2 == fla:
805 if n2 == a and fl2 == fla:
806 actions[f] = ('k' , (), "remote unchanged")
806 actions[f] = ('k' , (), "remote unchanged")
807 elif n1 == a and fl1 == fla: # local unchanged - use remote
807 elif n1 == a and fl1 == fla: # local unchanged - use remote
808 if n1 == n2: # optimization: keep local content
808 if n1 == n2: # optimization: keep local content
809 actions[f] = ('e', (fl2,), "update permissions")
809 actions[f] = ('e', (fl2,), "update permissions")
810 else:
810 else:
811 actions[f] = ('g', (fl2, False), "remote is newer")
811 actions[f] = ('g', (fl2, False), "remote is newer")
812 elif nol and n2 == a: # remote only changed 'x'
812 elif nol and n2 == a: # remote only changed 'x'
813 actions[f] = ('e', (fl2,), "update permissions")
813 actions[f] = ('e', (fl2,), "update permissions")
814 elif nol and n1 == a: # local only changed 'x'
814 elif nol and n1 == a: # local only changed 'x'
815 actions[f] = ('g', (fl1, False), "remote is newer")
815 actions[f] = ('g', (fl1, False), "remote is newer")
816 else: # both changed something
816 else: # both changed something
817 actions[f] = ('m', (f, f, f, False, pa.node()),
817 actions[f] = ('m', (f, f, f, False, pa.node()),
818 "versions differ")
818 "versions differ")
819 elif n1: # file exists only on local side
819 elif n1: # file exists only on local side
820 if f in copied:
820 if f in copied:
821 pass # we'll deal with it on m2 side
821 pass # we'll deal with it on m2 side
822 elif f in movewithdir: # directory rename, move local
822 elif f in movewithdir: # directory rename, move local
823 f2 = movewithdir[f]
823 f2 = movewithdir[f]
824 if f2 in m2:
824 if f2 in m2:
825 actions[f2] = ('m', (f, f2, None, True, pa.node()),
825 actions[f2] = ('m', (f, f2, None, True, pa.node()),
826 "remote directory rename, both created")
826 "remote directory rename, both created")
827 else:
827 else:
828 actions[f2] = ('dm', (f, fl1),
828 actions[f2] = ('dm', (f, fl1),
829 "remote directory rename - move from " + f)
829 "remote directory rename - move from " + f)
830 elif f in copy:
830 elif f in copy:
831 f2 = copy[f]
831 f2 = copy[f]
832 actions[f] = ('m', (f, f2, f2, False, pa.node()),
832 actions[f] = ('m', (f, f2, f2, False, pa.node()),
833 "local copied/moved from " + f2)
833 "local copied/moved from " + f2)
834 elif f in ma: # clean, a different, no remote
834 elif f in ma: # clean, a different, no remote
835 if n1 != ma[f]:
835 if n1 != ma[f]:
836 if acceptremote:
836 if acceptremote:
837 actions[f] = ('r', None, "remote delete")
837 actions[f] = ('r', None, "remote delete")
838 else:
838 else:
839 actions[f] = ('cd', (f, None, f, False, pa.node()),
839 actions[f] = ('cd', (f, None, f, False, pa.node()),
840 "prompt changed/deleted")
840 "prompt changed/deleted")
841 elif n1[20:] == 'a':
841 elif n1[20:] == 'a':
842 # This extra 'a' is added by working copy manifest to mark
842 # This extra 'a' is added by working copy manifest to mark
843 # the file as locally added. We should forget it instead of
843 # the file as locally added. We should forget it instead of
844 # deleting it.
844 # deleting it.
845 actions[f] = ('f', None, "remote deleted")
845 actions[f] = ('f', None, "remote deleted")
846 else:
846 else:
847 actions[f] = ('r', None, "other deleted")
847 actions[f] = ('r', None, "other deleted")
848 elif n2: # file exists only on remote side
848 elif n2: # file exists only on remote side
849 if f in copied:
849 if f in copied:
850 pass # we'll deal with it on m1 side
850 pass # we'll deal with it on m1 side
851 elif f in movewithdir:
851 elif f in movewithdir:
852 f2 = movewithdir[f]
852 f2 = movewithdir[f]
853 if f2 in m1:
853 if f2 in m1:
854 actions[f2] = ('m', (f2, f, None, False, pa.node()),
854 actions[f2] = ('m', (f2, f, None, False, pa.node()),
855 "local directory rename, both created")
855 "local directory rename, both created")
856 else:
856 else:
857 actions[f2] = ('dg', (f, fl2),
857 actions[f2] = ('dg', (f, fl2),
858 "local directory rename - get from " + f)
858 "local directory rename - get from " + f)
859 elif f in copy:
859 elif f in copy:
860 f2 = copy[f]
860 f2 = copy[f]
861 if f2 in m2:
861 if f2 in m2:
862 actions[f] = ('m', (f2, f, f2, False, pa.node()),
862 actions[f] = ('m', (f2, f, f2, False, pa.node()),
863 "remote copied from " + f2)
863 "remote copied from " + f2)
864 else:
864 else:
865 actions[f] = ('m', (f2, f, f2, True, pa.node()),
865 actions[f] = ('m', (f2, f, f2, True, pa.node()),
866 "remote moved from " + f2)
866 "remote moved from " + f2)
867 elif f not in ma:
867 elif f not in ma:
868 # local unknown, remote created: the logic is described by the
868 # local unknown, remote created: the logic is described by the
869 # following table:
869 # following table:
870 #
870 #
871 # force branchmerge different | action
871 # force branchmerge different | action
872 # n * * | create
872 # n * * | create
873 # y n * | create
873 # y n * | create
874 # y y n | create
874 # y y n | create
875 # y y y | merge
875 # y y y | merge
876 #
876 #
877 # Checking whether the files are different is expensive, so we
877 # Checking whether the files are different is expensive, so we
878 # don't do that when we can avoid it.
878 # don't do that when we can avoid it.
879 if not force:
879 if not force:
880 actions[f] = ('c', (fl2,), "remote created")
880 actions[f] = ('c', (fl2,), "remote created")
881 elif not branchmerge:
881 elif not branchmerge:
882 actions[f] = ('c', (fl2,), "remote created")
882 actions[f] = ('c', (fl2,), "remote created")
883 else:
883 else:
884 actions[f] = ('cm', (fl2, pa.node()),
884 actions[f] = ('cm', (fl2, pa.node()),
885 "remote created, get or merge")
885 "remote created, get or merge")
886 elif n2 != ma[f]:
886 elif n2 != ma[f]:
887 if acceptremote:
887 if acceptremote:
888 actions[f] = ('c', (fl2,), "remote recreating")
888 actions[f] = ('c', (fl2,), "remote recreating")
889 else:
889 else:
890 actions[f] = ('dc', (None, f, f, False, pa.node()),
890 actions[f] = ('dc', (None, f, f, False, pa.node()),
891 "prompt deleted/changed")
891 "prompt deleted/changed")
892
892
893 return actions, diverge, renamedelete
893 return actions, diverge, renamedelete
894
894
895 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
895 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
896 """Resolves false conflicts where the nodeid changed but the content
896 """Resolves false conflicts where the nodeid changed but the content
897 remained the same."""
897 remained the same."""
898
898
899 for f, (m, args, msg) in actions.items():
899 for f, (m, args, msg) in actions.items():
900 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]):
901 # local did change but ended up with same content
901 # local did change but ended up with same content
902 actions[f] = 'r', None, "prompt same"
902 actions[f] = 'r', None, "prompt same"
903 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]):
904 # remote did change but ended up with same content
904 # remote did change but ended up with same content
905 del actions[f] # don't get = keep local deleted
905 del actions[f] # don't get = keep local deleted
906
906
907 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
907 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
908 acceptremote, followcopies, matcher=None):
908 acceptremote, followcopies, matcher=None):
909 "Calculate the actions needed to merge mctx into wctx using ancestors"
909 "Calculate the actions needed to merge mctx into wctx using ancestors"
910 if len(ancestors) == 1: # default
910 if len(ancestors) == 1: # default
911 actions, diverge, renamedelete = manifestmerge(
911 actions, diverge, renamedelete = manifestmerge(
912 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
912 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
913 acceptremote, followcopies)
913 acceptremote, followcopies)
914 _checkunknownfiles(repo, wctx, mctx, force, actions)
914 _checkunknownfiles(repo, wctx, mctx, force, actions)
915
915
916 else: # only when merge.preferancestor=* - the default
916 else: # only when merge.preferancestor=* - the default
917 repo.ui.note(
917 repo.ui.note(
918 _("note: merging %s and %s using bids from ancestors %s\n") %
918 _("note: merging %s and %s using bids from ancestors %s\n") %
919 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
919 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
920
920
921 # Call for bids
921 # Call for bids
922 fbids = {} # mapping filename to bids (action method to list af actions)
922 fbids = {} # mapping filename to bids (action method to list af actions)
923 diverge, renamedelete = None, None
923 diverge, renamedelete = None, None
924 for ancestor in ancestors:
924 for ancestor in ancestors:
925 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
925 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
926 actions, diverge1, renamedelete1 = manifestmerge(
926 actions, diverge1, renamedelete1 = manifestmerge(
927 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
927 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
928 acceptremote, followcopies)
928 acceptremote, followcopies)
929 _checkunknownfiles(repo, wctx, mctx, force, actions)
929 _checkunknownfiles(repo, wctx, mctx, force, actions)
930
930
931 # Track the shortest set of warning on the theory that bid
931 # Track the shortest set of warning on the theory that bid
932 # merge will correctly incorporate more information
932 # merge will correctly incorporate more information
933 if diverge is None or len(diverge1) < len(diverge):
933 if diverge is None or len(diverge1) < len(diverge):
934 diverge = diverge1
934 diverge = diverge1
935 if renamedelete is None or len(renamedelete) < len(renamedelete1):
935 if renamedelete is None or len(renamedelete) < len(renamedelete1):
936 renamedelete = renamedelete1
936 renamedelete = renamedelete1
937
937
938 for f, a in sorted(actions.iteritems()):
938 for f, a in sorted(actions.iteritems()):
939 m, args, msg = a
939 m, args, msg = a
940 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
940 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
941 if f in fbids:
941 if f in fbids:
942 d = fbids[f]
942 d = fbids[f]
943 if m in d:
943 if m in d:
944 d[m].append(a)
944 d[m].append(a)
945 else:
945 else:
946 d[m] = [a]
946 d[m] = [a]
947 else:
947 else:
948 fbids[f] = {m: [a]}
948 fbids[f] = {m: [a]}
949
949
950 # Pick the best bid for each file
950 # Pick the best bid for each file
951 repo.ui.note(_('\nauction for merging merge bids\n'))
951 repo.ui.note(_('\nauction for merging merge bids\n'))
952 actions = {}
952 actions = {}
953 for f, bids in sorted(fbids.items()):
953 for f, bids in sorted(fbids.items()):
954 # bids is a mapping from action method to list af actions
954 # bids is a mapping from action method to list af actions
955 # Consensus?
955 # Consensus?
956 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
957 m, l = bids.items()[0]
957 m, l = bids.items()[0]
958 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
959 repo.ui.note(" %s: consensus for %s\n" % (f, m))
959 repo.ui.note(" %s: consensus for %s\n" % (f, m))
960 actions[f] = l[0]
960 actions[f] = l[0]
961 continue
961 continue
962 # If keep is an option, just do it.
962 # If keep is an option, just do it.
963 if 'k' in bids:
963 if 'k' in bids:
964 repo.ui.note(" %s: picking 'keep' action\n" % f)
964 repo.ui.note(" %s: picking 'keep' action\n" % f)
965 actions[f] = bids['k'][0]
965 actions[f] = bids['k'][0]
966 continue
966 continue
967 # 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.
968 if 'g' in bids:
968 if 'g' in bids:
969 ga0 = bids['g'][0]
969 ga0 = bids['g'][0]
970 if all(a == ga0 for a in bids['g'][1:]):
970 if all(a == ga0 for a in bids['g'][1:]):
971 repo.ui.note(" %s: picking 'get' action\n" % f)
971 repo.ui.note(" %s: picking 'get' action\n" % f)
972 actions[f] = ga0
972 actions[f] = ga0
973 continue
973 continue
974 # TODO: Consider other simple actions such as mode changes
974 # TODO: Consider other simple actions such as mode changes
975 # Handle inefficient democrazy.
975 # Handle inefficient democrazy.
976 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
976 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
977 for m, l in sorted(bids.items()):
977 for m, l in sorted(bids.items()):
978 for _f, args, msg in l:
978 for _f, args, msg in l:
979 repo.ui.note(' %s -> %s\n' % (msg, m))
979 repo.ui.note(' %s -> %s\n' % (msg, m))
980 # Pick random action. TODO: Instead, prompt user when resolving
980 # Pick random action. TODO: Instead, prompt user when resolving
981 m, l = bids.items()[0]
981 m, l = bids.items()[0]
982 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
982 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
983 (f, m))
983 (f, m))
984 actions[f] = l[0]
984 actions[f] = l[0]
985 continue
985 continue
986 repo.ui.note(_('end of auction\n\n'))
986 repo.ui.note(_('end of auction\n\n'))
987
987
988 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
988 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
989
989
990 if wctx.rev() is None:
990 if wctx.rev() is None:
991 fractions = _forgetremoved(wctx, mctx, branchmerge)
991 fractions = _forgetremoved(wctx, mctx, branchmerge)
992 actions.update(fractions)
992 actions.update(fractions)
993
993
994 return actions, diverge, renamedelete
994 return actions, diverge, renamedelete
995
995
996 def batchremove(repo, actions):
996 def batchremove(repo, actions):
997 """apply removes to the working directory
997 """apply removes to the working directory
998
998
999 yields tuples for progress updates
999 yields tuples for progress updates
1000 """
1000 """
1001 verbose = repo.ui.verbose
1001 verbose = repo.ui.verbose
1002 unlink = util.unlinkpath
1002 unlink = util.unlinkpath
1003 wjoin = repo.wjoin
1003 wjoin = repo.wjoin
1004 audit = repo.wvfs.audit
1004 audit = repo.wvfs.audit
1005 i = 0
1005 i = 0
1006 for f, args, msg in actions:
1006 for f, args, msg in actions:
1007 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1007 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1008 if verbose:
1008 if verbose:
1009 repo.ui.note(_("removing %s\n") % f)
1009 repo.ui.note(_("removing %s\n") % f)
1010 audit(f)
1010 audit(f)
1011 try:
1011 try:
1012 unlink(wjoin(f), ignoremissing=True)
1012 unlink(wjoin(f), ignoremissing=True)
1013 except OSError as inst:
1013 except OSError as inst:
1014 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1014 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1015 (f, inst.strerror))
1015 (f, inst.strerror))
1016 if i == 100:
1016 if i == 100:
1017 yield i, f
1017 yield i, f
1018 i = 0
1018 i = 0
1019 i += 1
1019 i += 1
1020 if i > 0:
1020 if i > 0:
1021 yield i, f
1021 yield i, f
1022
1022
1023 def batchget(repo, mctx, actions):
1023 def batchget(repo, mctx, actions):
1024 """apply gets to the working directory
1024 """apply gets to the working directory
1025
1025
1026 mctx is the context to get from
1026 mctx is the context to get from
1027
1027
1028 yields tuples for progress updates
1028 yields tuples for progress updates
1029 """
1029 """
1030 verbose = repo.ui.verbose
1030 verbose = repo.ui.verbose
1031 fctx = mctx.filectx
1031 fctx = mctx.filectx
1032 wwrite = repo.wwrite
1032 wwrite = repo.wwrite
1033 ui = repo.ui
1033 ui = repo.ui
1034 i = 0
1034 i = 0
1035 for f, (flags, backup), msg in actions:
1035 for f, (flags, backup), msg in actions:
1036 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1036 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1037 if verbose:
1037 if verbose:
1038 repo.ui.note(_("getting %s\n") % f)
1038 repo.ui.note(_("getting %s\n") % f)
1039
1039
1040 if backup:
1040 if backup:
1041 absf = repo.wjoin(f)
1041 absf = repo.wjoin(f)
1042 orig = scmutil.origpath(ui, repo, absf)
1042 orig = scmutil.origpath(ui, repo, absf)
1043 try:
1043 try:
1044 # TODO Mercurial has always aborted if an untracked directory
1044 # TODO Mercurial has always aborted if an untracked directory
1045 # is replaced by a tracked file, or generally with
1045 # is replaced by a tracked file, or generally with
1046 # file/directory merges. This needs to be sorted out.
1046 # file/directory merges. This needs to be sorted out.
1047 if repo.wvfs.isfileorlink(f):
1047 if repo.wvfs.isfileorlink(f):
1048 util.rename(absf, orig)
1048 util.rename(absf, orig)
1049 except OSError as e:
1049 except OSError as e:
1050 if e.errno != errno.ENOENT:
1050 if e.errno != errno.ENOENT:
1051 raise
1051 raise
1052
1052
1053 wwrite(f, fctx(f).data(), flags)
1053 wwrite(f, fctx(f).data(), flags)
1054 if i == 100:
1054 if i == 100:
1055 yield i, f
1055 yield i, f
1056 i = 0
1056 i = 0
1057 i += 1
1057 i += 1
1058 if i > 0:
1058 if i > 0:
1059 yield i, f
1059 yield i, f
1060
1060
1061 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1061 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1062 """apply the merge action list to the working directory
1062 """apply the merge action list to the working directory
1063
1063
1064 wctx is the working copy context
1064 wctx is the working copy context
1065 mctx is the context to be merged into the working copy
1065 mctx is the context to be merged into the working copy
1066
1066
1067 Return a tuple of counts (updated, merged, removed, unresolved) that
1067 Return a tuple of counts (updated, merged, removed, unresolved) that
1068 describes how many files were affected by the update.
1068 describes how many files were affected by the update.
1069 """
1069 """
1070
1070
1071 updated, merged, removed = 0, 0, 0
1071 updated, merged, removed = 0, 0, 0
1072 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
1072 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
1073 moves = []
1073 moves = []
1074 for m, l in actions.items():
1074 for m, l in actions.items():
1075 l.sort()
1075 l.sort()
1076
1076
1077 # 'cd' and 'dc' actions are treated like other merge conflicts
1077 # 'cd' and 'dc' actions are treated like other merge conflicts
1078 mergeactions = sorted(actions['cd'])
1078 mergeactions = sorted(actions['cd'])
1079 mergeactions.extend(sorted(actions['dc']))
1079 mergeactions.extend(sorted(actions['dc']))
1080 mergeactions.extend(actions['m'])
1080 mergeactions.extend(actions['m'])
1081 for f, args, msg in mergeactions:
1081 for f, args, msg in mergeactions:
1082 f1, f2, fa, move, anc = args
1082 f1, f2, fa, move, anc = args
1083 if f == '.hgsubstate': # merged internally
1083 if f == '.hgsubstate': # merged internally
1084 continue
1084 continue
1085 if f1 is None:
1085 if f1 is None:
1086 fcl = filemerge.absentfilectx(wctx, fa)
1086 fcl = filemerge.absentfilectx(wctx, fa)
1087 else:
1087 else:
1088 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))
1089 fcl = wctx[f1]
1089 fcl = wctx[f1]
1090 if f2 is None:
1090 if f2 is None:
1091 fco = filemerge.absentfilectx(mctx, fa)
1091 fco = filemerge.absentfilectx(mctx, fa)
1092 else:
1092 else:
1093 fco = mctx[f2]
1093 fco = mctx[f2]
1094 actx = repo[anc]
1094 actx = repo[anc]
1095 if fa in actx:
1095 if fa in actx:
1096 fca = actx[fa]
1096 fca = actx[fa]
1097 else:
1097 else:
1098 # TODO: move to absentfilectx
1098 # TODO: move to absentfilectx
1099 fca = repo.filectx(f1, fileid=nullrev)
1099 fca = repo.filectx(f1, fileid=nullrev)
1100 ms.add(fcl, fco, fca, f)
1100 ms.add(fcl, fco, fca, f)
1101 if f1 != f and move:
1101 if f1 != f and move:
1102 moves.append(f1)
1102 moves.append(f1)
1103
1103
1104 audit = repo.wvfs.audit
1104 audit = repo.wvfs.audit
1105 _updating = _('updating')
1105 _updating = _('updating')
1106 _files = _('files')
1106 _files = _('files')
1107 progress = repo.ui.progress
1107 progress = repo.ui.progress
1108
1108
1109 # remove renamed files after safely stored
1109 # remove renamed files after safely stored
1110 for f in moves:
1110 for f in moves:
1111 if os.path.lexists(repo.wjoin(f)):
1111 if os.path.lexists(repo.wjoin(f)):
1112 repo.ui.debug("removing %s\n" % f)
1112 repo.ui.debug("removing %s\n" % f)
1113 audit(f)
1113 audit(f)
1114 util.unlinkpath(repo.wjoin(f))
1114 util.unlinkpath(repo.wjoin(f))
1115
1115
1116 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')
1117
1117
1118 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1118 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1119 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1119 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1120
1120
1121 # remove in parallel (must come first)
1121 # remove in parallel (must come first)
1122 z = 0
1122 z = 0
1123 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
1123 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
1124 for i, item in prog:
1124 for i, item in prog:
1125 z += i
1125 z += i
1126 progress(_updating, z, item=item, total=numupdates, unit=_files)
1126 progress(_updating, z, item=item, total=numupdates, unit=_files)
1127 removed = len(actions['r'])
1127 removed = len(actions['r'])
1128
1128
1129 # get in parallel
1129 # get in parallel
1130 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'])
1131 for i, item in prog:
1131 for i, item in prog:
1132 z += i
1132 z += i
1133 progress(_updating, z, item=item, total=numupdates, unit=_files)
1133 progress(_updating, z, item=item, total=numupdates, unit=_files)
1134 updated = len(actions['g'])
1134 updated = len(actions['g'])
1135
1135
1136 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1136 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1137 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1137 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1138
1138
1139 # forget (manifest only, just log it) (must come first)
1139 # forget (manifest only, just log it) (must come first)
1140 for f, args, msg in actions['f']:
1140 for f, args, msg in actions['f']:
1141 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1141 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1142 z += 1
1142 z += 1
1143 progress(_updating, z, item=f, total=numupdates, unit=_files)
1143 progress(_updating, z, item=f, total=numupdates, unit=_files)
1144
1144
1145 # re-add (manifest only, just log it)
1145 # re-add (manifest only, just log it)
1146 for f, args, msg in actions['a']:
1146 for f, args, msg in actions['a']:
1147 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1147 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1148 z += 1
1148 z += 1
1149 progress(_updating, z, item=f, total=numupdates, unit=_files)
1149 progress(_updating, z, item=f, total=numupdates, unit=_files)
1150
1150
1151 # re-add/mark as modified (manifest only, just log it)
1151 # re-add/mark as modified (manifest only, just log it)
1152 for f, args, msg in actions['am']:
1152 for f, args, msg in actions['am']:
1153 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1153 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1154 z += 1
1154 z += 1
1155 progress(_updating, z, item=f, total=numupdates, unit=_files)
1155 progress(_updating, z, item=f, total=numupdates, unit=_files)
1156
1156
1157 # keep (noop, just log it)
1157 # keep (noop, just log it)
1158 for f, args, msg in actions['k']:
1158 for f, args, msg in actions['k']:
1159 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1159 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1160 # no progress
1160 # no progress
1161
1161
1162 # directory rename, move local
1162 # directory rename, move local
1163 for f, args, msg in actions['dm']:
1163 for f, args, msg in actions['dm']:
1164 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1164 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1165 z += 1
1165 z += 1
1166 progress(_updating, z, item=f, total=numupdates, unit=_files)
1166 progress(_updating, z, item=f, total=numupdates, unit=_files)
1167 f0, flags = args
1167 f0, flags = args
1168 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1168 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1169 audit(f)
1169 audit(f)
1170 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1170 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1171 util.unlinkpath(repo.wjoin(f0))
1171 util.unlinkpath(repo.wjoin(f0))
1172 updated += 1
1172 updated += 1
1173
1173
1174 # local directory rename, get
1174 # local directory rename, get
1175 for f, args, msg in actions['dg']:
1175 for f, args, msg in actions['dg']:
1176 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1176 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1177 z += 1
1177 z += 1
1178 progress(_updating, z, item=f, total=numupdates, unit=_files)
1178 progress(_updating, z, item=f, total=numupdates, unit=_files)
1179 f0, flags = args
1179 f0, flags = args
1180 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1180 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1181 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1181 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1182 updated += 1
1182 updated += 1
1183
1183
1184 # exec
1184 # exec
1185 for f, args, msg in actions['e']:
1185 for f, args, msg in actions['e']:
1186 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1186 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1187 z += 1
1187 z += 1
1188 progress(_updating, z, item=f, total=numupdates, unit=_files)
1188 progress(_updating, z, item=f, total=numupdates, unit=_files)
1189 flags, = args
1189 flags, = args
1190 audit(f)
1190 audit(f)
1191 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1191 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1192 updated += 1
1192 updated += 1
1193
1193
1194 # 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
1195 # 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
1196 # True
1196 # True
1197 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1197 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1198
1198
1199 if usemergedriver:
1199 if usemergedriver:
1200 ms.commit()
1200 ms.commit()
1201 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1201 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1202 # the driver might leave some files unresolved
1202 # the driver might leave some files unresolved
1203 unresolvedf = set(ms.unresolved())
1203 unresolvedf = set(ms.unresolved())
1204 if not proceed:
1204 if not proceed:
1205 # 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
1206 # error out
1206 # error out
1207 return updated, merged, removed, max(len(unresolvedf), 1)
1207 return updated, merged, removed, max(len(unresolvedf), 1)
1208 newactions = []
1208 newactions = []
1209 for f, args, msg in mergeactions:
1209 for f, args, msg in mergeactions:
1210 if f in unresolvedf:
1210 if f in unresolvedf:
1211 newactions.append((f, args, msg))
1211 newactions.append((f, args, msg))
1212 mergeactions = newactions
1212 mergeactions = newactions
1213
1213
1214 # premerge
1214 # premerge
1215 tocomplete = []
1215 tocomplete = []
1216 for f, args, msg in mergeactions:
1216 for f, args, msg in mergeactions:
1217 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1217 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1218 z += 1
1218 z += 1
1219 progress(_updating, z, item=f, total=numupdates, unit=_files)
1219 progress(_updating, z, item=f, total=numupdates, unit=_files)
1220 if f == '.hgsubstate': # subrepo states need updating
1220 if f == '.hgsubstate': # subrepo states need updating
1221 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1221 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1222 overwrite)
1222 overwrite)
1223 continue
1223 continue
1224 audit(f)
1224 audit(f)
1225 complete, r = ms.preresolve(f, wctx, labels=labels)
1225 complete, r = ms.preresolve(f, wctx, labels=labels)
1226 if not complete:
1226 if not complete:
1227 numupdates += 1
1227 numupdates += 1
1228 tocomplete.append((f, args, msg))
1228 tocomplete.append((f, args, msg))
1229
1229
1230 # merge
1230 # merge
1231 for f, args, msg in tocomplete:
1231 for f, args, msg in tocomplete:
1232 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1232 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1233 z += 1
1233 z += 1
1234 progress(_updating, z, item=f, total=numupdates, unit=_files)
1234 progress(_updating, z, item=f, total=numupdates, unit=_files)
1235 ms.resolve(f, wctx, labels=labels)
1235 ms.resolve(f, wctx, labels=labels)
1236
1236
1237 ms.commit()
1237 ms.commit()
1238
1238
1239 unresolved = ms.unresolvedcount()
1239 unresolved = ms.unresolvedcount()
1240
1240
1241 if usemergedriver and not unresolved and ms.mdstate() != 's':
1241 if usemergedriver and not unresolved and ms.mdstate() != 's':
1242 if not driverconclude(repo, ms, wctx, labels=labels):
1242 if not driverconclude(repo, ms, wctx, labels=labels):
1243 # 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
1244 # error out
1244 # error out
1245 unresolved = max(unresolved, 1)
1245 unresolved = max(unresolved, 1)
1246
1246
1247 ms.commit()
1247 ms.commit()
1248
1248
1249 msupdated, msmerged, msremoved = ms.counts()
1249 msupdated, msmerged, msremoved = ms.counts()
1250 updated += msupdated
1250 updated += msupdated
1251 merged += msmerged
1251 merged += msmerged
1252 removed += msremoved
1252 removed += msremoved
1253
1253
1254 extraactions = ms.actions()
1254 extraactions = ms.actions()
1255 for k, acts in extraactions.iteritems():
1255 for k, acts in extraactions.iteritems():
1256 actions[k].extend(acts)
1256 actions[k].extend(acts)
1257
1257
1258 progress(_updating, None, total=numupdates, unit=_files)
1258 progress(_updating, None, total=numupdates, unit=_files)
1259
1259
1260 return updated, merged, removed, unresolved
1260 return updated, merged, removed, unresolved
1261
1261
1262 def recordupdates(repo, actions, branchmerge):
1262 def recordupdates(repo, actions, branchmerge):
1263 "record merge actions to the dirstate"
1263 "record merge actions to the dirstate"
1264 # remove (must come first)
1264 # remove (must come first)
1265 for f, args, msg in actions.get('r', []):
1265 for f, args, msg in actions.get('r', []):
1266 if branchmerge:
1266 if branchmerge:
1267 repo.dirstate.remove(f)
1267 repo.dirstate.remove(f)
1268 else:
1268 else:
1269 repo.dirstate.drop(f)
1269 repo.dirstate.drop(f)
1270
1270
1271 # forget (must come first)
1271 # forget (must come first)
1272 for f, args, msg in actions.get('f', []):
1272 for f, args, msg in actions.get('f', []):
1273 repo.dirstate.drop(f)
1273 repo.dirstate.drop(f)
1274
1274
1275 # re-add
1275 # re-add
1276 for f, args, msg in actions.get('a', []):
1276 for f, args, msg in actions.get('a', []):
1277 repo.dirstate.add(f)
1277 repo.dirstate.add(f)
1278
1278
1279 # re-add/mark as modified
1279 # re-add/mark as modified
1280 for f, args, msg in actions.get('am', []):
1280 for f, args, msg in actions.get('am', []):
1281 if branchmerge:
1281 if branchmerge:
1282 repo.dirstate.normallookup(f)
1282 repo.dirstate.normallookup(f)
1283 else:
1283 else:
1284 repo.dirstate.add(f)
1284 repo.dirstate.add(f)
1285
1285
1286 # exec change
1286 # exec change
1287 for f, args, msg in actions.get('e', []):
1287 for f, args, msg in actions.get('e', []):
1288 repo.dirstate.normallookup(f)
1288 repo.dirstate.normallookup(f)
1289
1289
1290 # keep
1290 # keep
1291 for f, args, msg in actions.get('k', []):
1291 for f, args, msg in actions.get('k', []):
1292 pass
1292 pass
1293
1293
1294 # get
1294 # get
1295 for f, args, msg in actions.get('g', []):
1295 for f, args, msg in actions.get('g', []):
1296 if branchmerge:
1296 if branchmerge:
1297 repo.dirstate.otherparent(f)
1297 repo.dirstate.otherparent(f)
1298 else:
1298 else:
1299 repo.dirstate.normal(f)
1299 repo.dirstate.normal(f)
1300
1300
1301 # merge
1301 # merge
1302 for f, args, msg in actions.get('m', []):
1302 for f, args, msg in actions.get('m', []):
1303 f1, f2, fa, move, anc = args
1303 f1, f2, fa, move, anc = args
1304 if branchmerge:
1304 if branchmerge:
1305 # We've done a branch merge, mark this file as merged
1305 # We've done a branch merge, mark this file as merged
1306 # so that we properly record the merger later
1306 # so that we properly record the merger later
1307 repo.dirstate.merge(f)
1307 repo.dirstate.merge(f)
1308 if f1 != f2: # copy/rename
1308 if f1 != f2: # copy/rename
1309 if move:
1309 if move:
1310 repo.dirstate.remove(f1)
1310 repo.dirstate.remove(f1)
1311 if f1 != f:
1311 if f1 != f:
1312 repo.dirstate.copy(f1, f)
1312 repo.dirstate.copy(f1, f)
1313 else:
1313 else:
1314 repo.dirstate.copy(f2, f)
1314 repo.dirstate.copy(f2, f)
1315 else:
1315 else:
1316 # We've update-merged a locally modified file, so
1316 # We've update-merged a locally modified file, so
1317 # we set the dirstate to emulate a normal checkout
1317 # we set the dirstate to emulate a normal checkout
1318 # of that file some time in the past. Thus our
1318 # of that file some time in the past. Thus our
1319 # merge will appear as a normal local file
1319 # merge will appear as a normal local file
1320 # modification.
1320 # modification.
1321 if f2 == f: # file not locally copied/moved
1321 if f2 == f: # file not locally copied/moved
1322 repo.dirstate.normallookup(f)
1322 repo.dirstate.normallookup(f)
1323 if move:
1323 if move:
1324 repo.dirstate.drop(f1)
1324 repo.dirstate.drop(f1)
1325
1325
1326 # directory rename, move local
1326 # directory rename, move local
1327 for f, args, msg in actions.get('dm', []):
1327 for f, args, msg in actions.get('dm', []):
1328 f0, flag = args
1328 f0, flag = args
1329 if branchmerge:
1329 if branchmerge:
1330 repo.dirstate.add(f)
1330 repo.dirstate.add(f)
1331 repo.dirstate.remove(f0)
1331 repo.dirstate.remove(f0)
1332 repo.dirstate.copy(f0, f)
1332 repo.dirstate.copy(f0, f)
1333 else:
1333 else:
1334 repo.dirstate.normal(f)
1334 repo.dirstate.normal(f)
1335 repo.dirstate.drop(f0)
1335 repo.dirstate.drop(f0)
1336
1336
1337 # directory rename, get
1337 # directory rename, get
1338 for f, args, msg in actions.get('dg', []):
1338 for f, args, msg in actions.get('dg', []):
1339 f0, flag = args
1339 f0, flag = args
1340 if branchmerge:
1340 if branchmerge:
1341 repo.dirstate.add(f)
1341 repo.dirstate.add(f)
1342 repo.dirstate.copy(f0, f)
1342 repo.dirstate.copy(f0, f)
1343 else:
1343 else:
1344 repo.dirstate.normal(f)
1344 repo.dirstate.normal(f)
1345
1345
1346 def update(repo, node, branchmerge, force, ancestor=None,
1346 def update(repo, node, branchmerge, force, ancestor=None,
1347 mergeancestor=False, labels=None, matcher=None):
1347 mergeancestor=False, labels=None, matcher=None):
1348 """
1348 """
1349 Perform a merge between the working directory and the given node
1349 Perform a merge between the working directory and the given node
1350
1350
1351 node = the node to update to, or None if unspecified
1351 node = the node to update to, or None if unspecified
1352 branchmerge = whether to merge between branches
1352 branchmerge = whether to merge between branches
1353 force = whether to force branch merging or file overwriting
1353 force = whether to force branch merging or file overwriting
1354 matcher = a matcher to filter file lists (dirstate not updated)
1354 matcher = a matcher to filter file lists (dirstate not updated)
1355 mergeancestor = whether it is merging with an ancestor. If true,
1355 mergeancestor = whether it is merging with an ancestor. If true,
1356 we should accept the incoming changes for any prompts that occur.
1356 we should accept the incoming changes for any prompts that occur.
1357 If false, merging with an ancestor (fast-forward) is only allowed
1357 If false, merging with an ancestor (fast-forward) is only allowed
1358 between different named branches. This flag is used by rebase extension
1358 between different named branches. This flag is used by rebase extension
1359 as a temporary fix and should be avoided in general.
1359 as a temporary fix and should be avoided in general.
1360 labels = labels to use for base, local and other
1360
1361
1361 The table below shows all the behaviors of the update command
1362 The table below shows all the behaviors of the update command
1362 given the -c and -C or no options, whether the working directory
1363 given the -c and -C or no options, whether the working directory
1363 is dirty, whether a revision is specified, and the relationship of
1364 is dirty, whether a revision is specified, and the relationship of
1364 the parent rev to the target rev (linear, on the same named
1365 the parent rev to the target rev (linear, on the same named
1365 branch, or on another named branch).
1366 branch, or on another named branch).
1366
1367
1367 This logic is tested by test-update-branches.t.
1368 This logic is tested by test-update-branches.t.
1368
1369
1369 -c -C dirty rev | linear same cross
1370 -c -C dirty rev | linear same cross
1370 n n n n | ok (1) x
1371 n n n n | ok (1) x
1371 n n n y | ok ok ok
1372 n n n y | ok ok ok
1372 n n y n | merge (2) (2)
1373 n n y n | merge (2) (2)
1373 n n y y | merge (3) (3)
1374 n n y y | merge (3) (3)
1374 n y * * | discard discard discard
1375 n y * * | discard discard discard
1375 y n y * | (4) (4) (4)
1376 y n y * | (4) (4) (4)
1376 y n n * | ok ok ok
1377 y n n * | ok ok ok
1377 y y * * | (5) (5) (5)
1378 y y * * | (5) (5) (5)
1378
1379
1379 x = can't happen
1380 x = can't happen
1380 * = don't-care
1381 * = don't-care
1381 1 = abort: not a linear update (merge or update --check to force update)
1382 1 = abort: not a linear update (merge or update --check to force update)
1382 2 = abort: uncommitted changes (commit and merge, or update --clean to
1383 2 = abort: uncommitted changes (commit and merge, or update --clean to
1383 discard changes)
1384 discard changes)
1384 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1385 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1385 4 = abort: uncommitted changes (checked in commands.py)
1386 4 = abort: uncommitted changes (checked in commands.py)
1386 5 = incompatible options (checked in commands.py)
1387 5 = incompatible options (checked in commands.py)
1387
1388
1388 Return the same tuple as applyupdates().
1389 Return the same tuple as applyupdates().
1389 """
1390 """
1390
1391
1391 onode = node
1392 onode = node
1392 # If we're doing a partial update, we need to skip updating
1393 # If we're doing a partial update, we need to skip updating
1393 # the dirstate, so make a note of any partial-ness to the
1394 # the dirstate, so make a note of any partial-ness to the
1394 # update here.
1395 # update here.
1395 if matcher is None or matcher.always():
1396 if matcher is None or matcher.always():
1396 partial = False
1397 partial = False
1397 else:
1398 else:
1398 partial = True
1399 partial = True
1399 with repo.wlock():
1400 with repo.wlock():
1400 wc = repo[None]
1401 wc = repo[None]
1401 pl = wc.parents()
1402 pl = wc.parents()
1402 p1 = pl[0]
1403 p1 = pl[0]
1403 pas = [None]
1404 pas = [None]
1404 if ancestor is not None:
1405 if ancestor is not None:
1405 pas = [repo[ancestor]]
1406 pas = [repo[ancestor]]
1406
1407
1407 if node is None:
1408 if node is None:
1408 if (repo.ui.configbool('devel', 'all-warnings')
1409 if (repo.ui.configbool('devel', 'all-warnings')
1409 or repo.ui.configbool('devel', 'oldapi')):
1410 or repo.ui.configbool('devel', 'oldapi')):
1410 repo.ui.develwarn('update with no target')
1411 repo.ui.develwarn('update with no target')
1411 rev, _mark, _act = destutil.destupdate(repo)
1412 rev, _mark, _act = destutil.destupdate(repo)
1412 node = repo[rev].node()
1413 node = repo[rev].node()
1413
1414
1414 overwrite = force and not branchmerge
1415 overwrite = force and not branchmerge
1415
1416
1416 p2 = repo[node]
1417 p2 = repo[node]
1417 if pas[0] is None:
1418 if pas[0] is None:
1418 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1419 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1419 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1420 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1420 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1421 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1421 else:
1422 else:
1422 pas = [p1.ancestor(p2, warn=branchmerge)]
1423 pas = [p1.ancestor(p2, warn=branchmerge)]
1423
1424
1424 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1425 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1425
1426
1426 ### check phase
1427 ### check phase
1427 if not overwrite:
1428 if not overwrite:
1428 if len(pl) > 1:
1429 if len(pl) > 1:
1429 raise error.Abort(_("outstanding uncommitted merge"))
1430 raise error.Abort(_("outstanding uncommitted merge"))
1430 ms = mergestate.read(repo)
1431 ms = mergestate.read(repo)
1431 if list(ms.unresolved()):
1432 if list(ms.unresolved()):
1432 raise error.Abort(_("outstanding merge conflicts"))
1433 raise error.Abort(_("outstanding merge conflicts"))
1433 if branchmerge:
1434 if branchmerge:
1434 if pas == [p2]:
1435 if pas == [p2]:
1435 raise error.Abort(_("merging with a working directory ancestor"
1436 raise error.Abort(_("merging with a working directory ancestor"
1436 " has no effect"))
1437 " has no effect"))
1437 elif pas == [p1]:
1438 elif pas == [p1]:
1438 if not mergeancestor and p1.branch() == p2.branch():
1439 if not mergeancestor and p1.branch() == p2.branch():
1439 raise error.Abort(_("nothing to merge"),
1440 raise error.Abort(_("nothing to merge"),
1440 hint=_("use 'hg update' "
1441 hint=_("use 'hg update' "
1441 "or check 'hg heads'"))
1442 "or check 'hg heads'"))
1442 if not force and (wc.files() or wc.deleted()):
1443 if not force and (wc.files() or wc.deleted()):
1443 raise error.Abort(_("uncommitted changes"),
1444 raise error.Abort(_("uncommitted changes"),
1444 hint=_("use 'hg status' to list changes"))
1445 hint=_("use 'hg status' to list changes"))
1445 for s in sorted(wc.substate):
1446 for s in sorted(wc.substate):
1446 wc.sub(s).bailifchanged()
1447 wc.sub(s).bailifchanged()
1447
1448
1448 elif not overwrite:
1449 elif not overwrite:
1449 if p1 == p2: # no-op update
1450 if p1 == p2: # no-op update
1450 # call the hooks and exit early
1451 # call the hooks and exit early
1451 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1452 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1452 repo.hook('update', parent1=xp2, parent2='', error=0)
1453 repo.hook('update', parent1=xp2, parent2='', error=0)
1453 return 0, 0, 0, 0
1454 return 0, 0, 0, 0
1454
1455
1455 if pas not in ([p1], [p2]): # nonlinear
1456 if pas not in ([p1], [p2]): # nonlinear
1456 dirty = wc.dirty(missing=True)
1457 dirty = wc.dirty(missing=True)
1457 if dirty or onode is None:
1458 if dirty or onode is None:
1458 # Branching is a bit strange to ensure we do the minimal
1459 # Branching is a bit strange to ensure we do the minimal
1459 # amount of call to obsolete.background.
1460 # amount of call to obsolete.background.
1460 foreground = obsolete.foreground(repo, [p1.node()])
1461 foreground = obsolete.foreground(repo, [p1.node()])
1461 # note: the <node> variable contains a random identifier
1462 # note: the <node> variable contains a random identifier
1462 if repo[node].node() in foreground:
1463 if repo[node].node() in foreground:
1463 pas = [p1] # allow updating to successors
1464 pas = [p1] # allow updating to successors
1464 elif dirty:
1465 elif dirty:
1465 msg = _("uncommitted changes")
1466 msg = _("uncommitted changes")
1466 if onode is None:
1467 if onode is None:
1467 hint = _("commit and merge, or update --clean to"
1468 hint = _("commit and merge, or update --clean to"
1468 " discard changes")
1469 " discard changes")
1469 else:
1470 else:
1470 hint = _("commit or update --clean to discard"
1471 hint = _("commit or update --clean to discard"
1471 " changes")
1472 " changes")
1472 raise error.Abort(msg, hint=hint)
1473 raise error.Abort(msg, hint=hint)
1473 else: # node is none
1474 else: # node is none
1474 msg = _("not a linear update")
1475 msg = _("not a linear update")
1475 hint = _("merge or update --check to force update")
1476 hint = _("merge or update --check to force update")
1476 raise error.Abort(msg, hint=hint)
1477 raise error.Abort(msg, hint=hint)
1477 else:
1478 else:
1478 # Allow jumping branches if clean and specific rev given
1479 # Allow jumping branches if clean and specific rev given
1479 pas = [p1]
1480 pas = [p1]
1480
1481
1481 # deprecated config: merge.followcopies
1482 # deprecated config: merge.followcopies
1482 followcopies = False
1483 followcopies = False
1483 if overwrite:
1484 if overwrite:
1484 pas = [wc]
1485 pas = [wc]
1485 elif pas == [p2]: # backwards
1486 elif pas == [p2]: # backwards
1486 pas = [wc.p1()]
1487 pas = [wc.p1()]
1487 elif not branchmerge and not wc.dirty(missing=True):
1488 elif not branchmerge and not wc.dirty(missing=True):
1488 pass
1489 pass
1489 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1490 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1490 followcopies = True
1491 followcopies = True
1491
1492
1492 ### calculate phase
1493 ### calculate phase
1493 actionbyfile, diverge, renamedelete = calculateupdates(
1494 actionbyfile, diverge, renamedelete = calculateupdates(
1494 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1495 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1495 followcopies, matcher=matcher)
1496 followcopies, matcher=matcher)
1496
1497
1497 # Prompt and create actions. Most of this is in the resolve phase
1498 # Prompt and create actions. Most of this is in the resolve phase
1498 # already, but we can't handle .hgsubstate in filemerge or
1499 # already, but we can't handle .hgsubstate in filemerge or
1499 # subrepo.submerge yet so we have to keep prompting for it.
1500 # subrepo.submerge yet so we have to keep prompting for it.
1500 if '.hgsubstate' in actionbyfile:
1501 if '.hgsubstate' in actionbyfile:
1501 f = '.hgsubstate'
1502 f = '.hgsubstate'
1502 m, args, msg = actionbyfile[f]
1503 m, args, msg = actionbyfile[f]
1503 if m == 'cd':
1504 if m == 'cd':
1504 if repo.ui.promptchoice(
1505 if repo.ui.promptchoice(
1505 _("local changed %s which remote deleted\n"
1506 _("local changed %s which remote deleted\n"
1506 "use (c)hanged version or (d)elete?"
1507 "use (c)hanged version or (d)elete?"
1507 "$$ &Changed $$ &Delete") % f, 0):
1508 "$$ &Changed $$ &Delete") % f, 0):
1508 actionbyfile[f] = ('r', None, "prompt delete")
1509 actionbyfile[f] = ('r', None, "prompt delete")
1509 elif f in p1:
1510 elif f in p1:
1510 actionbyfile[f] = ('am', None, "prompt keep")
1511 actionbyfile[f] = ('am', None, "prompt keep")
1511 else:
1512 else:
1512 actionbyfile[f] = ('a', None, "prompt keep")
1513 actionbyfile[f] = ('a', None, "prompt keep")
1513 elif m == 'dc':
1514 elif m == 'dc':
1514 f1, f2, fa, move, anc = args
1515 f1, f2, fa, move, anc = args
1515 flags = p2[f2].flags()
1516 flags = p2[f2].flags()
1516 if repo.ui.promptchoice(
1517 if repo.ui.promptchoice(
1517 _("remote changed %s which local deleted\n"
1518 _("remote changed %s which local deleted\n"
1518 "use (c)hanged version or leave (d)eleted?"
1519 "use (c)hanged version or leave (d)eleted?"
1519 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1520 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1520 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1521 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1521 else:
1522 else:
1522 del actionbyfile[f]
1523 del actionbyfile[f]
1523
1524
1524 # Convert to dictionary-of-lists format
1525 # Convert to dictionary-of-lists format
1525 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1526 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1526 for f, (m, args, msg) in actionbyfile.iteritems():
1527 for f, (m, args, msg) in actionbyfile.iteritems():
1527 if m not in actions:
1528 if m not in actions:
1528 actions[m] = []
1529 actions[m] = []
1529 actions[m].append((f, args, msg))
1530 actions[m].append((f, args, msg))
1530
1531
1531 if not util.checkcase(repo.path):
1532 if not util.checkcase(repo.path):
1532 # check collision between files only in p2 for clean update
1533 # check collision between files only in p2 for clean update
1533 if (not branchmerge and
1534 if (not branchmerge and
1534 (force or not wc.dirty(missing=True, branch=False))):
1535 (force or not wc.dirty(missing=True, branch=False))):
1535 _checkcollision(repo, p2.manifest(), None)
1536 _checkcollision(repo, p2.manifest(), None)
1536 else:
1537 else:
1537 _checkcollision(repo, wc.manifest(), actions)
1538 _checkcollision(repo, wc.manifest(), actions)
1538
1539
1539 # divergent renames
1540 # divergent renames
1540 for f, fl in sorted(diverge.iteritems()):
1541 for f, fl in sorted(diverge.iteritems()):
1541 repo.ui.warn(_("note: possible conflict - %s was renamed "
1542 repo.ui.warn(_("note: possible conflict - %s was renamed "
1542 "multiple times to:\n") % f)
1543 "multiple times to:\n") % f)
1543 for nf in fl:
1544 for nf in fl:
1544 repo.ui.warn(" %s\n" % nf)
1545 repo.ui.warn(" %s\n" % nf)
1545
1546
1546 # rename and delete
1547 # rename and delete
1547 for f, fl in sorted(renamedelete.iteritems()):
1548 for f, fl in sorted(renamedelete.iteritems()):
1548 repo.ui.warn(_("note: possible conflict - %s was deleted "
1549 repo.ui.warn(_("note: possible conflict - %s was deleted "
1549 "and renamed to:\n") % f)
1550 "and renamed to:\n") % f)
1550 for nf in fl:
1551 for nf in fl:
1551 repo.ui.warn(" %s\n" % nf)
1552 repo.ui.warn(" %s\n" % nf)
1552
1553
1553 ### apply phase
1554 ### apply phase
1554 if not branchmerge: # just jump to the new rev
1555 if not branchmerge: # just jump to the new rev
1555 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1556 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1556 if not partial:
1557 if not partial:
1557 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1558 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1558 # note that we're in the middle of an update
1559 # note that we're in the middle of an update
1559 repo.vfs.write('updatestate', p2.hex())
1560 repo.vfs.write('updatestate', p2.hex())
1560
1561
1561 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1562 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1562
1563
1563 if not partial:
1564 if not partial:
1564 repo.dirstate.beginparentchange()
1565 repo.dirstate.beginparentchange()
1565 repo.setparents(fp1, fp2)
1566 repo.setparents(fp1, fp2)
1566 recordupdates(repo, actions, branchmerge)
1567 recordupdates(repo, actions, branchmerge)
1567 # update completed, clear state
1568 # update completed, clear state
1568 util.unlink(repo.join('updatestate'))
1569 util.unlink(repo.join('updatestate'))
1569
1570
1570 if not branchmerge:
1571 if not branchmerge:
1571 repo.dirstate.setbranch(p2.branch())
1572 repo.dirstate.setbranch(p2.branch())
1572 repo.dirstate.endparentchange()
1573 repo.dirstate.endparentchange()
1573
1574
1574 if not partial:
1575 if not partial:
1575 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1576 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1576 return stats
1577 return stats
1577
1578
1578 def graft(repo, ctx, pctx, labels, keepparent=False):
1579 def graft(repo, ctx, pctx, labels, keepparent=False):
1579 """Do a graft-like merge.
1580 """Do a graft-like merge.
1580
1581
1581 This is a merge where the merge ancestor is chosen such that one
1582 This is a merge where the merge ancestor is chosen such that one
1582 or more changesets are grafted onto the current changeset. In
1583 or more changesets are grafted onto the current changeset. In
1583 addition to the merge, this fixes up the dirstate to include only
1584 addition to the merge, this fixes up the dirstate to include only
1584 a single parent (if keepparent is False) and tries to duplicate any
1585 a single parent (if keepparent is False) and tries to duplicate any
1585 renames/copies appropriately.
1586 renames/copies appropriately.
1586
1587
1587 ctx - changeset to rebase
1588 ctx - changeset to rebase
1588 pctx - merge base, usually ctx.p1()
1589 pctx - merge base, usually ctx.p1()
1589 labels - merge labels eg ['local', 'graft']
1590 labels - merge labels eg ['local', 'graft']
1590 keepparent - keep second parent if any
1591 keepparent - keep second parent if any
1591
1592
1592 """
1593 """
1593 # If we're grafting a descendant onto an ancestor, be sure to pass
1594 # If we're grafting a descendant onto an ancestor, be sure to pass
1594 # mergeancestor=True to update. This does two things: 1) allows the merge if
1595 # mergeancestor=True to update. This does two things: 1) allows the merge if
1595 # the destination is the same as the parent of the ctx (so we can use graft
1596 # the destination is the same as the parent of the ctx (so we can use graft
1596 # to copy commits), and 2) informs update that the incoming changes are
1597 # to copy commits), and 2) informs update that the incoming changes are
1597 # newer than the destination so it doesn't prompt about "remote changed foo
1598 # newer than the destination so it doesn't prompt about "remote changed foo
1598 # which local deleted".
1599 # which local deleted".
1599 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1600 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1600
1601
1601 stats = update(repo, ctx.node(), True, True, pctx.node(),
1602 stats = update(repo, ctx.node(), True, True, pctx.node(),
1602 mergeancestor=mergeancestor, labels=labels)
1603 mergeancestor=mergeancestor, labels=labels)
1603
1604
1604 pother = nullid
1605 pother = nullid
1605 parents = ctx.parents()
1606 parents = ctx.parents()
1606 if keepparent and len(parents) == 2 and pctx in parents:
1607 if keepparent and len(parents) == 2 and pctx in parents:
1607 parents.remove(pctx)
1608 parents.remove(pctx)
1608 pother = parents[0].node()
1609 pother = parents[0].node()
1609
1610
1610 repo.dirstate.beginparentchange()
1611 repo.dirstate.beginparentchange()
1611 repo.setparents(repo['.'].node(), pother)
1612 repo.setparents(repo['.'].node(), pother)
1612 repo.dirstate.write(repo.currenttransaction())
1613 repo.dirstate.write(repo.currenttransaction())
1613 # fix up dirstate for copies and renames
1614 # fix up dirstate for copies and renames
1614 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1615 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1615 repo.dirstate.endparentchange()
1616 repo.dirstate.endparentchange()
1616 return stats
1617 return stats
General Comments 0
You need to be logged in to leave comments. Login now