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