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