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