##// END OF EJS Templates
merge.recordupdates: don't require action keys to be present in dict...
Siddharth Agarwal -
r27087:98fc5837 default
parent child Browse files
Show More
@@ -1,1456 +1,1456 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12 import shutil
12 import shutil
13 import struct
13 import struct
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 bin,
17 bin,
18 hex,
18 hex,
19 nullhex,
19 nullhex,
20 nullid,
20 nullid,
21 nullrev,
21 nullrev,
22 )
22 )
23 from . import (
23 from . import (
24 copies,
24 copies,
25 destutil,
25 destutil,
26 error,
26 error,
27 filemerge,
27 filemerge,
28 obsolete,
28 obsolete,
29 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):
512 def actions(self):
513 """return lists of actions to perform on the dirstate"""
513 """return lists of actions to perform on the dirstate"""
514 actions = {'r': [], 'a': [], 'g': []}
514 actions = {'r': [], 'a': [], 'g': []}
515 for f, (r, action) in self._results.iteritems():
515 for f, (r, action) in self._results.iteritems():
516 if action is not None:
516 if action is not None:
517 actions[action].append((f, None, "merge result"))
517 actions[action].append((f, None, "merge result"))
518 return actions
518 return actions
519
519
520 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
520 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
521 if f2 is None:
521 if f2 is None:
522 f2 = f
522 f2 = f
523 return (os.path.isfile(repo.wjoin(f))
523 return (os.path.isfile(repo.wjoin(f))
524 and repo.wvfs.audit.check(f)
524 and repo.wvfs.audit.check(f)
525 and repo.dirstate.normalize(f) not in repo.dirstate
525 and repo.dirstate.normalize(f) not in repo.dirstate
526 and mctx[f2].cmp(wctx[f]))
526 and mctx[f2].cmp(wctx[f]))
527
527
528 def _checkunknownfiles(repo, wctx, mctx, force, actions):
528 def _checkunknownfiles(repo, wctx, mctx, force, actions):
529 """
529 """
530 Considers any actions that care about the presence of conflicting unknown
530 Considers any actions that care about the presence of conflicting unknown
531 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
532 choose a different action.
532 choose a different action.
533 """
533 """
534 aborts = []
534 aborts = []
535 if not force:
535 if not force:
536 for f, (m, args, msg) in actions.iteritems():
536 for f, (m, args, msg) in actions.iteritems():
537 if m in ('c', 'dc'):
537 if m in ('c', 'dc'):
538 if _checkunknownfile(repo, wctx, mctx, f):
538 if _checkunknownfile(repo, wctx, mctx, f):
539 aborts.append(f)
539 aborts.append(f)
540 elif m == 'dg':
540 elif m == 'dg':
541 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
541 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
542 aborts.append(f)
542 aborts.append(f)
543
543
544 for f in sorted(aborts):
544 for f in sorted(aborts):
545 repo.ui.warn(_("%s: untracked file differs\n") % f)
545 repo.ui.warn(_("%s: untracked file differs\n") % f)
546 if aborts:
546 if aborts:
547 raise error.Abort(_("untracked files in working directory differ "
547 raise error.Abort(_("untracked files in working directory differ "
548 "from files in requested revision"))
548 "from files in requested revision"))
549
549
550 for f, (m, args, msg) in actions.iteritems():
550 for f, (m, args, msg) in actions.iteritems():
551 if m == 'c':
551 if m == 'c':
552 actions[f] = ('g', args, msg)
552 actions[f] = ('g', args, msg)
553 elif m == 'cm':
553 elif m == 'cm':
554 fl2, anc = args
554 fl2, anc = args
555 different = _checkunknownfile(repo, wctx, mctx, f)
555 different = _checkunknownfile(repo, wctx, mctx, f)
556 if different:
556 if different:
557 actions[f] = ('m', (f, f, None, False, anc),
557 actions[f] = ('m', (f, f, None, False, anc),
558 "remote differs from untracked local")
558 "remote differs from untracked local")
559 else:
559 else:
560 actions[f] = ('g', (fl2,), "remote created")
560 actions[f] = ('g', (fl2,), "remote created")
561
561
562 def _forgetremoved(wctx, mctx, branchmerge):
562 def _forgetremoved(wctx, mctx, branchmerge):
563 """
563 """
564 Forget removed files
564 Forget removed files
565
565
566 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
567 neither the working directory nor the target rev has the file,
567 neither the working directory nor the target rev has the file,
568 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
569 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
570 manifest.
570 manifest.
571
571
572 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
573 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
574 as removed.
574 as removed.
575 """
575 """
576
576
577 actions = {}
577 actions = {}
578 m = 'f'
578 m = 'f'
579 if branchmerge:
579 if branchmerge:
580 m = 'r'
580 m = 'r'
581 for f in wctx.deleted():
581 for f in wctx.deleted():
582 if f not in mctx:
582 if f not in mctx:
583 actions[f] = m, None, "forget deleted"
583 actions[f] = m, None, "forget deleted"
584
584
585 if not branchmerge:
585 if not branchmerge:
586 for f in wctx.removed():
586 for f in wctx.removed():
587 if f not in mctx:
587 if f not in mctx:
588 actions[f] = 'f', None, "forget removed"
588 actions[f] = 'f', None, "forget removed"
589
589
590 return actions
590 return actions
591
591
592 def _checkcollision(repo, wmf, actions):
592 def _checkcollision(repo, wmf, actions):
593 # build provisional merged manifest up
593 # build provisional merged manifest up
594 pmmf = set(wmf)
594 pmmf = set(wmf)
595
595
596 if actions:
596 if actions:
597 # k, dr, e and rd are no-op
597 # k, dr, e and rd are no-op
598 for m in 'a', 'f', 'g', 'cd', 'dc':
598 for m in 'a', 'f', 'g', 'cd', 'dc':
599 for f, args, msg in actions[m]:
599 for f, args, msg in actions[m]:
600 pmmf.add(f)
600 pmmf.add(f)
601 for f, args, msg in actions['r']:
601 for f, args, msg in actions['r']:
602 pmmf.discard(f)
602 pmmf.discard(f)
603 for f, args, msg in actions['dm']:
603 for f, args, msg in actions['dm']:
604 f2, flags = args
604 f2, flags = args
605 pmmf.discard(f2)
605 pmmf.discard(f2)
606 pmmf.add(f)
606 pmmf.add(f)
607 for f, args, msg in actions['dg']:
607 for f, args, msg in actions['dg']:
608 pmmf.add(f)
608 pmmf.add(f)
609 for f, args, msg in actions['m']:
609 for f, args, msg in actions['m']:
610 f1, f2, fa, move, anc = args
610 f1, f2, fa, move, anc = args
611 if move:
611 if move:
612 pmmf.discard(f1)
612 pmmf.discard(f1)
613 pmmf.add(f)
613 pmmf.add(f)
614
614
615 # check case-folding collision in provisional merged manifest
615 # check case-folding collision in provisional merged manifest
616 foldmap = {}
616 foldmap = {}
617 for f in sorted(pmmf):
617 for f in sorted(pmmf):
618 fold = util.normcase(f)
618 fold = util.normcase(f)
619 if fold in foldmap:
619 if fold in foldmap:
620 raise error.Abort(_("case-folding collision between %s and %s")
620 raise error.Abort(_("case-folding collision between %s and %s")
621 % (f, foldmap[fold]))
621 % (f, foldmap[fold]))
622 foldmap[fold] = f
622 foldmap[fold] = f
623
623
624 # check case-folding of directories
624 # check case-folding of directories
625 foldprefix = unfoldprefix = lastfull = ''
625 foldprefix = unfoldprefix = lastfull = ''
626 for fold, f in sorted(foldmap.items()):
626 for fold, f in sorted(foldmap.items()):
627 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
627 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
628 # the folded prefix matches but actual casing is different
628 # the folded prefix matches but actual casing is different
629 raise error.Abort(_("case-folding collision between "
629 raise error.Abort(_("case-folding collision between "
630 "%s and directory of %s") % (lastfull, f))
630 "%s and directory of %s") % (lastfull, f))
631 foldprefix = fold + '/'
631 foldprefix = fold + '/'
632 unfoldprefix = f + '/'
632 unfoldprefix = f + '/'
633 lastfull = f
633 lastfull = f
634
634
635 def driverpreprocess(repo, ms, wctx, labels=None):
635 def driverpreprocess(repo, ms, wctx, labels=None):
636 """run the preprocess step of the merge driver, if any
636 """run the preprocess step of the merge driver, if any
637
637
638 This is currently not implemented -- it's an extension point."""
638 This is currently not implemented -- it's an extension point."""
639 return True
639 return True
640
640
641 def driverconclude(repo, ms, wctx, labels=None):
641 def driverconclude(repo, ms, wctx, labels=None):
642 """run the conclude step of the merge driver, if any
642 """run the conclude step of the merge driver, if any
643
643
644 This is currently not implemented -- it's an extension point."""
644 This is currently not implemented -- it's an extension point."""
645 return True
645 return True
646
646
647 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
647 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
648 acceptremote, followcopies):
648 acceptremote, followcopies):
649 """
649 """
650 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
651
651
652 branchmerge and force are as passed in to update
652 branchmerge and force are as passed in to update
653 partial = function to filter file lists
653 partial = function to filter file lists
654 acceptremote = accept the incoming changes without prompting
654 acceptremote = accept the incoming changes without prompting
655 """
655 """
656
656
657 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
657 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
658
658
659 # 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
660 [x.manifest() for x in
660 [x.manifest() for x in
661 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
661 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
662
662
663 if followcopies:
663 if followcopies:
664 ret = copies.mergecopies(repo, wctx, p2, pa)
664 ret = copies.mergecopies(repo, wctx, p2, pa)
665 copy, movewithdir, diverge, renamedelete = ret
665 copy, movewithdir, diverge, renamedelete = ret
666
666
667 repo.ui.note(_("resolving manifests\n"))
667 repo.ui.note(_("resolving manifests\n"))
668 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
668 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
669 % (bool(branchmerge), bool(force), bool(partial)))
669 % (bool(branchmerge), bool(force), bool(partial)))
670 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))
671
671
672 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
672 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
673 copied = set(copy.values())
673 copied = set(copy.values())
674 copied.update(movewithdir.values())
674 copied.update(movewithdir.values())
675
675
676 if '.hgsubstate' in m1:
676 if '.hgsubstate' in m1:
677 # check whether sub state is modified
677 # check whether sub state is modified
678 for s in sorted(wctx.substate):
678 for s in sorted(wctx.substate):
679 if wctx.sub(s).dirty():
679 if wctx.sub(s).dirty():
680 m1['.hgsubstate'] += '+'
680 m1['.hgsubstate'] += '+'
681 break
681 break
682
682
683 # Compare manifests
683 # Compare manifests
684 diff = m1.diff(m2)
684 diff = m1.diff(m2)
685
685
686 actions = {}
686 actions = {}
687 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
687 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
688 if partial and not partial(f):
688 if partial and not partial(f):
689 continue
689 continue
690 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
691 if f not in ma:
691 if f not in ma:
692 fa = copy.get(f, None)
692 fa = copy.get(f, None)
693 if fa is not None:
693 if fa is not None:
694 actions[f] = ('m', (f, f, fa, False, pa.node()),
694 actions[f] = ('m', (f, f, fa, False, pa.node()),
695 "both renamed from " + fa)
695 "both renamed from " + fa)
696 else:
696 else:
697 actions[f] = ('m', (f, f, None, False, pa.node()),
697 actions[f] = ('m', (f, f, None, False, pa.node()),
698 "both created")
698 "both created")
699 else:
699 else:
700 a = ma[f]
700 a = ma[f]
701 fla = ma.flags(f)
701 fla = ma.flags(f)
702 nol = 'l' not in fl1 + fl2 + fla
702 nol = 'l' not in fl1 + fl2 + fla
703 if n2 == a and fl2 == fla:
703 if n2 == a and fl2 == fla:
704 actions[f] = ('k' , (), "remote unchanged")
704 actions[f] = ('k' , (), "remote unchanged")
705 elif n1 == a and fl1 == fla: # local unchanged - use remote
705 elif n1 == a and fl1 == fla: # local unchanged - use remote
706 if n1 == n2: # optimization: keep local content
706 if n1 == n2: # optimization: keep local content
707 actions[f] = ('e', (fl2,), "update permissions")
707 actions[f] = ('e', (fl2,), "update permissions")
708 else:
708 else:
709 actions[f] = ('g', (fl2,), "remote is newer")
709 actions[f] = ('g', (fl2,), "remote is newer")
710 elif nol and n2 == a: # remote only changed 'x'
710 elif nol and n2 == a: # remote only changed 'x'
711 actions[f] = ('e', (fl2,), "update permissions")
711 actions[f] = ('e', (fl2,), "update permissions")
712 elif nol and n1 == a: # local only changed 'x'
712 elif nol and n1 == a: # local only changed 'x'
713 actions[f] = ('g', (fl1,), "remote is newer")
713 actions[f] = ('g', (fl1,), "remote is newer")
714 else: # both changed something
714 else: # both changed something
715 actions[f] = ('m', (f, f, f, False, pa.node()),
715 actions[f] = ('m', (f, f, f, False, pa.node()),
716 "versions differ")
716 "versions differ")
717 elif n1: # file exists only on local side
717 elif n1: # file exists only on local side
718 if f in copied:
718 if f in copied:
719 pass # we'll deal with it on m2 side
719 pass # we'll deal with it on m2 side
720 elif f in movewithdir: # directory rename, move local
720 elif f in movewithdir: # directory rename, move local
721 f2 = movewithdir[f]
721 f2 = movewithdir[f]
722 if f2 in m2:
722 if f2 in m2:
723 actions[f2] = ('m', (f, f2, None, True, pa.node()),
723 actions[f2] = ('m', (f, f2, None, True, pa.node()),
724 "remote directory rename, both created")
724 "remote directory rename, both created")
725 else:
725 else:
726 actions[f2] = ('dm', (f, fl1),
726 actions[f2] = ('dm', (f, fl1),
727 "remote directory rename - move from " + f)
727 "remote directory rename - move from " + f)
728 elif f in copy:
728 elif f in copy:
729 f2 = copy[f]
729 f2 = copy[f]
730 actions[f] = ('m', (f, f2, f2, False, pa.node()),
730 actions[f] = ('m', (f, f2, f2, False, pa.node()),
731 "local copied/moved from " + f2)
731 "local copied/moved from " + f2)
732 elif f in ma: # clean, a different, no remote
732 elif f in ma: # clean, a different, no remote
733 if n1 != ma[f]:
733 if n1 != ma[f]:
734 if acceptremote:
734 if acceptremote:
735 actions[f] = ('r', None, "remote delete")
735 actions[f] = ('r', None, "remote delete")
736 else:
736 else:
737 actions[f] = ('cd', (f, None, f, False, pa.node()),
737 actions[f] = ('cd', (f, None, f, False, pa.node()),
738 "prompt changed/deleted")
738 "prompt changed/deleted")
739 elif n1[20:] == 'a':
739 elif n1[20:] == 'a':
740 # This extra 'a' is added by working copy manifest to mark
740 # This extra 'a' is added by working copy manifest to mark
741 # the file as locally added. We should forget it instead of
741 # the file as locally added. We should forget it instead of
742 # deleting it.
742 # deleting it.
743 actions[f] = ('f', None, "remote deleted")
743 actions[f] = ('f', None, "remote deleted")
744 else:
744 else:
745 actions[f] = ('r', None, "other deleted")
745 actions[f] = ('r', None, "other deleted")
746 elif n2: # file exists only on remote side
746 elif n2: # file exists only on remote side
747 if f in copied:
747 if f in copied:
748 pass # we'll deal with it on m1 side
748 pass # we'll deal with it on m1 side
749 elif f in movewithdir:
749 elif f in movewithdir:
750 f2 = movewithdir[f]
750 f2 = movewithdir[f]
751 if f2 in m1:
751 if f2 in m1:
752 actions[f2] = ('m', (f2, f, None, False, pa.node()),
752 actions[f2] = ('m', (f2, f, None, False, pa.node()),
753 "local directory rename, both created")
753 "local directory rename, both created")
754 else:
754 else:
755 actions[f2] = ('dg', (f, fl2),
755 actions[f2] = ('dg', (f, fl2),
756 "local directory rename - get from " + f)
756 "local directory rename - get from " + f)
757 elif f in copy:
757 elif f in copy:
758 f2 = copy[f]
758 f2 = copy[f]
759 if f2 in m2:
759 if f2 in m2:
760 actions[f] = ('m', (f2, f, f2, False, pa.node()),
760 actions[f] = ('m', (f2, f, f2, False, pa.node()),
761 "remote copied from " + f2)
761 "remote copied from " + f2)
762 else:
762 else:
763 actions[f] = ('m', (f2, f, f2, True, pa.node()),
763 actions[f] = ('m', (f2, f, f2, True, pa.node()),
764 "remote moved from " + f2)
764 "remote moved from " + f2)
765 elif f not in ma:
765 elif f not in ma:
766 # local unknown, remote created: the logic is described by the
766 # local unknown, remote created: the logic is described by the
767 # following table:
767 # following table:
768 #
768 #
769 # force branchmerge different | action
769 # force branchmerge different | action
770 # n * * | create
770 # n * * | create
771 # y n * | create
771 # y n * | create
772 # y y n | create
772 # y y n | create
773 # y y y | merge
773 # y y y | merge
774 #
774 #
775 # Checking whether the files are different is expensive, so we
775 # Checking whether the files are different is expensive, so we
776 # don't do that when we can avoid it.
776 # don't do that when we can avoid it.
777 if not force:
777 if not force:
778 actions[f] = ('c', (fl2,), "remote created")
778 actions[f] = ('c', (fl2,), "remote created")
779 elif not branchmerge:
779 elif not branchmerge:
780 actions[f] = ('c', (fl2,), "remote created")
780 actions[f] = ('c', (fl2,), "remote created")
781 else:
781 else:
782 actions[f] = ('cm', (fl2, pa.node()),
782 actions[f] = ('cm', (fl2, pa.node()),
783 "remote created, get or merge")
783 "remote created, get or merge")
784 elif n2 != ma[f]:
784 elif n2 != ma[f]:
785 if acceptremote:
785 if acceptremote:
786 actions[f] = ('c', (fl2,), "remote recreating")
786 actions[f] = ('c', (fl2,), "remote recreating")
787 else:
787 else:
788 actions[f] = ('dc', (None, f, f, False, pa.node()),
788 actions[f] = ('dc', (None, f, f, False, pa.node()),
789 "prompt deleted/changed")
789 "prompt deleted/changed")
790
790
791 return actions, diverge, renamedelete
791 return actions, diverge, renamedelete
792
792
793 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
793 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
794 """Resolves false conflicts where the nodeid changed but the content
794 """Resolves false conflicts where the nodeid changed but the content
795 remained the same."""
795 remained the same."""
796
796
797 for f, (m, args, msg) in actions.items():
797 for f, (m, args, msg) in actions.items():
798 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]):
799 # local did change but ended up with same content
799 # local did change but ended up with same content
800 actions[f] = 'r', None, "prompt same"
800 actions[f] = 'r', None, "prompt same"
801 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]):
802 # remote did change but ended up with same content
802 # remote did change but ended up with same content
803 del actions[f] # don't get = keep local deleted
803 del actions[f] # don't get = keep local deleted
804
804
805 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
805 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
806 acceptremote, followcopies):
806 acceptremote, followcopies):
807 "Calculate the actions needed to merge mctx into wctx using ancestors"
807 "Calculate the actions needed to merge mctx into wctx using ancestors"
808
808
809 if len(ancestors) == 1: # default
809 if len(ancestors) == 1: # default
810 actions, diverge, renamedelete = manifestmerge(
810 actions, diverge, renamedelete = manifestmerge(
811 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
811 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
812 acceptremote, followcopies)
812 acceptremote, followcopies)
813 _checkunknownfiles(repo, wctx, mctx, force, actions)
813 _checkunknownfiles(repo, wctx, mctx, force, actions)
814
814
815 else: # only when merge.preferancestor=* - the default
815 else: # only when merge.preferancestor=* - the default
816 repo.ui.note(
816 repo.ui.note(
817 _("note: merging %s and %s using bids from ancestors %s\n") %
817 _("note: merging %s and %s using bids from ancestors %s\n") %
818 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
818 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
819
819
820 # Call for bids
820 # Call for bids
821 fbids = {} # mapping filename to bids (action method to list af actions)
821 fbids = {} # mapping filename to bids (action method to list af actions)
822 diverge, renamedelete = None, None
822 diverge, renamedelete = None, None
823 for ancestor in ancestors:
823 for ancestor in ancestors:
824 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
824 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
825 actions, diverge1, renamedelete1 = manifestmerge(
825 actions, diverge1, renamedelete1 = manifestmerge(
826 repo, wctx, mctx, ancestor, branchmerge, force, partial,
826 repo, wctx, mctx, ancestor, branchmerge, force, partial,
827 acceptremote, followcopies)
827 acceptremote, followcopies)
828 _checkunknownfiles(repo, wctx, mctx, force, actions)
828 _checkunknownfiles(repo, wctx, mctx, force, actions)
829
829
830 # Track the shortest set of warning on the theory that bid
830 # Track the shortest set of warning on the theory that bid
831 # merge will correctly incorporate more information
831 # merge will correctly incorporate more information
832 if diverge is None or len(diverge1) < len(diverge):
832 if diverge is None or len(diverge1) < len(diverge):
833 diverge = diverge1
833 diverge = diverge1
834 if renamedelete is None or len(renamedelete) < len(renamedelete1):
834 if renamedelete is None or len(renamedelete) < len(renamedelete1):
835 renamedelete = renamedelete1
835 renamedelete = renamedelete1
836
836
837 for f, a in sorted(actions.iteritems()):
837 for f, a in sorted(actions.iteritems()):
838 m, args, msg = a
838 m, args, msg = a
839 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
839 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
840 if f in fbids:
840 if f in fbids:
841 d = fbids[f]
841 d = fbids[f]
842 if m in d:
842 if m in d:
843 d[m].append(a)
843 d[m].append(a)
844 else:
844 else:
845 d[m] = [a]
845 d[m] = [a]
846 else:
846 else:
847 fbids[f] = {m: [a]}
847 fbids[f] = {m: [a]}
848
848
849 # Pick the best bid for each file
849 # Pick the best bid for each file
850 repo.ui.note(_('\nauction for merging merge bids\n'))
850 repo.ui.note(_('\nauction for merging merge bids\n'))
851 actions = {}
851 actions = {}
852 for f, bids in sorted(fbids.items()):
852 for f, bids in sorted(fbids.items()):
853 # bids is a mapping from action method to list af actions
853 # bids is a mapping from action method to list af actions
854 # Consensus?
854 # Consensus?
855 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
856 m, l = bids.items()[0]
856 m, l = bids.items()[0]
857 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
858 repo.ui.note(" %s: consensus for %s\n" % (f, m))
858 repo.ui.note(" %s: consensus for %s\n" % (f, m))
859 actions[f] = l[0]
859 actions[f] = l[0]
860 continue
860 continue
861 # If keep is an option, just do it.
861 # If keep is an option, just do it.
862 if 'k' in bids:
862 if 'k' in bids:
863 repo.ui.note(" %s: picking 'keep' action\n" % f)
863 repo.ui.note(" %s: picking 'keep' action\n" % f)
864 actions[f] = bids['k'][0]
864 actions[f] = bids['k'][0]
865 continue
865 continue
866 # 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.
867 if 'g' in bids:
867 if 'g' in bids:
868 ga0 = bids['g'][0]
868 ga0 = bids['g'][0]
869 if all(a == ga0 for a in bids['g'][1:]):
869 if all(a == ga0 for a in bids['g'][1:]):
870 repo.ui.note(" %s: picking 'get' action\n" % f)
870 repo.ui.note(" %s: picking 'get' action\n" % f)
871 actions[f] = ga0
871 actions[f] = ga0
872 continue
872 continue
873 # TODO: Consider other simple actions such as mode changes
873 # TODO: Consider other simple actions such as mode changes
874 # Handle inefficient democrazy.
874 # Handle inefficient democrazy.
875 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
875 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
876 for m, l in sorted(bids.items()):
876 for m, l in sorted(bids.items()):
877 for _f, args, msg in l:
877 for _f, args, msg in l:
878 repo.ui.note(' %s -> %s\n' % (msg, m))
878 repo.ui.note(' %s -> %s\n' % (msg, m))
879 # Pick random action. TODO: Instead, prompt user when resolving
879 # Pick random action. TODO: Instead, prompt user when resolving
880 m, l = bids.items()[0]
880 m, l = bids.items()[0]
881 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
881 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
882 (f, m))
882 (f, m))
883 actions[f] = l[0]
883 actions[f] = l[0]
884 continue
884 continue
885 repo.ui.note(_('end of auction\n\n'))
885 repo.ui.note(_('end of auction\n\n'))
886
886
887 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
887 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
888
888
889 if wctx.rev() is None:
889 if wctx.rev() is None:
890 fractions = _forgetremoved(wctx, mctx, branchmerge)
890 fractions = _forgetremoved(wctx, mctx, branchmerge)
891 actions.update(fractions)
891 actions.update(fractions)
892
892
893 return actions, diverge, renamedelete
893 return actions, diverge, renamedelete
894
894
895 def batchremove(repo, actions):
895 def batchremove(repo, actions):
896 """apply removes to the working directory
896 """apply removes to the working directory
897
897
898 yields tuples for progress updates
898 yields tuples for progress updates
899 """
899 """
900 verbose = repo.ui.verbose
900 verbose = repo.ui.verbose
901 unlink = util.unlinkpath
901 unlink = util.unlinkpath
902 wjoin = repo.wjoin
902 wjoin = repo.wjoin
903 audit = repo.wvfs.audit
903 audit = repo.wvfs.audit
904 i = 0
904 i = 0
905 for f, args, msg in actions:
905 for f, args, msg in actions:
906 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
906 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
907 if verbose:
907 if verbose:
908 repo.ui.note(_("removing %s\n") % f)
908 repo.ui.note(_("removing %s\n") % f)
909 audit(f)
909 audit(f)
910 try:
910 try:
911 unlink(wjoin(f), ignoremissing=True)
911 unlink(wjoin(f), ignoremissing=True)
912 except OSError as inst:
912 except OSError as inst:
913 repo.ui.warn(_("update failed to remove %s: %s!\n") %
913 repo.ui.warn(_("update failed to remove %s: %s!\n") %
914 (f, inst.strerror))
914 (f, inst.strerror))
915 if i == 100:
915 if i == 100:
916 yield i, f
916 yield i, f
917 i = 0
917 i = 0
918 i += 1
918 i += 1
919 if i > 0:
919 if i > 0:
920 yield i, f
920 yield i, f
921
921
922 def batchget(repo, mctx, actions):
922 def batchget(repo, mctx, actions):
923 """apply gets to the working directory
923 """apply gets to the working directory
924
924
925 mctx is the context to get from
925 mctx is the context to get from
926
926
927 yields tuples for progress updates
927 yields tuples for progress updates
928 """
928 """
929 verbose = repo.ui.verbose
929 verbose = repo.ui.verbose
930 fctx = mctx.filectx
930 fctx = mctx.filectx
931 wwrite = repo.wwrite
931 wwrite = repo.wwrite
932 i = 0
932 i = 0
933 for f, args, msg in actions:
933 for f, args, msg in actions:
934 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
934 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
935 if verbose:
935 if verbose:
936 repo.ui.note(_("getting %s\n") % f)
936 repo.ui.note(_("getting %s\n") % f)
937 wwrite(f, fctx(f).data(), args[0])
937 wwrite(f, fctx(f).data(), args[0])
938 if i == 100:
938 if i == 100:
939 yield i, f
939 yield i, f
940 i = 0
940 i = 0
941 i += 1
941 i += 1
942 if i > 0:
942 if i > 0:
943 yield i, f
943 yield i, f
944
944
945 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
945 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
946 """apply the merge action list to the working directory
946 """apply the merge action list to the working directory
947
947
948 wctx is the working copy context
948 wctx is the working copy context
949 mctx is the context to be merged into the working copy
949 mctx is the context to be merged into the working copy
950
950
951 Return a tuple of counts (updated, merged, removed, unresolved) that
951 Return a tuple of counts (updated, merged, removed, unresolved) that
952 describes how many files were affected by the update.
952 describes how many files were affected by the update.
953 """
953 """
954
954
955 updated, merged, removed = 0, 0, 0
955 updated, merged, removed = 0, 0, 0
956 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
956 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
957 moves = []
957 moves = []
958 for m, l in actions.items():
958 for m, l in actions.items():
959 l.sort()
959 l.sort()
960
960
961 # prescan for merges
961 # prescan for merges
962 for f, args, msg in actions['m']:
962 for f, args, msg in actions['m']:
963 f1, f2, fa, move, anc = args
963 f1, f2, fa, move, anc = args
964 if f == '.hgsubstate': # merged internally
964 if f == '.hgsubstate': # merged internally
965 continue
965 continue
966 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))
967 fcl = wctx[f1]
967 fcl = wctx[f1]
968 fco = mctx[f2]
968 fco = mctx[f2]
969 actx = repo[anc]
969 actx = repo[anc]
970 if fa in actx:
970 if fa in actx:
971 fca = actx[fa]
971 fca = actx[fa]
972 else:
972 else:
973 fca = repo.filectx(f1, fileid=nullrev)
973 fca = repo.filectx(f1, fileid=nullrev)
974 ms.add(fcl, fco, fca, f)
974 ms.add(fcl, fco, fca, f)
975 if f1 != f and move:
975 if f1 != f and move:
976 moves.append(f1)
976 moves.append(f1)
977
977
978 audit = repo.wvfs.audit
978 audit = repo.wvfs.audit
979 _updating = _('updating')
979 _updating = _('updating')
980 _files = _('files')
980 _files = _('files')
981 progress = repo.ui.progress
981 progress = repo.ui.progress
982
982
983 # remove renamed files after safely stored
983 # remove renamed files after safely stored
984 for f in moves:
984 for f in moves:
985 if os.path.lexists(repo.wjoin(f)):
985 if os.path.lexists(repo.wjoin(f)):
986 repo.ui.debug("removing %s\n" % f)
986 repo.ui.debug("removing %s\n" % f)
987 audit(f)
987 audit(f)
988 util.unlinkpath(repo.wjoin(f))
988 util.unlinkpath(repo.wjoin(f))
989
989
990 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')
991
991
992 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
992 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
993 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
993 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
994
994
995 # remove in parallel (must come first)
995 # remove in parallel (must come first)
996 z = 0
996 z = 0
997 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
997 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
998 for i, item in prog:
998 for i, item in prog:
999 z += i
999 z += i
1000 progress(_updating, z, item=item, total=numupdates, unit=_files)
1000 progress(_updating, z, item=item, total=numupdates, unit=_files)
1001 removed = len(actions['r'])
1001 removed = len(actions['r'])
1002
1002
1003 # get in parallel
1003 # get in parallel
1004 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'])
1005 for i, item in prog:
1005 for i, item in prog:
1006 z += i
1006 z += i
1007 progress(_updating, z, item=item, total=numupdates, unit=_files)
1007 progress(_updating, z, item=item, total=numupdates, unit=_files)
1008 updated = len(actions['g'])
1008 updated = len(actions['g'])
1009
1009
1010 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1010 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1011 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1011 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
1012
1012
1013 # forget (manifest only, just log it) (must come first)
1013 # forget (manifest only, just log it) (must come first)
1014 for f, args, msg in actions['f']:
1014 for f, args, msg in actions['f']:
1015 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1015 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1016 z += 1
1016 z += 1
1017 progress(_updating, z, item=f, total=numupdates, unit=_files)
1017 progress(_updating, z, item=f, total=numupdates, unit=_files)
1018
1018
1019 # re-add (manifest only, just log it)
1019 # re-add (manifest only, just log it)
1020 for f, args, msg in actions['a']:
1020 for f, args, msg in actions['a']:
1021 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1021 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1022 z += 1
1022 z += 1
1023 progress(_updating, z, item=f, total=numupdates, unit=_files)
1023 progress(_updating, z, item=f, total=numupdates, unit=_files)
1024
1024
1025 # keep (noop, just log it)
1025 # keep (noop, just log it)
1026 for f, args, msg in actions['k']:
1026 for f, args, msg in actions['k']:
1027 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1027 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1028 # no progress
1028 # no progress
1029
1029
1030 # directory rename, move local
1030 # directory rename, move local
1031 for f, args, msg in actions['dm']:
1031 for f, args, msg in actions['dm']:
1032 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1032 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1033 z += 1
1033 z += 1
1034 progress(_updating, z, item=f, total=numupdates, unit=_files)
1034 progress(_updating, z, item=f, total=numupdates, unit=_files)
1035 f0, flags = args
1035 f0, flags = args
1036 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1036 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1037 audit(f)
1037 audit(f)
1038 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1038 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1039 util.unlinkpath(repo.wjoin(f0))
1039 util.unlinkpath(repo.wjoin(f0))
1040 updated += 1
1040 updated += 1
1041
1041
1042 # local directory rename, get
1042 # local directory rename, get
1043 for f, args, msg in actions['dg']:
1043 for f, args, msg in actions['dg']:
1044 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1044 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1045 z += 1
1045 z += 1
1046 progress(_updating, z, item=f, total=numupdates, unit=_files)
1046 progress(_updating, z, item=f, total=numupdates, unit=_files)
1047 f0, flags = args
1047 f0, flags = args
1048 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1048 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1049 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1049 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1050 updated += 1
1050 updated += 1
1051
1051
1052 # exec
1052 # exec
1053 for f, args, msg in actions['e']:
1053 for f, args, msg in actions['e']:
1054 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1054 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1055 z += 1
1055 z += 1
1056 progress(_updating, z, item=f, total=numupdates, unit=_files)
1056 progress(_updating, z, item=f, total=numupdates, unit=_files)
1057 flags, = args
1057 flags, = args
1058 audit(f)
1058 audit(f)
1059 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1059 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1060 updated += 1
1060 updated += 1
1061
1061
1062 mergeactions = actions['m']
1062 mergeactions = actions['m']
1063 # 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
1064 # 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
1065 # True
1065 # True
1066 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1066 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1067
1067
1068 if usemergedriver:
1068 if usemergedriver:
1069 ms.commit()
1069 ms.commit()
1070 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1070 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1071 # the driver might leave some files unresolved
1071 # the driver might leave some files unresolved
1072 unresolvedf = set(ms.unresolved())
1072 unresolvedf = set(ms.unresolved())
1073 if not proceed:
1073 if not proceed:
1074 # 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
1075 # error out
1075 # error out
1076 return updated, merged, removed, max(len(unresolvedf), 1)
1076 return updated, merged, removed, max(len(unresolvedf), 1)
1077 newactions = []
1077 newactions = []
1078 for f, args, msg in mergeactions:
1078 for f, args, msg in mergeactions:
1079 if f in unresolvedf:
1079 if f in unresolvedf:
1080 newactions.append((f, args, msg))
1080 newactions.append((f, args, msg))
1081 mergeactions = newactions
1081 mergeactions = newactions
1082
1082
1083 # premerge
1083 # premerge
1084 tocomplete = []
1084 tocomplete = []
1085 for f, args, msg in mergeactions:
1085 for f, args, msg in mergeactions:
1086 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1086 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1087 z += 1
1087 z += 1
1088 progress(_updating, z, item=f, total=numupdates, unit=_files)
1088 progress(_updating, z, item=f, total=numupdates, unit=_files)
1089 if f == '.hgsubstate': # subrepo states need updating
1089 if f == '.hgsubstate': # subrepo states need updating
1090 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1090 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1091 overwrite)
1091 overwrite)
1092 continue
1092 continue
1093 audit(f)
1093 audit(f)
1094 complete, r = ms.preresolve(f, wctx, labels=labels)
1094 complete, r = ms.preresolve(f, wctx, labels=labels)
1095 if not complete:
1095 if not complete:
1096 numupdates += 1
1096 numupdates += 1
1097 tocomplete.append((f, args, msg))
1097 tocomplete.append((f, args, msg))
1098
1098
1099 # merge
1099 # merge
1100 for f, args, msg in tocomplete:
1100 for f, args, msg in tocomplete:
1101 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1101 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1102 z += 1
1102 z += 1
1103 progress(_updating, z, item=f, total=numupdates, unit=_files)
1103 progress(_updating, z, item=f, total=numupdates, unit=_files)
1104 ms.resolve(f, wctx, labels=labels)
1104 ms.resolve(f, wctx, labels=labels)
1105
1105
1106 ms.commit()
1106 ms.commit()
1107
1107
1108 unresolved = ms.unresolvedcount()
1108 unresolved = ms.unresolvedcount()
1109
1109
1110 if usemergedriver and not unresolved and ms.mdstate() != 's':
1110 if usemergedriver and not unresolved and ms.mdstate() != 's':
1111 if not driverconclude(repo, ms, wctx, labels=labels):
1111 if not driverconclude(repo, ms, wctx, labels=labels):
1112 # 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
1113 # error out
1113 # error out
1114 unresolved = max(unresolved, 1)
1114 unresolved = max(unresolved, 1)
1115
1115
1116 ms.commit()
1116 ms.commit()
1117
1117
1118 msupdated, msmerged, msremoved = ms.counts()
1118 msupdated, msmerged, msremoved = ms.counts()
1119 updated += msupdated
1119 updated += msupdated
1120 merged += msmerged
1120 merged += msmerged
1121 removed += msremoved
1121 removed += msremoved
1122
1122
1123 extraactions = ms.actions()
1123 extraactions = ms.actions()
1124 for a in 'rag':
1124 for a in 'rag':
1125 actions[a].extend(extraactions[a])
1125 actions[a].extend(extraactions[a])
1126
1126
1127 progress(_updating, None, total=numupdates, unit=_files)
1127 progress(_updating, None, total=numupdates, unit=_files)
1128
1128
1129 return updated, merged, removed, unresolved
1129 return updated, merged, removed, unresolved
1130
1130
1131 def recordupdates(repo, actions, branchmerge):
1131 def recordupdates(repo, actions, branchmerge):
1132 "record merge actions to the dirstate"
1132 "record merge actions to the dirstate"
1133 # remove (must come first)
1133 # remove (must come first)
1134 for f, args, msg in actions['r']:
1134 for f, args, msg in actions.get('r', []):
1135 if branchmerge:
1135 if branchmerge:
1136 repo.dirstate.remove(f)
1136 repo.dirstate.remove(f)
1137 else:
1137 else:
1138 repo.dirstate.drop(f)
1138 repo.dirstate.drop(f)
1139
1139
1140 # forget (must come first)
1140 # forget (must come first)
1141 for f, args, msg in actions['f']:
1141 for f, args, msg in actions.get('f', []):
1142 repo.dirstate.drop(f)
1142 repo.dirstate.drop(f)
1143
1143
1144 # re-add
1144 # re-add
1145 for f, args, msg in actions['a']:
1145 for f, args, msg in actions.get('a', []):
1146 if not branchmerge:
1146 if not branchmerge:
1147 repo.dirstate.add(f)
1147 repo.dirstate.add(f)
1148
1148
1149 # exec change
1149 # exec change
1150 for f, args, msg in actions['e']:
1150 for f, args, msg in actions.get('e', []):
1151 repo.dirstate.normallookup(f)
1151 repo.dirstate.normallookup(f)
1152
1152
1153 # keep
1153 # keep
1154 for f, args, msg in actions['k']:
1154 for f, args, msg in actions.get('k', []):
1155 pass
1155 pass
1156
1156
1157 # get
1157 # get
1158 for f, args, msg in actions['g']:
1158 for f, args, msg in actions.get('g', []):
1159 if branchmerge:
1159 if branchmerge:
1160 repo.dirstate.otherparent(f)
1160 repo.dirstate.otherparent(f)
1161 else:
1161 else:
1162 repo.dirstate.normal(f)
1162 repo.dirstate.normal(f)
1163
1163
1164 # merge
1164 # merge
1165 for f, args, msg in actions['m']:
1165 for f, args, msg in actions.get('m', []):
1166 f1, f2, fa, move, anc = args
1166 f1, f2, fa, move, anc = args
1167 if branchmerge:
1167 if branchmerge:
1168 # We've done a branch merge, mark this file as merged
1168 # We've done a branch merge, mark this file as merged
1169 # so that we properly record the merger later
1169 # so that we properly record the merger later
1170 repo.dirstate.merge(f)
1170 repo.dirstate.merge(f)
1171 if f1 != f2: # copy/rename
1171 if f1 != f2: # copy/rename
1172 if move:
1172 if move:
1173 repo.dirstate.remove(f1)
1173 repo.dirstate.remove(f1)
1174 if f1 != f:
1174 if f1 != f:
1175 repo.dirstate.copy(f1, f)
1175 repo.dirstate.copy(f1, f)
1176 else:
1176 else:
1177 repo.dirstate.copy(f2, f)
1177 repo.dirstate.copy(f2, f)
1178 else:
1178 else:
1179 # We've update-merged a locally modified file, so
1179 # We've update-merged a locally modified file, so
1180 # we set the dirstate to emulate a normal checkout
1180 # we set the dirstate to emulate a normal checkout
1181 # of that file some time in the past. Thus our
1181 # of that file some time in the past. Thus our
1182 # merge will appear as a normal local file
1182 # merge will appear as a normal local file
1183 # modification.
1183 # modification.
1184 if f2 == f: # file not locally copied/moved
1184 if f2 == f: # file not locally copied/moved
1185 repo.dirstate.normallookup(f)
1185 repo.dirstate.normallookup(f)
1186 if move:
1186 if move:
1187 repo.dirstate.drop(f1)
1187 repo.dirstate.drop(f1)
1188
1188
1189 # directory rename, move local
1189 # directory rename, move local
1190 for f, args, msg in actions['dm']:
1190 for f, args, msg in actions.get('dm', []):
1191 f0, flag = args
1191 f0, flag = args
1192 if branchmerge:
1192 if branchmerge:
1193 repo.dirstate.add(f)
1193 repo.dirstate.add(f)
1194 repo.dirstate.remove(f0)
1194 repo.dirstate.remove(f0)
1195 repo.dirstate.copy(f0, f)
1195 repo.dirstate.copy(f0, f)
1196 else:
1196 else:
1197 repo.dirstate.normal(f)
1197 repo.dirstate.normal(f)
1198 repo.dirstate.drop(f0)
1198 repo.dirstate.drop(f0)
1199
1199
1200 # directory rename, get
1200 # directory rename, get
1201 for f, args, msg in actions['dg']:
1201 for f, args, msg in actions.get('dg', []):
1202 f0, flag = args
1202 f0, flag = args
1203 if branchmerge:
1203 if branchmerge:
1204 repo.dirstate.add(f)
1204 repo.dirstate.add(f)
1205 repo.dirstate.copy(f0, f)
1205 repo.dirstate.copy(f0, f)
1206 else:
1206 else:
1207 repo.dirstate.normal(f)
1207 repo.dirstate.normal(f)
1208
1208
1209 def update(repo, node, branchmerge, force, partial, ancestor=None,
1209 def update(repo, node, branchmerge, force, partial, ancestor=None,
1210 mergeancestor=False, labels=None):
1210 mergeancestor=False, labels=None):
1211 """
1211 """
1212 Perform a merge between the working directory and the given node
1212 Perform a merge between the working directory and the given node
1213
1213
1214 node = the node to update to, or None if unspecified
1214 node = the node to update to, or None if unspecified
1215 branchmerge = whether to merge between branches
1215 branchmerge = whether to merge between branches
1216 force = whether to force branch merging or file overwriting
1216 force = whether to force branch merging or file overwriting
1217 partial = a function to filter file lists (dirstate not updated)
1217 partial = a function to filter file lists (dirstate not updated)
1218 mergeancestor = whether it is merging with an ancestor. If true,
1218 mergeancestor = whether it is merging with an ancestor. If true,
1219 we should accept the incoming changes for any prompts that occur.
1219 we should accept the incoming changes for any prompts that occur.
1220 If false, merging with an ancestor (fast-forward) is only allowed
1220 If false, merging with an ancestor (fast-forward) is only allowed
1221 between different named branches. This flag is used by rebase extension
1221 between different named branches. This flag is used by rebase extension
1222 as a temporary fix and should be avoided in general.
1222 as a temporary fix and should be avoided in general.
1223
1223
1224 The table below shows all the behaviors of the update command
1224 The table below shows all the behaviors of the update command
1225 given the -c and -C or no options, whether the working directory
1225 given the -c and -C or no options, whether the working directory
1226 is dirty, whether a revision is specified, and the relationship of
1226 is dirty, whether a revision is specified, and the relationship of
1227 the parent rev to the target rev (linear, on the same named
1227 the parent rev to the target rev (linear, on the same named
1228 branch, or on another named branch).
1228 branch, or on another named branch).
1229
1229
1230 This logic is tested by test-update-branches.t.
1230 This logic is tested by test-update-branches.t.
1231
1231
1232 -c -C dirty rev | linear same cross
1232 -c -C dirty rev | linear same cross
1233 n n n n | ok (1) x
1233 n n n n | ok (1) x
1234 n n n y | ok ok ok
1234 n n n y | ok ok ok
1235 n n y n | merge (2) (2)
1235 n n y n | merge (2) (2)
1236 n n y y | merge (3) (3)
1236 n n y y | merge (3) (3)
1237 n y * * | discard discard discard
1237 n y * * | discard discard discard
1238 y n y * | (4) (4) (4)
1238 y n y * | (4) (4) (4)
1239 y n n * | ok ok ok
1239 y n n * | ok ok ok
1240 y y * * | (5) (5) (5)
1240 y y * * | (5) (5) (5)
1241
1241
1242 x = can't happen
1242 x = can't happen
1243 * = don't-care
1243 * = don't-care
1244 1 = abort: not a linear update (merge or update --check to force update)
1244 1 = abort: not a linear update (merge or update --check to force update)
1245 2 = abort: uncommitted changes (commit and merge, or update --clean to
1245 2 = abort: uncommitted changes (commit and merge, or update --clean to
1246 discard changes)
1246 discard changes)
1247 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1247 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1248 4 = abort: uncommitted changes (checked in commands.py)
1248 4 = abort: uncommitted changes (checked in commands.py)
1249 5 = incompatible options (checked in commands.py)
1249 5 = incompatible options (checked in commands.py)
1250
1250
1251 Return the same tuple as applyupdates().
1251 Return the same tuple as applyupdates().
1252 """
1252 """
1253
1253
1254 onode = node
1254 onode = node
1255 wlock = repo.wlock()
1255 wlock = repo.wlock()
1256 try:
1256 try:
1257 wc = repo[None]
1257 wc = repo[None]
1258 pl = wc.parents()
1258 pl = wc.parents()
1259 p1 = pl[0]
1259 p1 = pl[0]
1260 pas = [None]
1260 pas = [None]
1261 if ancestor is not None:
1261 if ancestor is not None:
1262 pas = [repo[ancestor]]
1262 pas = [repo[ancestor]]
1263
1263
1264 if node is None:
1264 if node is None:
1265 if (repo.ui.configbool('devel', 'all-warnings')
1265 if (repo.ui.configbool('devel', 'all-warnings')
1266 or repo.ui.configbool('devel', 'oldapi')):
1266 or repo.ui.configbool('devel', 'oldapi')):
1267 repo.ui.develwarn('update with no target')
1267 repo.ui.develwarn('update with no target')
1268 rev, _mark, _act = destutil.destupdate(repo)
1268 rev, _mark, _act = destutil.destupdate(repo)
1269 node = repo[rev].node()
1269 node = repo[rev].node()
1270
1270
1271 overwrite = force and not branchmerge
1271 overwrite = force and not branchmerge
1272
1272
1273 p2 = repo[node]
1273 p2 = repo[node]
1274 if pas[0] is None:
1274 if pas[0] is None:
1275 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1275 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1276 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1276 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1277 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1277 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1278 else:
1278 else:
1279 pas = [p1.ancestor(p2, warn=branchmerge)]
1279 pas = [p1.ancestor(p2, warn=branchmerge)]
1280
1280
1281 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1281 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1282
1282
1283 ### check phase
1283 ### check phase
1284 if not overwrite and len(pl) > 1:
1284 if not overwrite and len(pl) > 1:
1285 raise error.Abort(_("outstanding uncommitted merge"))
1285 raise error.Abort(_("outstanding uncommitted merge"))
1286 if branchmerge:
1286 if branchmerge:
1287 if pas == [p2]:
1287 if pas == [p2]:
1288 raise error.Abort(_("merging with a working directory ancestor"
1288 raise error.Abort(_("merging with a working directory ancestor"
1289 " has no effect"))
1289 " has no effect"))
1290 elif pas == [p1]:
1290 elif pas == [p1]:
1291 if not mergeancestor and p1.branch() == p2.branch():
1291 if not mergeancestor and p1.branch() == p2.branch():
1292 raise error.Abort(_("nothing to merge"),
1292 raise error.Abort(_("nothing to merge"),
1293 hint=_("use 'hg update' "
1293 hint=_("use 'hg update' "
1294 "or check 'hg heads'"))
1294 "or check 'hg heads'"))
1295 if not force and (wc.files() or wc.deleted()):
1295 if not force and (wc.files() or wc.deleted()):
1296 raise error.Abort(_("uncommitted changes"),
1296 raise error.Abort(_("uncommitted changes"),
1297 hint=_("use 'hg status' to list changes"))
1297 hint=_("use 'hg status' to list changes"))
1298 for s in sorted(wc.substate):
1298 for s in sorted(wc.substate):
1299 wc.sub(s).bailifchanged()
1299 wc.sub(s).bailifchanged()
1300
1300
1301 elif not overwrite:
1301 elif not overwrite:
1302 if p1 == p2: # no-op update
1302 if p1 == p2: # no-op update
1303 # call the hooks and exit early
1303 # call the hooks and exit early
1304 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1304 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1305 repo.hook('update', parent1=xp2, parent2='', error=0)
1305 repo.hook('update', parent1=xp2, parent2='', error=0)
1306 return 0, 0, 0, 0
1306 return 0, 0, 0, 0
1307
1307
1308 if pas not in ([p1], [p2]): # nonlinear
1308 if pas not in ([p1], [p2]): # nonlinear
1309 dirty = wc.dirty(missing=True)
1309 dirty = wc.dirty(missing=True)
1310 if dirty or onode is None:
1310 if dirty or onode is None:
1311 # Branching is a bit strange to ensure we do the minimal
1311 # Branching is a bit strange to ensure we do the minimal
1312 # amount of call to obsolete.background.
1312 # amount of call to obsolete.background.
1313 foreground = obsolete.foreground(repo, [p1.node()])
1313 foreground = obsolete.foreground(repo, [p1.node()])
1314 # note: the <node> variable contains a random identifier
1314 # note: the <node> variable contains a random identifier
1315 if repo[node].node() in foreground:
1315 if repo[node].node() in foreground:
1316 pas = [p1] # allow updating to successors
1316 pas = [p1] # allow updating to successors
1317 elif dirty:
1317 elif dirty:
1318 msg = _("uncommitted changes")
1318 msg = _("uncommitted changes")
1319 if onode is None:
1319 if onode is None:
1320 hint = _("commit and merge, or update --clean to"
1320 hint = _("commit and merge, or update --clean to"
1321 " discard changes")
1321 " discard changes")
1322 else:
1322 else:
1323 hint = _("commit or update --clean to discard"
1323 hint = _("commit or update --clean to discard"
1324 " changes")
1324 " changes")
1325 raise error.Abort(msg, hint=hint)
1325 raise error.Abort(msg, hint=hint)
1326 else: # node is none
1326 else: # node is none
1327 msg = _("not a linear update")
1327 msg = _("not a linear update")
1328 hint = _("merge or update --check to force update")
1328 hint = _("merge or update --check to force update")
1329 raise error.Abort(msg, hint=hint)
1329 raise error.Abort(msg, hint=hint)
1330 else:
1330 else:
1331 # Allow jumping branches if clean and specific rev given
1331 # Allow jumping branches if clean and specific rev given
1332 pas = [p1]
1332 pas = [p1]
1333
1333
1334 # deprecated config: merge.followcopies
1334 # deprecated config: merge.followcopies
1335 followcopies = False
1335 followcopies = False
1336 if overwrite:
1336 if overwrite:
1337 pas = [wc]
1337 pas = [wc]
1338 elif pas == [p2]: # backwards
1338 elif pas == [p2]: # backwards
1339 pas = [wc.p1()]
1339 pas = [wc.p1()]
1340 elif not branchmerge and not wc.dirty(missing=True):
1340 elif not branchmerge and not wc.dirty(missing=True):
1341 pass
1341 pass
1342 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1342 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1343 followcopies = True
1343 followcopies = True
1344
1344
1345 ### calculate phase
1345 ### calculate phase
1346 actionbyfile, diverge, renamedelete = calculateupdates(
1346 actionbyfile, diverge, renamedelete = calculateupdates(
1347 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1347 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1348 followcopies)
1348 followcopies)
1349 # Convert to dictionary-of-lists format
1349 # Convert to dictionary-of-lists format
1350 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1350 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1351 for f, (m, args, msg) in actionbyfile.iteritems():
1351 for f, (m, args, msg) in actionbyfile.iteritems():
1352 if m not in actions:
1352 if m not in actions:
1353 actions[m] = []
1353 actions[m] = []
1354 actions[m].append((f, args, msg))
1354 actions[m].append((f, args, msg))
1355
1355
1356 if not util.checkcase(repo.path):
1356 if not util.checkcase(repo.path):
1357 # check collision between files only in p2 for clean update
1357 # check collision between files only in p2 for clean update
1358 if (not branchmerge and
1358 if (not branchmerge and
1359 (force or not wc.dirty(missing=True, branch=False))):
1359 (force or not wc.dirty(missing=True, branch=False))):
1360 _checkcollision(repo, p2.manifest(), None)
1360 _checkcollision(repo, p2.manifest(), None)
1361 else:
1361 else:
1362 _checkcollision(repo, wc.manifest(), actions)
1362 _checkcollision(repo, wc.manifest(), actions)
1363
1363
1364 # Prompt and create actions. TODO: Move this towards resolve phase.
1364 # Prompt and create actions. TODO: Move this towards resolve phase.
1365 for f, args, msg in sorted(actions['cd']):
1365 for f, args, msg in sorted(actions['cd']):
1366 if repo.ui.promptchoice(
1366 if repo.ui.promptchoice(
1367 _("local changed %s which remote deleted\n"
1367 _("local changed %s which remote deleted\n"
1368 "use (c)hanged version or (d)elete?"
1368 "use (c)hanged version or (d)elete?"
1369 "$$ &Changed $$ &Delete") % f, 0):
1369 "$$ &Changed $$ &Delete") % f, 0):
1370 actions['r'].append((f, None, "prompt delete"))
1370 actions['r'].append((f, None, "prompt delete"))
1371 else:
1371 else:
1372 actions['a'].append((f, None, "prompt keep"))
1372 actions['a'].append((f, None, "prompt keep"))
1373
1373
1374 for f, args, msg in sorted(actions['dc']):
1374 for f, args, msg in sorted(actions['dc']):
1375 f1, f2, fa, move, anc = args
1375 f1, f2, fa, move, anc = args
1376 flags = p2[f2].flags()
1376 flags = p2[f2].flags()
1377 if repo.ui.promptchoice(
1377 if repo.ui.promptchoice(
1378 _("remote changed %s which local deleted\n"
1378 _("remote changed %s which local deleted\n"
1379 "use (c)hanged version or leave (d)eleted?"
1379 "use (c)hanged version or leave (d)eleted?"
1380 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1380 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1381 actions['g'].append((f, (flags,), "prompt recreating"))
1381 actions['g'].append((f, (flags,), "prompt recreating"))
1382
1382
1383 # divergent renames
1383 # divergent renames
1384 for f, fl in sorted(diverge.iteritems()):
1384 for f, fl in sorted(diverge.iteritems()):
1385 repo.ui.warn(_("note: possible conflict - %s was renamed "
1385 repo.ui.warn(_("note: possible conflict - %s was renamed "
1386 "multiple times to:\n") % f)
1386 "multiple times to:\n") % f)
1387 for nf in fl:
1387 for nf in fl:
1388 repo.ui.warn(" %s\n" % nf)
1388 repo.ui.warn(" %s\n" % nf)
1389
1389
1390 # rename and delete
1390 # rename and delete
1391 for f, fl in sorted(renamedelete.iteritems()):
1391 for f, fl in sorted(renamedelete.iteritems()):
1392 repo.ui.warn(_("note: possible conflict - %s was deleted "
1392 repo.ui.warn(_("note: possible conflict - %s was deleted "
1393 "and renamed to:\n") % f)
1393 "and renamed to:\n") % f)
1394 for nf in fl:
1394 for nf in fl:
1395 repo.ui.warn(" %s\n" % nf)
1395 repo.ui.warn(" %s\n" % nf)
1396
1396
1397 ### apply phase
1397 ### apply phase
1398 if not branchmerge: # just jump to the new rev
1398 if not branchmerge: # just jump to the new rev
1399 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1399 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1400 if not partial:
1400 if not partial:
1401 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1401 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1402 # note that we're in the middle of an update
1402 # note that we're in the middle of an update
1403 repo.vfs.write('updatestate', p2.hex())
1403 repo.vfs.write('updatestate', p2.hex())
1404
1404
1405 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1405 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1406
1406
1407 if not partial:
1407 if not partial:
1408 repo.dirstate.beginparentchange()
1408 repo.dirstate.beginparentchange()
1409 repo.setparents(fp1, fp2)
1409 repo.setparents(fp1, fp2)
1410 recordupdates(repo, actions, branchmerge)
1410 recordupdates(repo, actions, branchmerge)
1411 # update completed, clear state
1411 # update completed, clear state
1412 util.unlink(repo.join('updatestate'))
1412 util.unlink(repo.join('updatestate'))
1413
1413
1414 if not branchmerge:
1414 if not branchmerge:
1415 repo.dirstate.setbranch(p2.branch())
1415 repo.dirstate.setbranch(p2.branch())
1416 repo.dirstate.endparentchange()
1416 repo.dirstate.endparentchange()
1417 finally:
1417 finally:
1418 wlock.release()
1418 wlock.release()
1419
1419
1420 if not partial:
1420 if not partial:
1421 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1421 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1422 return stats
1422 return stats
1423
1423
1424 def graft(repo, ctx, pctx, labels):
1424 def graft(repo, ctx, pctx, labels):
1425 """Do a graft-like merge.
1425 """Do a graft-like merge.
1426
1426
1427 This is a merge where the merge ancestor is chosen such that one
1427 This is a merge where the merge ancestor is chosen such that one
1428 or more changesets are grafted onto the current changeset. In
1428 or more changesets are grafted onto the current changeset. In
1429 addition to the merge, this fixes up the dirstate to include only
1429 addition to the merge, this fixes up the dirstate to include only
1430 a single parent and tries to duplicate any renames/copies
1430 a single parent and tries to duplicate any renames/copies
1431 appropriately.
1431 appropriately.
1432
1432
1433 ctx - changeset to rebase
1433 ctx - changeset to rebase
1434 pctx - merge base, usually ctx.p1()
1434 pctx - merge base, usually ctx.p1()
1435 labels - merge labels eg ['local', 'graft']
1435 labels - merge labels eg ['local', 'graft']
1436
1436
1437 """
1437 """
1438 # If we're grafting a descendant onto an ancestor, be sure to pass
1438 # If we're grafting a descendant onto an ancestor, be sure to pass
1439 # mergeancestor=True to update. This does two things: 1) allows the merge if
1439 # mergeancestor=True to update. This does two things: 1) allows the merge if
1440 # the destination is the same as the parent of the ctx (so we can use graft
1440 # the destination is the same as the parent of the ctx (so we can use graft
1441 # to copy commits), and 2) informs update that the incoming changes are
1441 # to copy commits), and 2) informs update that the incoming changes are
1442 # newer than the destination so it doesn't prompt about "remote changed foo
1442 # newer than the destination so it doesn't prompt about "remote changed foo
1443 # which local deleted".
1443 # which local deleted".
1444 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1444 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1445
1445
1446 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1446 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1447 mergeancestor=mergeancestor, labels=labels)
1447 mergeancestor=mergeancestor, labels=labels)
1448
1448
1449 # drop the second merge parent
1449 # drop the second merge parent
1450 repo.dirstate.beginparentchange()
1450 repo.dirstate.beginparentchange()
1451 repo.setparents(repo['.'].node(), nullid)
1451 repo.setparents(repo['.'].node(), nullid)
1452 repo.dirstate.write(repo.currenttransaction())
1452 repo.dirstate.write(repo.currenttransaction())
1453 # fix up dirstate for copies and renames
1453 # fix up dirstate for copies and renames
1454 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1454 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1455 repo.dirstate.endparentchange()
1455 repo.dirstate.endparentchange()
1456 return stats
1456 return stats
General Comments 0
You need to be logged in to leave comments. Login now