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