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