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