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