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