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