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