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