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