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