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