##// END OF EJS Templates
merge: hint about using `hg resolve` for resolving conflicts...
Martin von Zweigbergk -
r43072:7b80ad5a default
parent child Browse files
Show More
@@ -1,2334 +1,2335 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 stat
13 import stat
14 import struct
14 import struct
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 addednodeid,
18 addednodeid,
19 bin,
19 bin,
20 hex,
20 hex,
21 modifiednodeid,
21 modifiednodeid,
22 nullhex,
22 nullhex,
23 nullid,
23 nullid,
24 nullrev,
24 nullrev,
25 )
25 )
26 from .thirdparty import (
26 from .thirdparty import (
27 attr,
27 attr,
28 )
28 )
29 from . import (
29 from . import (
30 copies,
30 copies,
31 encoding,
31 encoding,
32 error,
32 error,
33 filemerge,
33 filemerge,
34 match as matchmod,
34 match as matchmod,
35 obsutil,
35 obsutil,
36 pycompat,
36 pycompat,
37 scmutil,
37 scmutil,
38 subrepoutil,
38 subrepoutil,
39 util,
39 util,
40 worker,
40 worker,
41 )
41 )
42
42
43 _pack = struct.pack
43 _pack = struct.pack
44 _unpack = struct.unpack
44 _unpack = struct.unpack
45
45
46 def _droponode(data):
46 def _droponode(data):
47 # used for compatibility for v1
47 # used for compatibility for v1
48 bits = data.split('\0')
48 bits = data.split('\0')
49 bits = bits[:-2] + bits[-1:]
49 bits = bits[:-2] + bits[-1:]
50 return '\0'.join(bits)
50 return '\0'.join(bits)
51
51
52 # Merge state record types. See ``mergestate`` docs for more.
52 # Merge state record types. See ``mergestate`` docs for more.
53 RECORD_LOCAL = b'L'
53 RECORD_LOCAL = b'L'
54 RECORD_OTHER = b'O'
54 RECORD_OTHER = b'O'
55 RECORD_MERGED = b'F'
55 RECORD_MERGED = b'F'
56 RECORD_CHANGEDELETE_CONFLICT = b'C'
56 RECORD_CHANGEDELETE_CONFLICT = b'C'
57 RECORD_MERGE_DRIVER_MERGE = b'D'
57 RECORD_MERGE_DRIVER_MERGE = b'D'
58 RECORD_PATH_CONFLICT = b'P'
58 RECORD_PATH_CONFLICT = b'P'
59 RECORD_MERGE_DRIVER_STATE = b'm'
59 RECORD_MERGE_DRIVER_STATE = b'm'
60 RECORD_FILE_VALUES = b'f'
60 RECORD_FILE_VALUES = b'f'
61 RECORD_LABELS = b'l'
61 RECORD_LABELS = b'l'
62 RECORD_OVERRIDE = b't'
62 RECORD_OVERRIDE = b't'
63 RECORD_UNSUPPORTED_MANDATORY = b'X'
63 RECORD_UNSUPPORTED_MANDATORY = b'X'
64 RECORD_UNSUPPORTED_ADVISORY = b'x'
64 RECORD_UNSUPPORTED_ADVISORY = b'x'
65
65
66 MERGE_DRIVER_STATE_UNMARKED = b'u'
66 MERGE_DRIVER_STATE_UNMARKED = b'u'
67 MERGE_DRIVER_STATE_MARKED = b'm'
67 MERGE_DRIVER_STATE_MARKED = b'm'
68 MERGE_DRIVER_STATE_SUCCESS = b's'
68 MERGE_DRIVER_STATE_SUCCESS = b's'
69
69
70 MERGE_RECORD_UNRESOLVED = b'u'
70 MERGE_RECORD_UNRESOLVED = b'u'
71 MERGE_RECORD_RESOLVED = b'r'
71 MERGE_RECORD_RESOLVED = b'r'
72 MERGE_RECORD_UNRESOLVED_PATH = b'pu'
72 MERGE_RECORD_UNRESOLVED_PATH = b'pu'
73 MERGE_RECORD_RESOLVED_PATH = b'pr'
73 MERGE_RECORD_RESOLVED_PATH = b'pr'
74 MERGE_RECORD_DRIVER_RESOLVED = b'd'
74 MERGE_RECORD_DRIVER_RESOLVED = b'd'
75
75
76 ACTION_FORGET = b'f'
76 ACTION_FORGET = b'f'
77 ACTION_REMOVE = b'r'
77 ACTION_REMOVE = b'r'
78 ACTION_ADD = b'a'
78 ACTION_ADD = b'a'
79 ACTION_GET = b'g'
79 ACTION_GET = b'g'
80 ACTION_PATH_CONFLICT = b'p'
80 ACTION_PATH_CONFLICT = b'p'
81 ACTION_PATH_CONFLICT_RESOLVE = b'pr'
81 ACTION_PATH_CONFLICT_RESOLVE = b'pr'
82 ACTION_ADD_MODIFIED = b'am'
82 ACTION_ADD_MODIFIED = b'am'
83 ACTION_CREATED = b'c'
83 ACTION_CREATED = b'c'
84 ACTION_DELETED_CHANGED = b'dc'
84 ACTION_DELETED_CHANGED = b'dc'
85 ACTION_CHANGED_DELETED = b'cd'
85 ACTION_CHANGED_DELETED = b'cd'
86 ACTION_MERGE = b'm'
86 ACTION_MERGE = b'm'
87 ACTION_LOCAL_DIR_RENAME_GET = b'dg'
87 ACTION_LOCAL_DIR_RENAME_GET = b'dg'
88 ACTION_DIR_RENAME_MOVE_LOCAL = b'dm'
88 ACTION_DIR_RENAME_MOVE_LOCAL = b'dm'
89 ACTION_KEEP = b'k'
89 ACTION_KEEP = b'k'
90 ACTION_EXEC = b'e'
90 ACTION_EXEC = b'e'
91 ACTION_CREATED_MERGE = b'cm'
91 ACTION_CREATED_MERGE = b'cm'
92
92
93 class mergestate(object):
93 class mergestate(object):
94 '''track 3-way merge state of individual files
94 '''track 3-way merge state of individual files
95
95
96 The merge state is stored on disk when needed. Two files are used: one with
96 The merge state is stored on disk when needed. Two files are used: one with
97 an old format (version 1), and one with a new format (version 2). Version 2
97 an old format (version 1), and one with a new format (version 2). Version 2
98 stores a superset of the data in version 1, including new kinds of records
98 stores a superset of the data in version 1, including new kinds of records
99 in the future. For more about the new format, see the documentation for
99 in the future. For more about the new format, see the documentation for
100 `_readrecordsv2`.
100 `_readrecordsv2`.
101
101
102 Each record can contain arbitrary content, and has an associated type. This
102 Each record can contain arbitrary content, and has an associated type. This
103 `type` should be a letter. If `type` is uppercase, the record is mandatory:
103 `type` should be a letter. If `type` is uppercase, the record is mandatory:
104 versions of Mercurial that don't support it should abort. If `type` is
104 versions of Mercurial that don't support it should abort. If `type` is
105 lowercase, the record can be safely ignored.
105 lowercase, the record can be safely ignored.
106
106
107 Currently known records:
107 Currently known records:
108
108
109 L: the node of the "local" part of the merge (hexified version)
109 L: the node of the "local" part of the merge (hexified version)
110 O: the node of the "other" part of the merge (hexified version)
110 O: the node of the "other" part of the merge (hexified version)
111 F: a file to be merged entry
111 F: a file to be merged entry
112 C: a change/delete or delete/change conflict
112 C: a change/delete or delete/change conflict
113 D: a file that the external merge driver will merge internally
113 D: a file that the external merge driver will merge internally
114 (experimental)
114 (experimental)
115 P: a path conflict (file vs directory)
115 P: a path conflict (file vs directory)
116 m: the external merge driver defined for this merge plus its run state
116 m: the external merge driver defined for this merge plus its run state
117 (experimental)
117 (experimental)
118 f: a (filename, dictionary) tuple of optional values for a given file
118 f: a (filename, dictionary) tuple of optional values for a given file
119 X: unsupported mandatory record type (used in tests)
119 X: unsupported mandatory record type (used in tests)
120 x: unsupported advisory record type (used in tests)
120 x: unsupported advisory record type (used in tests)
121 l: the labels for the parts of the merge.
121 l: the labels for the parts of the merge.
122
122
123 Merge driver run states (experimental):
123 Merge driver run states (experimental):
124 u: driver-resolved files unmarked -- needs to be run next time we're about
124 u: driver-resolved files unmarked -- needs to be run next time we're about
125 to resolve or commit
125 to resolve or commit
126 m: driver-resolved files marked -- only needs to be run before commit
126 m: driver-resolved files marked -- only needs to be run before commit
127 s: success/skipped -- does not need to be run any more
127 s: success/skipped -- does not need to be run any more
128
128
129 Merge record states (stored in self._state, indexed by filename):
129 Merge record states (stored in self._state, indexed by filename):
130 u: unresolved conflict
130 u: unresolved conflict
131 r: resolved conflict
131 r: resolved conflict
132 pu: unresolved path conflict (file conflicts with directory)
132 pu: unresolved path conflict (file conflicts with directory)
133 pr: resolved path conflict
133 pr: resolved path conflict
134 d: driver-resolved conflict
134 d: driver-resolved conflict
135
135
136 The resolve command transitions between 'u' and 'r' for conflicts and
136 The resolve command transitions between 'u' and 'r' for conflicts and
137 'pu' and 'pr' for path conflicts.
137 'pu' and 'pr' for path conflicts.
138 '''
138 '''
139 statepathv1 = 'merge/state'
139 statepathv1 = 'merge/state'
140 statepathv2 = 'merge/state2'
140 statepathv2 = 'merge/state2'
141
141
142 @staticmethod
142 @staticmethod
143 def clean(repo, node=None, other=None, labels=None):
143 def clean(repo, node=None, other=None, labels=None):
144 """Initialize a brand new merge state, removing any existing state on
144 """Initialize a brand new merge state, removing any existing state on
145 disk."""
145 disk."""
146 ms = mergestate(repo)
146 ms = mergestate(repo)
147 ms.reset(node, other, labels)
147 ms.reset(node, other, labels)
148 return ms
148 return ms
149
149
150 @staticmethod
150 @staticmethod
151 def read(repo):
151 def read(repo):
152 """Initialize the merge state, reading it from disk."""
152 """Initialize the merge state, reading it from disk."""
153 ms = mergestate(repo)
153 ms = mergestate(repo)
154 ms._read()
154 ms._read()
155 return ms
155 return ms
156
156
157 def __init__(self, repo):
157 def __init__(self, repo):
158 """Initialize the merge state.
158 """Initialize the merge state.
159
159
160 Do not use this directly! Instead call read() or clean()."""
160 Do not use this directly! Instead call read() or clean()."""
161 self._repo = repo
161 self._repo = repo
162 self._dirty = False
162 self._dirty = False
163 self._labels = None
163 self._labels = None
164
164
165 def reset(self, node=None, other=None, labels=None):
165 def reset(self, node=None, other=None, labels=None):
166 self._state = {}
166 self._state = {}
167 self._stateextras = {}
167 self._stateextras = {}
168 self._local = None
168 self._local = None
169 self._other = None
169 self._other = None
170 self._labels = labels
170 self._labels = labels
171 for var in ('localctx', 'otherctx'):
171 for var in ('localctx', 'otherctx'):
172 if var in vars(self):
172 if var in vars(self):
173 delattr(self, var)
173 delattr(self, var)
174 if node:
174 if node:
175 self._local = node
175 self._local = node
176 self._other = other
176 self._other = other
177 self._readmergedriver = None
177 self._readmergedriver = None
178 if self.mergedriver:
178 if self.mergedriver:
179 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
179 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
180 else:
180 else:
181 self._mdstate = MERGE_DRIVER_STATE_UNMARKED
181 self._mdstate = MERGE_DRIVER_STATE_UNMARKED
182 shutil.rmtree(self._repo.vfs.join('merge'), True)
182 shutil.rmtree(self._repo.vfs.join('merge'), True)
183 self._results = {}
183 self._results = {}
184 self._dirty = False
184 self._dirty = False
185
185
186 def _read(self):
186 def _read(self):
187 """Analyse each record content to restore a serialized state from disk
187 """Analyse each record content to restore a serialized state from disk
188
188
189 This function process "record" entry produced by the de-serialization
189 This function process "record" entry produced by the de-serialization
190 of on disk file.
190 of on disk file.
191 """
191 """
192 self._state = {}
192 self._state = {}
193 self._stateextras = {}
193 self._stateextras = {}
194 self._local = None
194 self._local = None
195 self._other = None
195 self._other = None
196 for var in ('localctx', 'otherctx'):
196 for var in ('localctx', 'otherctx'):
197 if var in vars(self):
197 if var in vars(self):
198 delattr(self, var)
198 delattr(self, var)
199 self._readmergedriver = None
199 self._readmergedriver = None
200 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
200 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
201 unsupported = set()
201 unsupported = set()
202 records = self._readrecords()
202 records = self._readrecords()
203 for rtype, record in records:
203 for rtype, record in records:
204 if rtype == RECORD_LOCAL:
204 if rtype == RECORD_LOCAL:
205 self._local = bin(record)
205 self._local = bin(record)
206 elif rtype == RECORD_OTHER:
206 elif rtype == RECORD_OTHER:
207 self._other = bin(record)
207 self._other = bin(record)
208 elif rtype == RECORD_MERGE_DRIVER_STATE:
208 elif rtype == RECORD_MERGE_DRIVER_STATE:
209 bits = record.split('\0', 1)
209 bits = record.split('\0', 1)
210 mdstate = bits[1]
210 mdstate = bits[1]
211 if len(mdstate) != 1 or mdstate not in (
211 if len(mdstate) != 1 or mdstate not in (
212 MERGE_DRIVER_STATE_UNMARKED, MERGE_DRIVER_STATE_MARKED,
212 MERGE_DRIVER_STATE_UNMARKED, MERGE_DRIVER_STATE_MARKED,
213 MERGE_DRIVER_STATE_SUCCESS):
213 MERGE_DRIVER_STATE_SUCCESS):
214 # the merge driver should be idempotent, so just rerun it
214 # the merge driver should be idempotent, so just rerun it
215 mdstate = MERGE_DRIVER_STATE_UNMARKED
215 mdstate = MERGE_DRIVER_STATE_UNMARKED
216
216
217 self._readmergedriver = bits[0]
217 self._readmergedriver = bits[0]
218 self._mdstate = mdstate
218 self._mdstate = mdstate
219 elif rtype in (RECORD_MERGED, RECORD_CHANGEDELETE_CONFLICT,
219 elif rtype in (RECORD_MERGED, RECORD_CHANGEDELETE_CONFLICT,
220 RECORD_PATH_CONFLICT, RECORD_MERGE_DRIVER_MERGE):
220 RECORD_PATH_CONFLICT, RECORD_MERGE_DRIVER_MERGE):
221 bits = record.split('\0')
221 bits = record.split('\0')
222 self._state[bits[0]] = bits[1:]
222 self._state[bits[0]] = bits[1:]
223 elif rtype == RECORD_FILE_VALUES:
223 elif rtype == RECORD_FILE_VALUES:
224 filename, rawextras = record.split('\0', 1)
224 filename, rawextras = record.split('\0', 1)
225 extraparts = rawextras.split('\0')
225 extraparts = rawextras.split('\0')
226 extras = {}
226 extras = {}
227 i = 0
227 i = 0
228 while i < len(extraparts):
228 while i < len(extraparts):
229 extras[extraparts[i]] = extraparts[i + 1]
229 extras[extraparts[i]] = extraparts[i + 1]
230 i += 2
230 i += 2
231
231
232 self._stateextras[filename] = extras
232 self._stateextras[filename] = extras
233 elif rtype == RECORD_LABELS:
233 elif rtype == RECORD_LABELS:
234 labels = record.split('\0', 2)
234 labels = record.split('\0', 2)
235 self._labels = [l for l in labels if len(l) > 0]
235 self._labels = [l for l in labels if len(l) > 0]
236 elif not rtype.islower():
236 elif not rtype.islower():
237 unsupported.add(rtype)
237 unsupported.add(rtype)
238 self._results = {}
238 self._results = {}
239 self._dirty = False
239 self._dirty = False
240
240
241 if unsupported:
241 if unsupported:
242 raise error.UnsupportedMergeRecords(unsupported)
242 raise error.UnsupportedMergeRecords(unsupported)
243
243
244 def _readrecords(self):
244 def _readrecords(self):
245 """Read merge state from disk and return a list of record (TYPE, data)
245 """Read merge state from disk and return a list of record (TYPE, data)
246
246
247 We read data from both v1 and v2 files and decide which one to use.
247 We read data from both v1 and v2 files and decide which one to use.
248
248
249 V1 has been used by version prior to 2.9.1 and contains less data than
249 V1 has been used by version prior to 2.9.1 and contains less data than
250 v2. We read both versions and check if no data in v2 contradicts
250 v2. We read both versions and check if no data in v2 contradicts
251 v1. If there is not contradiction we can safely assume that both v1
251 v1. If there is not contradiction we can safely assume that both v1
252 and v2 were written at the same time and use the extract data in v2. If
252 and v2 were written at the same time and use the extract data in v2. If
253 there is contradiction we ignore v2 content as we assume an old version
253 there is contradiction we ignore v2 content as we assume an old version
254 of Mercurial has overwritten the mergestate file and left an old v2
254 of Mercurial has overwritten the mergestate file and left an old v2
255 file around.
255 file around.
256
256
257 returns list of record [(TYPE, data), ...]"""
257 returns list of record [(TYPE, data), ...]"""
258 v1records = self._readrecordsv1()
258 v1records = self._readrecordsv1()
259 v2records = self._readrecordsv2()
259 v2records = self._readrecordsv2()
260 if self._v1v2match(v1records, v2records):
260 if self._v1v2match(v1records, v2records):
261 return v2records
261 return v2records
262 else:
262 else:
263 # v1 file is newer than v2 file, use it
263 # v1 file is newer than v2 file, use it
264 # we have to infer the "other" changeset of the merge
264 # we have to infer the "other" changeset of the merge
265 # we cannot do better than that with v1 of the format
265 # we cannot do better than that with v1 of the format
266 mctx = self._repo[None].parents()[-1]
266 mctx = self._repo[None].parents()[-1]
267 v1records.append((RECORD_OTHER, mctx.hex()))
267 v1records.append((RECORD_OTHER, mctx.hex()))
268 # add place holder "other" file node information
268 # add place holder "other" file node information
269 # nobody is using it yet so we do no need to fetch the data
269 # nobody is using it yet so we do no need to fetch the data
270 # if mctx was wrong `mctx[bits[-2]]` may fails.
270 # if mctx was wrong `mctx[bits[-2]]` may fails.
271 for idx, r in enumerate(v1records):
271 for idx, r in enumerate(v1records):
272 if r[0] == RECORD_MERGED:
272 if r[0] == RECORD_MERGED:
273 bits = r[1].split('\0')
273 bits = r[1].split('\0')
274 bits.insert(-2, '')
274 bits.insert(-2, '')
275 v1records[idx] = (r[0], '\0'.join(bits))
275 v1records[idx] = (r[0], '\0'.join(bits))
276 return v1records
276 return v1records
277
277
278 def _v1v2match(self, v1records, v2records):
278 def _v1v2match(self, v1records, v2records):
279 oldv2 = set() # old format version of v2 record
279 oldv2 = set() # old format version of v2 record
280 for rec in v2records:
280 for rec in v2records:
281 if rec[0] == RECORD_LOCAL:
281 if rec[0] == RECORD_LOCAL:
282 oldv2.add(rec)
282 oldv2.add(rec)
283 elif rec[0] == RECORD_MERGED:
283 elif rec[0] == RECORD_MERGED:
284 # drop the onode data (not contained in v1)
284 # drop the onode data (not contained in v1)
285 oldv2.add((RECORD_MERGED, _droponode(rec[1])))
285 oldv2.add((RECORD_MERGED, _droponode(rec[1])))
286 for rec in v1records:
286 for rec in v1records:
287 if rec not in oldv2:
287 if rec not in oldv2:
288 return False
288 return False
289 else:
289 else:
290 return True
290 return True
291
291
292 def _readrecordsv1(self):
292 def _readrecordsv1(self):
293 """read on disk merge state for version 1 file
293 """read on disk merge state for version 1 file
294
294
295 returns list of record [(TYPE, data), ...]
295 returns list of record [(TYPE, data), ...]
296
296
297 Note: the "F" data from this file are one entry short
297 Note: the "F" data from this file are one entry short
298 (no "other file node" entry)
298 (no "other file node" entry)
299 """
299 """
300 records = []
300 records = []
301 try:
301 try:
302 f = self._repo.vfs(self.statepathv1)
302 f = self._repo.vfs(self.statepathv1)
303 for i, l in enumerate(f):
303 for i, l in enumerate(f):
304 if i == 0:
304 if i == 0:
305 records.append((RECORD_LOCAL, l[:-1]))
305 records.append((RECORD_LOCAL, l[:-1]))
306 else:
306 else:
307 records.append((RECORD_MERGED, l[:-1]))
307 records.append((RECORD_MERGED, l[:-1]))
308 f.close()
308 f.close()
309 except IOError as err:
309 except IOError as err:
310 if err.errno != errno.ENOENT:
310 if err.errno != errno.ENOENT:
311 raise
311 raise
312 return records
312 return records
313
313
314 def _readrecordsv2(self):
314 def _readrecordsv2(self):
315 """read on disk merge state for version 2 file
315 """read on disk merge state for version 2 file
316
316
317 This format is a list of arbitrary records of the form:
317 This format is a list of arbitrary records of the form:
318
318
319 [type][length][content]
319 [type][length][content]
320
320
321 `type` is a single character, `length` is a 4 byte integer, and
321 `type` is a single character, `length` is a 4 byte integer, and
322 `content` is an arbitrary byte sequence of length `length`.
322 `content` is an arbitrary byte sequence of length `length`.
323
323
324 Mercurial versions prior to 3.7 have a bug where if there are
324 Mercurial versions prior to 3.7 have a bug where if there are
325 unsupported mandatory merge records, attempting to clear out the merge
325 unsupported mandatory merge records, attempting to clear out the merge
326 state with hg update --clean or similar aborts. The 't' record type
326 state with hg update --clean or similar aborts. The 't' record type
327 works around that by writing out what those versions treat as an
327 works around that by writing out what those versions treat as an
328 advisory record, but later versions interpret as special: the first
328 advisory record, but later versions interpret as special: the first
329 character is the 'real' record type and everything onwards is the data.
329 character is the 'real' record type and everything onwards is the data.
330
330
331 Returns list of records [(TYPE, data), ...]."""
331 Returns list of records [(TYPE, data), ...]."""
332 records = []
332 records = []
333 try:
333 try:
334 f = self._repo.vfs(self.statepathv2)
334 f = self._repo.vfs(self.statepathv2)
335 data = f.read()
335 data = f.read()
336 off = 0
336 off = 0
337 end = len(data)
337 end = len(data)
338 while off < end:
338 while off < end:
339 rtype = data[off:off + 1]
339 rtype = data[off:off + 1]
340 off += 1
340 off += 1
341 length = _unpack('>I', data[off:(off + 4)])[0]
341 length = _unpack('>I', data[off:(off + 4)])[0]
342 off += 4
342 off += 4
343 record = data[off:(off + length)]
343 record = data[off:(off + length)]
344 off += length
344 off += length
345 if rtype == RECORD_OVERRIDE:
345 if rtype == RECORD_OVERRIDE:
346 rtype, record = record[0:1], record[1:]
346 rtype, record = record[0:1], record[1:]
347 records.append((rtype, record))
347 records.append((rtype, record))
348 f.close()
348 f.close()
349 except IOError as err:
349 except IOError as err:
350 if err.errno != errno.ENOENT:
350 if err.errno != errno.ENOENT:
351 raise
351 raise
352 return records
352 return records
353
353
354 @util.propertycache
354 @util.propertycache
355 def mergedriver(self):
355 def mergedriver(self):
356 # protect against the following:
356 # protect against the following:
357 # - A configures a malicious merge driver in their hgrc, then
357 # - A configures a malicious merge driver in their hgrc, then
358 # pauses the merge
358 # pauses the merge
359 # - A edits their hgrc to remove references to the merge driver
359 # - A edits their hgrc to remove references to the merge driver
360 # - A gives a copy of their entire repo, including .hg, to B
360 # - A gives a copy of their entire repo, including .hg, to B
361 # - B inspects .hgrc and finds it to be clean
361 # - B inspects .hgrc and finds it to be clean
362 # - B then continues the merge and the malicious merge driver
362 # - B then continues the merge and the malicious merge driver
363 # gets invoked
363 # gets invoked
364 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
364 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
365 if (self._readmergedriver is not None
365 if (self._readmergedriver is not None
366 and self._readmergedriver != configmergedriver):
366 and self._readmergedriver != configmergedriver):
367 raise error.ConfigError(
367 raise error.ConfigError(
368 _("merge driver changed since merge started"),
368 _("merge driver changed since merge started"),
369 hint=_("revert merge driver change or abort merge"))
369 hint=_("revert merge driver change or abort merge"))
370
370
371 return configmergedriver
371 return configmergedriver
372
372
373 @util.propertycache
373 @util.propertycache
374 def localctx(self):
374 def localctx(self):
375 if self._local is None:
375 if self._local is None:
376 msg = "localctx accessed but self._local isn't set"
376 msg = "localctx accessed but self._local isn't set"
377 raise error.ProgrammingError(msg)
377 raise error.ProgrammingError(msg)
378 return self._repo[self._local]
378 return self._repo[self._local]
379
379
380 @util.propertycache
380 @util.propertycache
381 def otherctx(self):
381 def otherctx(self):
382 if self._other is None:
382 if self._other is None:
383 msg = "otherctx accessed but self._other isn't set"
383 msg = "otherctx accessed but self._other isn't set"
384 raise error.ProgrammingError(msg)
384 raise error.ProgrammingError(msg)
385 return self._repo[self._other]
385 return self._repo[self._other]
386
386
387 def active(self):
387 def active(self):
388 """Whether mergestate is active.
388 """Whether mergestate is active.
389
389
390 Returns True if there appears to be mergestate. This is a rough proxy
390 Returns True if there appears to be mergestate. This is a rough proxy
391 for "is a merge in progress."
391 for "is a merge in progress."
392 """
392 """
393 # Check local variables before looking at filesystem for performance
393 # Check local variables before looking at filesystem for performance
394 # reasons.
394 # reasons.
395 return (bool(self._local) or bool(self._state) or
395 return (bool(self._local) or bool(self._state) or
396 self._repo.vfs.exists(self.statepathv1) or
396 self._repo.vfs.exists(self.statepathv1) or
397 self._repo.vfs.exists(self.statepathv2))
397 self._repo.vfs.exists(self.statepathv2))
398
398
399 def commit(self):
399 def commit(self):
400 """Write current state on disk (if necessary)"""
400 """Write current state on disk (if necessary)"""
401 if self._dirty:
401 if self._dirty:
402 records = self._makerecords()
402 records = self._makerecords()
403 self._writerecords(records)
403 self._writerecords(records)
404 self._dirty = False
404 self._dirty = False
405
405
406 def _makerecords(self):
406 def _makerecords(self):
407 records = []
407 records = []
408 records.append((RECORD_LOCAL, hex(self._local)))
408 records.append((RECORD_LOCAL, hex(self._local)))
409 records.append((RECORD_OTHER, hex(self._other)))
409 records.append((RECORD_OTHER, hex(self._other)))
410 if self.mergedriver:
410 if self.mergedriver:
411 records.append((RECORD_MERGE_DRIVER_STATE, '\0'.join([
411 records.append((RECORD_MERGE_DRIVER_STATE, '\0'.join([
412 self.mergedriver, self._mdstate])))
412 self.mergedriver, self._mdstate])))
413 # Write out state items. In all cases, the value of the state map entry
413 # Write out state items. In all cases, the value of the state map entry
414 # is written as the contents of the record. The record type depends on
414 # is written as the contents of the record. The record type depends on
415 # the type of state that is stored, and capital-letter records are used
415 # the type of state that is stored, and capital-letter records are used
416 # to prevent older versions of Mercurial that do not support the feature
416 # to prevent older versions of Mercurial that do not support the feature
417 # from loading them.
417 # from loading them.
418 for filename, v in self._state.iteritems():
418 for filename, v in self._state.iteritems():
419 if v[0] == MERGE_RECORD_DRIVER_RESOLVED:
419 if v[0] == MERGE_RECORD_DRIVER_RESOLVED:
420 # Driver-resolved merge. These are stored in 'D' records.
420 # Driver-resolved merge. These are stored in 'D' records.
421 records.append((RECORD_MERGE_DRIVER_MERGE,
421 records.append((RECORD_MERGE_DRIVER_MERGE,
422 '\0'.join([filename] + v)))
422 '\0'.join([filename] + v)))
423 elif v[0] in (MERGE_RECORD_UNRESOLVED_PATH,
423 elif v[0] in (MERGE_RECORD_UNRESOLVED_PATH,
424 MERGE_RECORD_RESOLVED_PATH):
424 MERGE_RECORD_RESOLVED_PATH):
425 # Path conflicts. These are stored in 'P' records. The current
425 # Path conflicts. These are stored in 'P' records. The current
426 # resolution state ('pu' or 'pr') is stored within the record.
426 # resolution state ('pu' or 'pr') is stored within the record.
427 records.append((RECORD_PATH_CONFLICT,
427 records.append((RECORD_PATH_CONFLICT,
428 '\0'.join([filename] + v)))
428 '\0'.join([filename] + v)))
429 elif v[1] == nullhex or v[6] == nullhex:
429 elif v[1] == nullhex or v[6] == nullhex:
430 # Change/Delete or Delete/Change conflicts. These are stored in
430 # Change/Delete or Delete/Change conflicts. These are stored in
431 # 'C' records. v[1] is the local file, and is nullhex when the
431 # 'C' records. v[1] is the local file, and is nullhex when the
432 # file is deleted locally ('dc'). v[6] is the remote file, and
432 # file is deleted locally ('dc'). v[6] is the remote file, and
433 # is nullhex when the file is deleted remotely ('cd').
433 # is nullhex when the file is deleted remotely ('cd').
434 records.append((RECORD_CHANGEDELETE_CONFLICT,
434 records.append((RECORD_CHANGEDELETE_CONFLICT,
435 '\0'.join([filename] + v)))
435 '\0'.join([filename] + v)))
436 else:
436 else:
437 # Normal files. These are stored in 'F' records.
437 # Normal files. These are stored in 'F' records.
438 records.append((RECORD_MERGED,
438 records.append((RECORD_MERGED,
439 '\0'.join([filename] + v)))
439 '\0'.join([filename] + v)))
440 for filename, extras in sorted(self._stateextras.iteritems()):
440 for filename, extras in sorted(self._stateextras.iteritems()):
441 rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
441 rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
442 extras.iteritems())
442 extras.iteritems())
443 records.append((RECORD_FILE_VALUES,
443 records.append((RECORD_FILE_VALUES,
444 '%s\0%s' % (filename, rawextras)))
444 '%s\0%s' % (filename, rawextras)))
445 if self._labels is not None:
445 if self._labels is not None:
446 labels = '\0'.join(self._labels)
446 labels = '\0'.join(self._labels)
447 records.append((RECORD_LABELS, labels))
447 records.append((RECORD_LABELS, labels))
448 return records
448 return records
449
449
450 def _writerecords(self, records):
450 def _writerecords(self, records):
451 """Write current state on disk (both v1 and v2)"""
451 """Write current state on disk (both v1 and v2)"""
452 self._writerecordsv1(records)
452 self._writerecordsv1(records)
453 self._writerecordsv2(records)
453 self._writerecordsv2(records)
454
454
455 def _writerecordsv1(self, records):
455 def _writerecordsv1(self, records):
456 """Write current state on disk in a version 1 file"""
456 """Write current state on disk in a version 1 file"""
457 f = self._repo.vfs(self.statepathv1, 'wb')
457 f = self._repo.vfs(self.statepathv1, 'wb')
458 irecords = iter(records)
458 irecords = iter(records)
459 lrecords = next(irecords)
459 lrecords = next(irecords)
460 assert lrecords[0] == RECORD_LOCAL
460 assert lrecords[0] == RECORD_LOCAL
461 f.write(hex(self._local) + '\n')
461 f.write(hex(self._local) + '\n')
462 for rtype, data in irecords:
462 for rtype, data in irecords:
463 if rtype == RECORD_MERGED:
463 if rtype == RECORD_MERGED:
464 f.write('%s\n' % _droponode(data))
464 f.write('%s\n' % _droponode(data))
465 f.close()
465 f.close()
466
466
467 def _writerecordsv2(self, records):
467 def _writerecordsv2(self, records):
468 """Write current state on disk in a version 2 file
468 """Write current state on disk in a version 2 file
469
469
470 See the docstring for _readrecordsv2 for why we use 't'."""
470 See the docstring for _readrecordsv2 for why we use 't'."""
471 # these are the records that all version 2 clients can read
471 # these are the records that all version 2 clients can read
472 allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
472 allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
473 f = self._repo.vfs(self.statepathv2, 'wb')
473 f = self._repo.vfs(self.statepathv2, 'wb')
474 for key, data in records:
474 for key, data in records:
475 assert len(key) == 1
475 assert len(key) == 1
476 if key not in allowlist:
476 if key not in allowlist:
477 key, data = RECORD_OVERRIDE, '%s%s' % (key, data)
477 key, data = RECORD_OVERRIDE, '%s%s' % (key, data)
478 format = '>sI%is' % len(data)
478 format = '>sI%is' % len(data)
479 f.write(_pack(format, key, len(data), data))
479 f.write(_pack(format, key, len(data), data))
480 f.close()
480 f.close()
481
481
482 @staticmethod
482 @staticmethod
483 def getlocalkey(path):
483 def getlocalkey(path):
484 """hash the path of a local file context for storage in the .hg/merge
484 """hash the path of a local file context for storage in the .hg/merge
485 directory."""
485 directory."""
486
486
487 return hex(hashlib.sha1(path).digest())
487 return hex(hashlib.sha1(path).digest())
488
488
489 def add(self, fcl, fco, fca, fd):
489 def add(self, fcl, fco, fca, fd):
490 """add a new (potentially?) conflicting file the merge state
490 """add a new (potentially?) conflicting file the merge state
491 fcl: file context for local,
491 fcl: file context for local,
492 fco: file context for remote,
492 fco: file context for remote,
493 fca: file context for ancestors,
493 fca: file context for ancestors,
494 fd: file path of the resulting merge.
494 fd: file path of the resulting merge.
495
495
496 note: also write the local version to the `.hg/merge` directory.
496 note: also write the local version to the `.hg/merge` directory.
497 """
497 """
498 if fcl.isabsent():
498 if fcl.isabsent():
499 localkey = nullhex
499 localkey = nullhex
500 else:
500 else:
501 localkey = mergestate.getlocalkey(fcl.path())
501 localkey = mergestate.getlocalkey(fcl.path())
502 self._repo.vfs.write('merge/' + localkey, fcl.data())
502 self._repo.vfs.write('merge/' + localkey, fcl.data())
503 self._state[fd] = [MERGE_RECORD_UNRESOLVED, localkey, fcl.path(),
503 self._state[fd] = [MERGE_RECORD_UNRESOLVED, localkey, fcl.path(),
504 fca.path(), hex(fca.filenode()),
504 fca.path(), hex(fca.filenode()),
505 fco.path(), hex(fco.filenode()),
505 fco.path(), hex(fco.filenode()),
506 fcl.flags()]
506 fcl.flags()]
507 self._stateextras[fd] = {'ancestorlinknode': hex(fca.node())}
507 self._stateextras[fd] = {'ancestorlinknode': hex(fca.node())}
508 self._dirty = True
508 self._dirty = True
509
509
510 def addpath(self, path, frename, forigin):
510 def addpath(self, path, frename, forigin):
511 """add a new conflicting path to the merge state
511 """add a new conflicting path to the merge state
512 path: the path that conflicts
512 path: the path that conflicts
513 frename: the filename the conflicting file was renamed to
513 frename: the filename the conflicting file was renamed to
514 forigin: origin of the file ('l' or 'r' for local/remote)
514 forigin: origin of the file ('l' or 'r' for local/remote)
515 """
515 """
516 self._state[path] = [MERGE_RECORD_UNRESOLVED_PATH, frename, forigin]
516 self._state[path] = [MERGE_RECORD_UNRESOLVED_PATH, frename, forigin]
517 self._dirty = True
517 self._dirty = True
518
518
519 def __contains__(self, dfile):
519 def __contains__(self, dfile):
520 return dfile in self._state
520 return dfile in self._state
521
521
522 def __getitem__(self, dfile):
522 def __getitem__(self, dfile):
523 return self._state[dfile][0]
523 return self._state[dfile][0]
524
524
525 def __iter__(self):
525 def __iter__(self):
526 return iter(sorted(self._state))
526 return iter(sorted(self._state))
527
527
528 def files(self):
528 def files(self):
529 return self._state.keys()
529 return self._state.keys()
530
530
531 def mark(self, dfile, state):
531 def mark(self, dfile, state):
532 self._state[dfile][0] = state
532 self._state[dfile][0] = state
533 self._dirty = True
533 self._dirty = True
534
534
535 def mdstate(self):
535 def mdstate(self):
536 return self._mdstate
536 return self._mdstate
537
537
538 def unresolved(self):
538 def unresolved(self):
539 """Obtain the paths of unresolved files."""
539 """Obtain the paths of unresolved files."""
540
540
541 for f, entry in self._state.iteritems():
541 for f, entry in self._state.iteritems():
542 if entry[0] in (MERGE_RECORD_UNRESOLVED,
542 if entry[0] in (MERGE_RECORD_UNRESOLVED,
543 MERGE_RECORD_UNRESOLVED_PATH):
543 MERGE_RECORD_UNRESOLVED_PATH):
544 yield f
544 yield f
545
545
546 def driverresolved(self):
546 def driverresolved(self):
547 """Obtain the paths of driver-resolved files."""
547 """Obtain the paths of driver-resolved files."""
548
548
549 for f, entry in self._state.items():
549 for f, entry in self._state.items():
550 if entry[0] == MERGE_RECORD_DRIVER_RESOLVED:
550 if entry[0] == MERGE_RECORD_DRIVER_RESOLVED:
551 yield f
551 yield f
552
552
553 def extras(self, filename):
553 def extras(self, filename):
554 return self._stateextras.setdefault(filename, {})
554 return self._stateextras.setdefault(filename, {})
555
555
556 def _resolve(self, preresolve, dfile, wctx):
556 def _resolve(self, preresolve, dfile, wctx):
557 """rerun merge process for file path `dfile`"""
557 """rerun merge process for file path `dfile`"""
558 if self[dfile] in (MERGE_RECORD_RESOLVED,
558 if self[dfile] in (MERGE_RECORD_RESOLVED,
559 MERGE_RECORD_DRIVER_RESOLVED):
559 MERGE_RECORD_DRIVER_RESOLVED):
560 return True, 0
560 return True, 0
561 stateentry = self._state[dfile]
561 stateentry = self._state[dfile]
562 state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry
562 state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry
563 octx = self._repo[self._other]
563 octx = self._repo[self._other]
564 extras = self.extras(dfile)
564 extras = self.extras(dfile)
565 anccommitnode = extras.get('ancestorlinknode')
565 anccommitnode = extras.get('ancestorlinknode')
566 if anccommitnode:
566 if anccommitnode:
567 actx = self._repo[anccommitnode]
567 actx = self._repo[anccommitnode]
568 else:
568 else:
569 actx = None
569 actx = None
570 fcd = self._filectxorabsent(localkey, wctx, dfile)
570 fcd = self._filectxorabsent(localkey, wctx, dfile)
571 fco = self._filectxorabsent(onode, octx, ofile)
571 fco = self._filectxorabsent(onode, octx, ofile)
572 # TODO: move this to filectxorabsent
572 # TODO: move this to filectxorabsent
573 fca = self._repo.filectx(afile, fileid=anode, changectx=actx)
573 fca = self._repo.filectx(afile, fileid=anode, changectx=actx)
574 # "premerge" x flags
574 # "premerge" x flags
575 flo = fco.flags()
575 flo = fco.flags()
576 fla = fca.flags()
576 fla = fca.flags()
577 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
577 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
578 if fca.node() == nullid and flags != flo:
578 if fca.node() == nullid and flags != flo:
579 if preresolve:
579 if preresolve:
580 self._repo.ui.warn(
580 self._repo.ui.warn(
581 _('warning: cannot merge flags for %s '
581 _('warning: cannot merge flags for %s '
582 'without common ancestor - keeping local flags\n')
582 'without common ancestor - keeping local flags\n')
583 % afile)
583 % afile)
584 elif flags == fla:
584 elif flags == fla:
585 flags = flo
585 flags = flo
586 if preresolve:
586 if preresolve:
587 # restore local
587 # restore local
588 if localkey != nullhex:
588 if localkey != nullhex:
589 f = self._repo.vfs('merge/' + localkey)
589 f = self._repo.vfs('merge/' + localkey)
590 wctx[dfile].write(f.read(), flags)
590 wctx[dfile].write(f.read(), flags)
591 f.close()
591 f.close()
592 else:
592 else:
593 wctx[dfile].remove(ignoremissing=True)
593 wctx[dfile].remove(ignoremissing=True)
594 complete, r, deleted = filemerge.premerge(self._repo, wctx,
594 complete, r, deleted = filemerge.premerge(self._repo, wctx,
595 self._local, lfile, fcd,
595 self._local, lfile, fcd,
596 fco, fca,
596 fco, fca,
597 labels=self._labels)
597 labels=self._labels)
598 else:
598 else:
599 complete, r, deleted = filemerge.filemerge(self._repo, wctx,
599 complete, r, deleted = filemerge.filemerge(self._repo, wctx,
600 self._local, lfile, fcd,
600 self._local, lfile, fcd,
601 fco, fca,
601 fco, fca,
602 labels=self._labels)
602 labels=self._labels)
603 if r is None:
603 if r is None:
604 # no real conflict
604 # no real conflict
605 del self._state[dfile]
605 del self._state[dfile]
606 self._stateextras.pop(dfile, None)
606 self._stateextras.pop(dfile, None)
607 self._dirty = True
607 self._dirty = True
608 elif not r:
608 elif not r:
609 self.mark(dfile, MERGE_RECORD_RESOLVED)
609 self.mark(dfile, MERGE_RECORD_RESOLVED)
610
610
611 if complete:
611 if complete:
612 action = None
612 action = None
613 if deleted:
613 if deleted:
614 if fcd.isabsent():
614 if fcd.isabsent():
615 # dc: local picked. Need to drop if present, which may
615 # dc: local picked. Need to drop if present, which may
616 # happen on re-resolves.
616 # happen on re-resolves.
617 action = ACTION_FORGET
617 action = ACTION_FORGET
618 else:
618 else:
619 # cd: remote picked (or otherwise deleted)
619 # cd: remote picked (or otherwise deleted)
620 action = ACTION_REMOVE
620 action = ACTION_REMOVE
621 else:
621 else:
622 if fcd.isabsent(): # dc: remote picked
622 if fcd.isabsent(): # dc: remote picked
623 action = ACTION_GET
623 action = ACTION_GET
624 elif fco.isabsent(): # cd: local picked
624 elif fco.isabsent(): # cd: local picked
625 if dfile in self.localctx:
625 if dfile in self.localctx:
626 action = ACTION_ADD_MODIFIED
626 action = ACTION_ADD_MODIFIED
627 else:
627 else:
628 action = ACTION_ADD
628 action = ACTION_ADD
629 # else: regular merges (no action necessary)
629 # else: regular merges (no action necessary)
630 self._results[dfile] = r, action
630 self._results[dfile] = r, action
631
631
632 return complete, r
632 return complete, r
633
633
634 def _filectxorabsent(self, hexnode, ctx, f):
634 def _filectxorabsent(self, hexnode, ctx, f):
635 if hexnode == nullhex:
635 if hexnode == nullhex:
636 return filemerge.absentfilectx(ctx, f)
636 return filemerge.absentfilectx(ctx, f)
637 else:
637 else:
638 return ctx[f]
638 return ctx[f]
639
639
640 def preresolve(self, dfile, wctx):
640 def preresolve(self, dfile, wctx):
641 """run premerge process for dfile
641 """run premerge process for dfile
642
642
643 Returns whether the merge is complete, and the exit code."""
643 Returns whether the merge is complete, and the exit code."""
644 return self._resolve(True, dfile, wctx)
644 return self._resolve(True, dfile, wctx)
645
645
646 def resolve(self, dfile, wctx):
646 def resolve(self, dfile, wctx):
647 """run merge process (assuming premerge was run) for dfile
647 """run merge process (assuming premerge was run) for dfile
648
648
649 Returns the exit code of the merge."""
649 Returns the exit code of the merge."""
650 return self._resolve(False, dfile, wctx)[1]
650 return self._resolve(False, dfile, wctx)[1]
651
651
652 def counts(self):
652 def counts(self):
653 """return counts for updated, merged and removed files in this
653 """return counts for updated, merged and removed files in this
654 session"""
654 session"""
655 updated, merged, removed = 0, 0, 0
655 updated, merged, removed = 0, 0, 0
656 for r, action in self._results.itervalues():
656 for r, action in self._results.itervalues():
657 if r is None:
657 if r is None:
658 updated += 1
658 updated += 1
659 elif r == 0:
659 elif r == 0:
660 if action == ACTION_REMOVE:
660 if action == ACTION_REMOVE:
661 removed += 1
661 removed += 1
662 else:
662 else:
663 merged += 1
663 merged += 1
664 return updated, merged, removed
664 return updated, merged, removed
665
665
666 def unresolvedcount(self):
666 def unresolvedcount(self):
667 """get unresolved count for this merge (persistent)"""
667 """get unresolved count for this merge (persistent)"""
668 return len(list(self.unresolved()))
668 return len(list(self.unresolved()))
669
669
670 def actions(self):
670 def actions(self):
671 """return lists of actions to perform on the dirstate"""
671 """return lists of actions to perform on the dirstate"""
672 actions = {
672 actions = {
673 ACTION_REMOVE: [],
673 ACTION_REMOVE: [],
674 ACTION_FORGET: [],
674 ACTION_FORGET: [],
675 ACTION_ADD: [],
675 ACTION_ADD: [],
676 ACTION_ADD_MODIFIED: [],
676 ACTION_ADD_MODIFIED: [],
677 ACTION_GET: [],
677 ACTION_GET: [],
678 }
678 }
679 for f, (r, action) in self._results.iteritems():
679 for f, (r, action) in self._results.iteritems():
680 if action is not None:
680 if action is not None:
681 actions[action].append((f, None, "merge result"))
681 actions[action].append((f, None, "merge result"))
682 return actions
682 return actions
683
683
684 def recordactions(self):
684 def recordactions(self):
685 """record remove/add/get actions in the dirstate"""
685 """record remove/add/get actions in the dirstate"""
686 branchmerge = self._repo.dirstate.p2() != nullid
686 branchmerge = self._repo.dirstate.p2() != nullid
687 recordupdates(self._repo, self.actions(), branchmerge, None)
687 recordupdates(self._repo, self.actions(), branchmerge, None)
688
688
689 def queueremove(self, f):
689 def queueremove(self, f):
690 """queues a file to be removed from the dirstate
690 """queues a file to be removed from the dirstate
691
691
692 Meant for use by custom merge drivers."""
692 Meant for use by custom merge drivers."""
693 self._results[f] = 0, ACTION_REMOVE
693 self._results[f] = 0, ACTION_REMOVE
694
694
695 def queueadd(self, f):
695 def queueadd(self, f):
696 """queues a file to be added to the dirstate
696 """queues a file to be added to the dirstate
697
697
698 Meant for use by custom merge drivers."""
698 Meant for use by custom merge drivers."""
699 self._results[f] = 0, ACTION_ADD
699 self._results[f] = 0, ACTION_ADD
700
700
701 def queueget(self, f):
701 def queueget(self, f):
702 """queues a file to be marked modified in the dirstate
702 """queues a file to be marked modified in the dirstate
703
703
704 Meant for use by custom merge drivers."""
704 Meant for use by custom merge drivers."""
705 self._results[f] = 0, ACTION_GET
705 self._results[f] = 0, ACTION_GET
706
706
707 def _getcheckunknownconfig(repo, section, name):
707 def _getcheckunknownconfig(repo, section, name):
708 config = repo.ui.config(section, name)
708 config = repo.ui.config(section, name)
709 valid = ['abort', 'ignore', 'warn']
709 valid = ['abort', 'ignore', 'warn']
710 if config not in valid:
710 if config not in valid:
711 validstr = ', '.join(["'" + v + "'" for v in valid])
711 validstr = ', '.join(["'" + v + "'" for v in valid])
712 raise error.ConfigError(_("%s.%s not valid "
712 raise error.ConfigError(_("%s.%s not valid "
713 "('%s' is none of %s)")
713 "('%s' is none of %s)")
714 % (section, name, config, validstr))
714 % (section, name, config, validstr))
715 return config
715 return config
716
716
717 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
717 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
718 if wctx.isinmemory():
718 if wctx.isinmemory():
719 # Nothing to do in IMM because nothing in the "working copy" can be an
719 # Nothing to do in IMM because nothing in the "working copy" can be an
720 # unknown file.
720 # unknown file.
721 #
721 #
722 # Note that we should bail out here, not in ``_checkunknownfiles()``,
722 # Note that we should bail out here, not in ``_checkunknownfiles()``,
723 # because that function does other useful work.
723 # because that function does other useful work.
724 return False
724 return False
725
725
726 if f2 is None:
726 if f2 is None:
727 f2 = f
727 f2 = f
728 return (repo.wvfs.audit.check(f)
728 return (repo.wvfs.audit.check(f)
729 and repo.wvfs.isfileorlink(f)
729 and repo.wvfs.isfileorlink(f)
730 and repo.dirstate.normalize(f) not in repo.dirstate
730 and repo.dirstate.normalize(f) not in repo.dirstate
731 and mctx[f2].cmp(wctx[f]))
731 and mctx[f2].cmp(wctx[f]))
732
732
733 class _unknowndirschecker(object):
733 class _unknowndirschecker(object):
734 """
734 """
735 Look for any unknown files or directories that may have a path conflict
735 Look for any unknown files or directories that may have a path conflict
736 with a file. If any path prefix of the file exists as a file or link,
736 with a file. If any path prefix of the file exists as a file or link,
737 then it conflicts. If the file itself is a directory that contains any
737 then it conflicts. If the file itself is a directory that contains any
738 file that is not tracked, then it conflicts.
738 file that is not tracked, then it conflicts.
739
739
740 Returns the shortest path at which a conflict occurs, or None if there is
740 Returns the shortest path at which a conflict occurs, or None if there is
741 no conflict.
741 no conflict.
742 """
742 """
743 def __init__(self):
743 def __init__(self):
744 # A set of paths known to be good. This prevents repeated checking of
744 # A set of paths known to be good. This prevents repeated checking of
745 # dirs. It will be updated with any new dirs that are checked and found
745 # dirs. It will be updated with any new dirs that are checked and found
746 # to be safe.
746 # to be safe.
747 self._unknowndircache = set()
747 self._unknowndircache = set()
748
748
749 # A set of paths that are known to be absent. This prevents repeated
749 # A set of paths that are known to be absent. This prevents repeated
750 # checking of subdirectories that are known not to exist. It will be
750 # checking of subdirectories that are known not to exist. It will be
751 # updated with any new dirs that are checked and found to be absent.
751 # updated with any new dirs that are checked and found to be absent.
752 self._missingdircache = set()
752 self._missingdircache = set()
753
753
754 def __call__(self, repo, wctx, f):
754 def __call__(self, repo, wctx, f):
755 if wctx.isinmemory():
755 if wctx.isinmemory():
756 # Nothing to do in IMM for the same reason as ``_checkunknownfile``.
756 # Nothing to do in IMM for the same reason as ``_checkunknownfile``.
757 return False
757 return False
758
758
759 # Check for path prefixes that exist as unknown files.
759 # Check for path prefixes that exist as unknown files.
760 for p in reversed(list(util.finddirs(f))):
760 for p in reversed(list(util.finddirs(f))):
761 if p in self._missingdircache:
761 if p in self._missingdircache:
762 return
762 return
763 if p in self._unknowndircache:
763 if p in self._unknowndircache:
764 continue
764 continue
765 if repo.wvfs.audit.check(p):
765 if repo.wvfs.audit.check(p):
766 if (repo.wvfs.isfileorlink(p)
766 if (repo.wvfs.isfileorlink(p)
767 and repo.dirstate.normalize(p) not in repo.dirstate):
767 and repo.dirstate.normalize(p) not in repo.dirstate):
768 return p
768 return p
769 if not repo.wvfs.lexists(p):
769 if not repo.wvfs.lexists(p):
770 self._missingdircache.add(p)
770 self._missingdircache.add(p)
771 return
771 return
772 self._unknowndircache.add(p)
772 self._unknowndircache.add(p)
773
773
774 # Check if the file conflicts with a directory containing unknown files.
774 # Check if the file conflicts with a directory containing unknown files.
775 if repo.wvfs.audit.check(f) and repo.wvfs.isdir(f):
775 if repo.wvfs.audit.check(f) and repo.wvfs.isdir(f):
776 # Does the directory contain any files that are not in the dirstate?
776 # Does the directory contain any files that are not in the dirstate?
777 for p, dirs, files in repo.wvfs.walk(f):
777 for p, dirs, files in repo.wvfs.walk(f):
778 for fn in files:
778 for fn in files:
779 relf = util.pconvert(repo.wvfs.reljoin(p, fn))
779 relf = util.pconvert(repo.wvfs.reljoin(p, fn))
780 relf = repo.dirstate.normalize(relf, isknown=True)
780 relf = repo.dirstate.normalize(relf, isknown=True)
781 if relf not in repo.dirstate:
781 if relf not in repo.dirstate:
782 return f
782 return f
783 return None
783 return None
784
784
785 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
785 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
786 """
786 """
787 Considers any actions that care about the presence of conflicting unknown
787 Considers any actions that care about the presence of conflicting unknown
788 files. For some actions, the result is to abort; for others, it is to
788 files. For some actions, the result is to abort; for others, it is to
789 choose a different action.
789 choose a different action.
790 """
790 """
791 fileconflicts = set()
791 fileconflicts = set()
792 pathconflicts = set()
792 pathconflicts = set()
793 warnconflicts = set()
793 warnconflicts = set()
794 abortconflicts = set()
794 abortconflicts = set()
795 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
795 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
796 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
796 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
797 pathconfig = repo.ui.configbool('experimental', 'merge.checkpathconflicts')
797 pathconfig = repo.ui.configbool('experimental', 'merge.checkpathconflicts')
798 if not force:
798 if not force:
799 def collectconflicts(conflicts, config):
799 def collectconflicts(conflicts, config):
800 if config == 'abort':
800 if config == 'abort':
801 abortconflicts.update(conflicts)
801 abortconflicts.update(conflicts)
802 elif config == 'warn':
802 elif config == 'warn':
803 warnconflicts.update(conflicts)
803 warnconflicts.update(conflicts)
804
804
805 checkunknowndirs = _unknowndirschecker()
805 checkunknowndirs = _unknowndirschecker()
806 for f, (m, args, msg) in actions.iteritems():
806 for f, (m, args, msg) in actions.iteritems():
807 if m in (ACTION_CREATED, ACTION_DELETED_CHANGED):
807 if m in (ACTION_CREATED, ACTION_DELETED_CHANGED):
808 if _checkunknownfile(repo, wctx, mctx, f):
808 if _checkunknownfile(repo, wctx, mctx, f):
809 fileconflicts.add(f)
809 fileconflicts.add(f)
810 elif pathconfig and f not in wctx:
810 elif pathconfig and f not in wctx:
811 path = checkunknowndirs(repo, wctx, f)
811 path = checkunknowndirs(repo, wctx, f)
812 if path is not None:
812 if path is not None:
813 pathconflicts.add(path)
813 pathconflicts.add(path)
814 elif m == ACTION_LOCAL_DIR_RENAME_GET:
814 elif m == ACTION_LOCAL_DIR_RENAME_GET:
815 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
815 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
816 fileconflicts.add(f)
816 fileconflicts.add(f)
817
817
818 allconflicts = fileconflicts | pathconflicts
818 allconflicts = fileconflicts | pathconflicts
819 ignoredconflicts = {c for c in allconflicts
819 ignoredconflicts = {c for c in allconflicts
820 if repo.dirstate._ignore(c)}
820 if repo.dirstate._ignore(c)}
821 unknownconflicts = allconflicts - ignoredconflicts
821 unknownconflicts = allconflicts - ignoredconflicts
822 collectconflicts(ignoredconflicts, ignoredconfig)
822 collectconflicts(ignoredconflicts, ignoredconfig)
823 collectconflicts(unknownconflicts, unknownconfig)
823 collectconflicts(unknownconflicts, unknownconfig)
824 else:
824 else:
825 for f, (m, args, msg) in actions.iteritems():
825 for f, (m, args, msg) in actions.iteritems():
826 if m == ACTION_CREATED_MERGE:
826 if m == ACTION_CREATED_MERGE:
827 fl2, anc = args
827 fl2, anc = args
828 different = _checkunknownfile(repo, wctx, mctx, f)
828 different = _checkunknownfile(repo, wctx, mctx, f)
829 if repo.dirstate._ignore(f):
829 if repo.dirstate._ignore(f):
830 config = ignoredconfig
830 config = ignoredconfig
831 else:
831 else:
832 config = unknownconfig
832 config = unknownconfig
833
833
834 # The behavior when force is True is described by this table:
834 # The behavior when force is True is described by this table:
835 # config different mergeforce | action backup
835 # config different mergeforce | action backup
836 # * n * | get n
836 # * n * | get n
837 # * y y | merge -
837 # * y y | merge -
838 # abort y n | merge - (1)
838 # abort y n | merge - (1)
839 # warn y n | warn + get y
839 # warn y n | warn + get y
840 # ignore y n | get y
840 # ignore y n | get y
841 #
841 #
842 # (1) this is probably the wrong behavior here -- we should
842 # (1) this is probably the wrong behavior here -- we should
843 # probably abort, but some actions like rebases currently
843 # probably abort, but some actions like rebases currently
844 # don't like an abort happening in the middle of
844 # don't like an abort happening in the middle of
845 # merge.update.
845 # merge.update.
846 if not different:
846 if not different:
847 actions[f] = (ACTION_GET, (fl2, False), 'remote created')
847 actions[f] = (ACTION_GET, (fl2, False), 'remote created')
848 elif mergeforce or config == 'abort':
848 elif mergeforce or config == 'abort':
849 actions[f] = (ACTION_MERGE, (f, f, None, False, anc),
849 actions[f] = (ACTION_MERGE, (f, f, None, False, anc),
850 'remote differs from untracked local')
850 'remote differs from untracked local')
851 elif config == 'abort':
851 elif config == 'abort':
852 abortconflicts.add(f)
852 abortconflicts.add(f)
853 else:
853 else:
854 if config == 'warn':
854 if config == 'warn':
855 warnconflicts.add(f)
855 warnconflicts.add(f)
856 actions[f] = (ACTION_GET, (fl2, True), 'remote created')
856 actions[f] = (ACTION_GET, (fl2, True), 'remote created')
857
857
858 for f in sorted(abortconflicts):
858 for f in sorted(abortconflicts):
859 warn = repo.ui.warn
859 warn = repo.ui.warn
860 if f in pathconflicts:
860 if f in pathconflicts:
861 if repo.wvfs.isfileorlink(f):
861 if repo.wvfs.isfileorlink(f):
862 warn(_("%s: untracked file conflicts with directory\n") % f)
862 warn(_("%s: untracked file conflicts with directory\n") % f)
863 else:
863 else:
864 warn(_("%s: untracked directory conflicts with file\n") % f)
864 warn(_("%s: untracked directory conflicts with file\n") % f)
865 else:
865 else:
866 warn(_("%s: untracked file differs\n") % f)
866 warn(_("%s: untracked file differs\n") % f)
867 if abortconflicts:
867 if abortconflicts:
868 raise error.Abort(_("untracked files in working directory "
868 raise error.Abort(_("untracked files in working directory "
869 "differ from files in requested revision"))
869 "differ from files in requested revision"))
870
870
871 for f in sorted(warnconflicts):
871 for f in sorted(warnconflicts):
872 if repo.wvfs.isfileorlink(f):
872 if repo.wvfs.isfileorlink(f):
873 repo.ui.warn(_("%s: replacing untracked file\n") % f)
873 repo.ui.warn(_("%s: replacing untracked file\n") % f)
874 else:
874 else:
875 repo.ui.warn(_("%s: replacing untracked files in directory\n") % f)
875 repo.ui.warn(_("%s: replacing untracked files in directory\n") % f)
876
876
877 for f, (m, args, msg) in actions.iteritems():
877 for f, (m, args, msg) in actions.iteritems():
878 if m == ACTION_CREATED:
878 if m == ACTION_CREATED:
879 backup = (f in fileconflicts or f in pathconflicts or
879 backup = (f in fileconflicts or f in pathconflicts or
880 any(p in pathconflicts for p in util.finddirs(f)))
880 any(p in pathconflicts for p in util.finddirs(f)))
881 flags, = args
881 flags, = args
882 actions[f] = (ACTION_GET, (flags, backup), msg)
882 actions[f] = (ACTION_GET, (flags, backup), msg)
883
883
884 def _forgetremoved(wctx, mctx, branchmerge):
884 def _forgetremoved(wctx, mctx, branchmerge):
885 """
885 """
886 Forget removed files
886 Forget removed files
887
887
888 If we're jumping between revisions (as opposed to merging), and if
888 If we're jumping between revisions (as opposed to merging), and if
889 neither the working directory nor the target rev has the file,
889 neither the working directory nor the target rev has the file,
890 then we need to remove it from the dirstate, to prevent the
890 then we need to remove it from the dirstate, to prevent the
891 dirstate from listing the file when it is no longer in the
891 dirstate from listing the file when it is no longer in the
892 manifest.
892 manifest.
893
893
894 If we're merging, and the other revision has removed a file
894 If we're merging, and the other revision has removed a file
895 that is not present in the working directory, we need to mark it
895 that is not present in the working directory, we need to mark it
896 as removed.
896 as removed.
897 """
897 """
898
898
899 actions = {}
899 actions = {}
900 m = ACTION_FORGET
900 m = ACTION_FORGET
901 if branchmerge:
901 if branchmerge:
902 m = ACTION_REMOVE
902 m = ACTION_REMOVE
903 for f in wctx.deleted():
903 for f in wctx.deleted():
904 if f not in mctx:
904 if f not in mctx:
905 actions[f] = m, None, "forget deleted"
905 actions[f] = m, None, "forget deleted"
906
906
907 if not branchmerge:
907 if not branchmerge:
908 for f in wctx.removed():
908 for f in wctx.removed():
909 if f not in mctx:
909 if f not in mctx:
910 actions[f] = ACTION_FORGET, None, "forget removed"
910 actions[f] = ACTION_FORGET, None, "forget removed"
911
911
912 return actions
912 return actions
913
913
914 def _checkcollision(repo, wmf, actions):
914 def _checkcollision(repo, wmf, actions):
915 """
915 """
916 Check for case-folding collisions.
916 Check for case-folding collisions.
917 """
917 """
918
918
919 # If the repo is narrowed, filter out files outside the narrowspec.
919 # If the repo is narrowed, filter out files outside the narrowspec.
920 narrowmatch = repo.narrowmatch()
920 narrowmatch = repo.narrowmatch()
921 if not narrowmatch.always():
921 if not narrowmatch.always():
922 wmf = wmf.matches(narrowmatch)
922 wmf = wmf.matches(narrowmatch)
923 if actions:
923 if actions:
924 narrowactions = {}
924 narrowactions = {}
925 for m, actionsfortype in actions.iteritems():
925 for m, actionsfortype in actions.iteritems():
926 narrowactions[m] = []
926 narrowactions[m] = []
927 for (f, args, msg) in actionsfortype:
927 for (f, args, msg) in actionsfortype:
928 if narrowmatch(f):
928 if narrowmatch(f):
929 narrowactions[m].append((f, args, msg))
929 narrowactions[m].append((f, args, msg))
930 actions = narrowactions
930 actions = narrowactions
931
931
932 # build provisional merged manifest up
932 # build provisional merged manifest up
933 pmmf = set(wmf)
933 pmmf = set(wmf)
934
934
935 if actions:
935 if actions:
936 # KEEP and EXEC are no-op
936 # KEEP and EXEC are no-op
937 for m in (ACTION_ADD, ACTION_ADD_MODIFIED, ACTION_FORGET, ACTION_GET,
937 for m in (ACTION_ADD, ACTION_ADD_MODIFIED, ACTION_FORGET, ACTION_GET,
938 ACTION_CHANGED_DELETED, ACTION_DELETED_CHANGED):
938 ACTION_CHANGED_DELETED, ACTION_DELETED_CHANGED):
939 for f, args, msg in actions[m]:
939 for f, args, msg in actions[m]:
940 pmmf.add(f)
940 pmmf.add(f)
941 for f, args, msg in actions[ACTION_REMOVE]:
941 for f, args, msg in actions[ACTION_REMOVE]:
942 pmmf.discard(f)
942 pmmf.discard(f)
943 for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]:
943 for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]:
944 f2, flags = args
944 f2, flags = args
945 pmmf.discard(f2)
945 pmmf.discard(f2)
946 pmmf.add(f)
946 pmmf.add(f)
947 for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]:
947 for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]:
948 pmmf.add(f)
948 pmmf.add(f)
949 for f, args, msg in actions[ACTION_MERGE]:
949 for f, args, msg in actions[ACTION_MERGE]:
950 f1, f2, fa, move, anc = args
950 f1, f2, fa, move, anc = args
951 if move:
951 if move:
952 pmmf.discard(f1)
952 pmmf.discard(f1)
953 pmmf.add(f)
953 pmmf.add(f)
954
954
955 # check case-folding collision in provisional merged manifest
955 # check case-folding collision in provisional merged manifest
956 foldmap = {}
956 foldmap = {}
957 for f in pmmf:
957 for f in pmmf:
958 fold = util.normcase(f)
958 fold = util.normcase(f)
959 if fold in foldmap:
959 if fold in foldmap:
960 raise error.Abort(_("case-folding collision between %s and %s")
960 raise error.Abort(_("case-folding collision between %s and %s")
961 % (f, foldmap[fold]))
961 % (f, foldmap[fold]))
962 foldmap[fold] = f
962 foldmap[fold] = f
963
963
964 # check case-folding of directories
964 # check case-folding of directories
965 foldprefix = unfoldprefix = lastfull = ''
965 foldprefix = unfoldprefix = lastfull = ''
966 for fold, f in sorted(foldmap.items()):
966 for fold, f in sorted(foldmap.items()):
967 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
967 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
968 # the folded prefix matches but actual casing is different
968 # the folded prefix matches but actual casing is different
969 raise error.Abort(_("case-folding collision between "
969 raise error.Abort(_("case-folding collision between "
970 "%s and directory of %s") % (lastfull, f))
970 "%s and directory of %s") % (lastfull, f))
971 foldprefix = fold + '/'
971 foldprefix = fold + '/'
972 unfoldprefix = f + '/'
972 unfoldprefix = f + '/'
973 lastfull = f
973 lastfull = f
974
974
975 def driverpreprocess(repo, ms, wctx, labels=None):
975 def driverpreprocess(repo, ms, wctx, labels=None):
976 """run the preprocess step of the merge driver, if any
976 """run the preprocess step of the merge driver, if any
977
977
978 This is currently not implemented -- it's an extension point."""
978 This is currently not implemented -- it's an extension point."""
979 return True
979 return True
980
980
981 def driverconclude(repo, ms, wctx, labels=None):
981 def driverconclude(repo, ms, wctx, labels=None):
982 """run the conclude step of the merge driver, if any
982 """run the conclude step of the merge driver, if any
983
983
984 This is currently not implemented -- it's an extension point."""
984 This is currently not implemented -- it's an extension point."""
985 return True
985 return True
986
986
987 def _filesindirs(repo, manifest, dirs):
987 def _filesindirs(repo, manifest, dirs):
988 """
988 """
989 Generator that yields pairs of all the files in the manifest that are found
989 Generator that yields pairs of all the files in the manifest that are found
990 inside the directories listed in dirs, and which directory they are found
990 inside the directories listed in dirs, and which directory they are found
991 in.
991 in.
992 """
992 """
993 for f in manifest:
993 for f in manifest:
994 for p in util.finddirs(f):
994 for p in util.finddirs(f):
995 if p in dirs:
995 if p in dirs:
996 yield f, p
996 yield f, p
997 break
997 break
998
998
999 def checkpathconflicts(repo, wctx, mctx, actions):
999 def checkpathconflicts(repo, wctx, mctx, actions):
1000 """
1000 """
1001 Check if any actions introduce path conflicts in the repository, updating
1001 Check if any actions introduce path conflicts in the repository, updating
1002 actions to record or handle the path conflict accordingly.
1002 actions to record or handle the path conflict accordingly.
1003 """
1003 """
1004 mf = wctx.manifest()
1004 mf = wctx.manifest()
1005
1005
1006 # The set of local files that conflict with a remote directory.
1006 # The set of local files that conflict with a remote directory.
1007 localconflicts = set()
1007 localconflicts = set()
1008
1008
1009 # The set of directories that conflict with a remote file, and so may cause
1009 # The set of directories that conflict with a remote file, and so may cause
1010 # conflicts if they still contain any files after the merge.
1010 # conflicts if they still contain any files after the merge.
1011 remoteconflicts = set()
1011 remoteconflicts = set()
1012
1012
1013 # The set of directories that appear as both a file and a directory in the
1013 # The set of directories that appear as both a file and a directory in the
1014 # remote manifest. These indicate an invalid remote manifest, which
1014 # remote manifest. These indicate an invalid remote manifest, which
1015 # can't be updated to cleanly.
1015 # can't be updated to cleanly.
1016 invalidconflicts = set()
1016 invalidconflicts = set()
1017
1017
1018 # The set of directories that contain files that are being created.
1018 # The set of directories that contain files that are being created.
1019 createdfiledirs = set()
1019 createdfiledirs = set()
1020
1020
1021 # The set of files deleted by all the actions.
1021 # The set of files deleted by all the actions.
1022 deletedfiles = set()
1022 deletedfiles = set()
1023
1023
1024 for f, (m, args, msg) in actions.items():
1024 for f, (m, args, msg) in actions.items():
1025 if m in (ACTION_CREATED, ACTION_DELETED_CHANGED, ACTION_MERGE,
1025 if m in (ACTION_CREATED, ACTION_DELETED_CHANGED, ACTION_MERGE,
1026 ACTION_CREATED_MERGE):
1026 ACTION_CREATED_MERGE):
1027 # This action may create a new local file.
1027 # This action may create a new local file.
1028 createdfiledirs.update(util.finddirs(f))
1028 createdfiledirs.update(util.finddirs(f))
1029 if mf.hasdir(f):
1029 if mf.hasdir(f):
1030 # The file aliases a local directory. This might be ok if all
1030 # The file aliases a local directory. This might be ok if all
1031 # the files in the local directory are being deleted. This
1031 # the files in the local directory are being deleted. This
1032 # will be checked once we know what all the deleted files are.
1032 # will be checked once we know what all the deleted files are.
1033 remoteconflicts.add(f)
1033 remoteconflicts.add(f)
1034 # Track the names of all deleted files.
1034 # Track the names of all deleted files.
1035 if m == ACTION_REMOVE:
1035 if m == ACTION_REMOVE:
1036 deletedfiles.add(f)
1036 deletedfiles.add(f)
1037 if m == ACTION_MERGE:
1037 if m == ACTION_MERGE:
1038 f1, f2, fa, move, anc = args
1038 f1, f2, fa, move, anc = args
1039 if move:
1039 if move:
1040 deletedfiles.add(f1)
1040 deletedfiles.add(f1)
1041 if m == ACTION_DIR_RENAME_MOVE_LOCAL:
1041 if m == ACTION_DIR_RENAME_MOVE_LOCAL:
1042 f2, flags = args
1042 f2, flags = args
1043 deletedfiles.add(f2)
1043 deletedfiles.add(f2)
1044
1044
1045 # Check all directories that contain created files for path conflicts.
1045 # Check all directories that contain created files for path conflicts.
1046 for p in createdfiledirs:
1046 for p in createdfiledirs:
1047 if p in mf:
1047 if p in mf:
1048 if p in mctx:
1048 if p in mctx:
1049 # A file is in a directory which aliases both a local
1049 # A file is in a directory which aliases both a local
1050 # and a remote file. This is an internal inconsistency
1050 # and a remote file. This is an internal inconsistency
1051 # within the remote manifest.
1051 # within the remote manifest.
1052 invalidconflicts.add(p)
1052 invalidconflicts.add(p)
1053 else:
1053 else:
1054 # A file is in a directory which aliases a local file.
1054 # A file is in a directory which aliases a local file.
1055 # We will need to rename the local file.
1055 # We will need to rename the local file.
1056 localconflicts.add(p)
1056 localconflicts.add(p)
1057 if p in actions and actions[p][0] in (ACTION_CREATED,
1057 if p in actions and actions[p][0] in (ACTION_CREATED,
1058 ACTION_DELETED_CHANGED,
1058 ACTION_DELETED_CHANGED,
1059 ACTION_MERGE,
1059 ACTION_MERGE,
1060 ACTION_CREATED_MERGE):
1060 ACTION_CREATED_MERGE):
1061 # The file is in a directory which aliases a remote file.
1061 # The file is in a directory which aliases a remote file.
1062 # This is an internal inconsistency within the remote
1062 # This is an internal inconsistency within the remote
1063 # manifest.
1063 # manifest.
1064 invalidconflicts.add(p)
1064 invalidconflicts.add(p)
1065
1065
1066 # Rename all local conflicting files that have not been deleted.
1066 # Rename all local conflicting files that have not been deleted.
1067 for p in localconflicts:
1067 for p in localconflicts:
1068 if p not in deletedfiles:
1068 if p not in deletedfiles:
1069 ctxname = bytes(wctx).rstrip('+')
1069 ctxname = bytes(wctx).rstrip('+')
1070 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
1070 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
1071 actions[pnew] = (ACTION_PATH_CONFLICT_RESOLVE, (p,),
1071 actions[pnew] = (ACTION_PATH_CONFLICT_RESOLVE, (p,),
1072 'local path conflict')
1072 'local path conflict')
1073 actions[p] = (ACTION_PATH_CONFLICT, (pnew, 'l'),
1073 actions[p] = (ACTION_PATH_CONFLICT, (pnew, 'l'),
1074 'path conflict')
1074 'path conflict')
1075
1075
1076 if remoteconflicts:
1076 if remoteconflicts:
1077 # Check if all files in the conflicting directories have been removed.
1077 # Check if all files in the conflicting directories have been removed.
1078 ctxname = bytes(mctx).rstrip('+')
1078 ctxname = bytes(mctx).rstrip('+')
1079 for f, p in _filesindirs(repo, mf, remoteconflicts):
1079 for f, p in _filesindirs(repo, mf, remoteconflicts):
1080 if f not in deletedfiles:
1080 if f not in deletedfiles:
1081 m, args, msg = actions[p]
1081 m, args, msg = actions[p]
1082 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
1082 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
1083 if m in (ACTION_DELETED_CHANGED, ACTION_MERGE):
1083 if m in (ACTION_DELETED_CHANGED, ACTION_MERGE):
1084 # Action was merge, just update target.
1084 # Action was merge, just update target.
1085 actions[pnew] = (m, args, msg)
1085 actions[pnew] = (m, args, msg)
1086 else:
1086 else:
1087 # Action was create, change to renamed get action.
1087 # Action was create, change to renamed get action.
1088 fl = args[0]
1088 fl = args[0]
1089 actions[pnew] = (ACTION_LOCAL_DIR_RENAME_GET, (p, fl),
1089 actions[pnew] = (ACTION_LOCAL_DIR_RENAME_GET, (p, fl),
1090 'remote path conflict')
1090 'remote path conflict')
1091 actions[p] = (ACTION_PATH_CONFLICT, (pnew, ACTION_REMOVE),
1091 actions[p] = (ACTION_PATH_CONFLICT, (pnew, ACTION_REMOVE),
1092 'path conflict')
1092 'path conflict')
1093 remoteconflicts.remove(p)
1093 remoteconflicts.remove(p)
1094 break
1094 break
1095
1095
1096 if invalidconflicts:
1096 if invalidconflicts:
1097 for p in invalidconflicts:
1097 for p in invalidconflicts:
1098 repo.ui.warn(_("%s: is both a file and a directory\n") % p)
1098 repo.ui.warn(_("%s: is both a file and a directory\n") % p)
1099 raise error.Abort(_("destination manifest contains path conflicts"))
1099 raise error.Abort(_("destination manifest contains path conflicts"))
1100
1100
1101 def _filternarrowactions(narrowmatch, branchmerge, actions):
1101 def _filternarrowactions(narrowmatch, branchmerge, actions):
1102 """
1102 """
1103 Filters out actions that can ignored because the repo is narrowed.
1103 Filters out actions that can ignored because the repo is narrowed.
1104
1104
1105 Raise an exception if the merge cannot be completed because the repo is
1105 Raise an exception if the merge cannot be completed because the repo is
1106 narrowed.
1106 narrowed.
1107 """
1107 """
1108 nooptypes = {'k'} # TODO: handle with nonconflicttypes
1108 nooptypes = {'k'} # TODO: handle with nonconflicttypes
1109 nonconflicttypes = set('a am c cm f g r e'.split())
1109 nonconflicttypes = set('a am c cm f g r e'.split())
1110 # We mutate the items in the dict during iteration, so iterate
1110 # We mutate the items in the dict during iteration, so iterate
1111 # over a copy.
1111 # over a copy.
1112 for f, action in list(actions.items()):
1112 for f, action in list(actions.items()):
1113 if narrowmatch(f):
1113 if narrowmatch(f):
1114 pass
1114 pass
1115 elif not branchmerge:
1115 elif not branchmerge:
1116 del actions[f] # just updating, ignore changes outside clone
1116 del actions[f] # just updating, ignore changes outside clone
1117 elif action[0] in nooptypes:
1117 elif action[0] in nooptypes:
1118 del actions[f] # merge does not affect file
1118 del actions[f] # merge does not affect file
1119 elif action[0] in nonconflicttypes:
1119 elif action[0] in nonconflicttypes:
1120 raise error.Abort(_('merge affects file \'%s\' outside narrow, '
1120 raise error.Abort(_('merge affects file \'%s\' outside narrow, '
1121 'which is not yet supported') % f,
1121 'which is not yet supported') % f,
1122 hint=_('merging in the other direction '
1122 hint=_('merging in the other direction '
1123 'may work'))
1123 'may work'))
1124 else:
1124 else:
1125 raise error.Abort(_('conflict in file \'%s\' is outside '
1125 raise error.Abort(_('conflict in file \'%s\' is outside '
1126 'narrow clone') % f)
1126 'narrow clone') % f)
1127
1127
1128 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
1128 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
1129 acceptremote, followcopies, forcefulldiff=False):
1129 acceptremote, followcopies, forcefulldiff=False):
1130 """
1130 """
1131 Merge wctx and p2 with ancestor pa and generate merge action list
1131 Merge wctx and p2 with ancestor pa and generate merge action list
1132
1132
1133 branchmerge and force are as passed in to update
1133 branchmerge and force are as passed in to update
1134 matcher = matcher to filter file lists
1134 matcher = matcher to filter file lists
1135 acceptremote = accept the incoming changes without prompting
1135 acceptremote = accept the incoming changes without prompting
1136 """
1136 """
1137 if matcher is not None and matcher.always():
1137 if matcher is not None and matcher.always():
1138 matcher = None
1138 matcher = None
1139
1139
1140 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
1140 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
1141
1141
1142 # manifests fetched in order are going to be faster, so prime the caches
1142 # manifests fetched in order are going to be faster, so prime the caches
1143 [x.manifest() for x in
1143 [x.manifest() for x in
1144 sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)]
1144 sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)]
1145
1145
1146 if followcopies:
1146 if followcopies:
1147 ret = copies.mergecopies(repo, wctx, p2, pa)
1147 ret = copies.mergecopies(repo, wctx, p2, pa)
1148 copy, movewithdir, diverge, renamedelete, dirmove = ret
1148 copy, movewithdir, diverge, renamedelete, dirmove = ret
1149
1149
1150 boolbm = pycompat.bytestr(bool(branchmerge))
1150 boolbm = pycompat.bytestr(bool(branchmerge))
1151 boolf = pycompat.bytestr(bool(force))
1151 boolf = pycompat.bytestr(bool(force))
1152 boolm = pycompat.bytestr(bool(matcher))
1152 boolm = pycompat.bytestr(bool(matcher))
1153 repo.ui.note(_("resolving manifests\n"))
1153 repo.ui.note(_("resolving manifests\n"))
1154 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
1154 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
1155 % (boolbm, boolf, boolm))
1155 % (boolbm, boolf, boolm))
1156 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
1156 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
1157
1157
1158 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
1158 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
1159 copied = set(copy.values())
1159 copied = set(copy.values())
1160 copied.update(movewithdir.values())
1160 copied.update(movewithdir.values())
1161
1161
1162 if '.hgsubstate' in m1 and wctx.rev() is None:
1162 if '.hgsubstate' in m1 and wctx.rev() is None:
1163 # Check whether sub state is modified, and overwrite the manifest
1163 # Check whether sub state is modified, and overwrite the manifest
1164 # to flag the change. If wctx is a committed revision, we shouldn't
1164 # to flag the change. If wctx is a committed revision, we shouldn't
1165 # care for the dirty state of the working directory.
1165 # care for the dirty state of the working directory.
1166 if any(wctx.sub(s).dirty() for s in wctx.substate):
1166 if any(wctx.sub(s).dirty() for s in wctx.substate):
1167 m1['.hgsubstate'] = modifiednodeid
1167 m1['.hgsubstate'] = modifiednodeid
1168
1168
1169 # Don't use m2-vs-ma optimization if:
1169 # Don't use m2-vs-ma optimization if:
1170 # - ma is the same as m1 or m2, which we're just going to diff again later
1170 # - ma is the same as m1 or m2, which we're just going to diff again later
1171 # - The caller specifically asks for a full diff, which is useful during bid
1171 # - The caller specifically asks for a full diff, which is useful during bid
1172 # merge.
1172 # merge.
1173 if (pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff):
1173 if (pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff):
1174 # Identify which files are relevant to the merge, so we can limit the
1174 # Identify which files are relevant to the merge, so we can limit the
1175 # total m1-vs-m2 diff to just those files. This has significant
1175 # total m1-vs-m2 diff to just those files. This has significant
1176 # performance benefits in large repositories.
1176 # performance benefits in large repositories.
1177 relevantfiles = set(ma.diff(m2).keys())
1177 relevantfiles = set(ma.diff(m2).keys())
1178
1178
1179 # For copied and moved files, we need to add the source file too.
1179 # For copied and moved files, we need to add the source file too.
1180 for copykey, copyvalue in copy.iteritems():
1180 for copykey, copyvalue in copy.iteritems():
1181 if copyvalue in relevantfiles:
1181 if copyvalue in relevantfiles:
1182 relevantfiles.add(copykey)
1182 relevantfiles.add(copykey)
1183 for movedirkey in movewithdir:
1183 for movedirkey in movewithdir:
1184 relevantfiles.add(movedirkey)
1184 relevantfiles.add(movedirkey)
1185 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
1185 filesmatcher = scmutil.matchfiles(repo, relevantfiles)
1186 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
1186 matcher = matchmod.intersectmatchers(matcher, filesmatcher)
1187
1187
1188 diff = m1.diff(m2, match=matcher)
1188 diff = m1.diff(m2, match=matcher)
1189
1189
1190 actions = {}
1190 actions = {}
1191 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
1191 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
1192 if n1 and n2: # file exists on both local and remote side
1192 if n1 and n2: # file exists on both local and remote side
1193 if f not in ma:
1193 if f not in ma:
1194 fa = copy.get(f, None)
1194 fa = copy.get(f, None)
1195 if fa is not None:
1195 if fa is not None:
1196 actions[f] = (ACTION_MERGE, (f, f, fa, False, pa.node()),
1196 actions[f] = (ACTION_MERGE, (f, f, fa, False, pa.node()),
1197 'both renamed from %s' % fa)
1197 'both renamed from %s' % fa)
1198 else:
1198 else:
1199 actions[f] = (ACTION_MERGE, (f, f, None, False, pa.node()),
1199 actions[f] = (ACTION_MERGE, (f, f, None, False, pa.node()),
1200 'both created')
1200 'both created')
1201 else:
1201 else:
1202 a = ma[f]
1202 a = ma[f]
1203 fla = ma.flags(f)
1203 fla = ma.flags(f)
1204 nol = 'l' not in fl1 + fl2 + fla
1204 nol = 'l' not in fl1 + fl2 + fla
1205 if n2 == a and fl2 == fla:
1205 if n2 == a and fl2 == fla:
1206 actions[f] = (ACTION_KEEP, (), 'remote unchanged')
1206 actions[f] = (ACTION_KEEP, (), 'remote unchanged')
1207 elif n1 == a and fl1 == fla: # local unchanged - use remote
1207 elif n1 == a and fl1 == fla: # local unchanged - use remote
1208 if n1 == n2: # optimization: keep local content
1208 if n1 == n2: # optimization: keep local content
1209 actions[f] = (ACTION_EXEC, (fl2,), 'update permissions')
1209 actions[f] = (ACTION_EXEC, (fl2,), 'update permissions')
1210 else:
1210 else:
1211 actions[f] = (ACTION_GET, (fl2, False),
1211 actions[f] = (ACTION_GET, (fl2, False),
1212 'remote is newer')
1212 'remote is newer')
1213 elif nol and n2 == a: # remote only changed 'x'
1213 elif nol and n2 == a: # remote only changed 'x'
1214 actions[f] = (ACTION_EXEC, (fl2,), 'update permissions')
1214 actions[f] = (ACTION_EXEC, (fl2,), 'update permissions')
1215 elif nol and n1 == a: # local only changed 'x'
1215 elif nol and n1 == a: # local only changed 'x'
1216 actions[f] = (ACTION_GET, (fl1, False), 'remote is newer')
1216 actions[f] = (ACTION_GET, (fl1, False), 'remote is newer')
1217 else: # both changed something
1217 else: # both changed something
1218 actions[f] = (ACTION_MERGE, (f, f, f, False, pa.node()),
1218 actions[f] = (ACTION_MERGE, (f, f, f, False, pa.node()),
1219 'versions differ')
1219 'versions differ')
1220 elif n1: # file exists only on local side
1220 elif n1: # file exists only on local side
1221 if f in copied:
1221 if f in copied:
1222 pass # we'll deal with it on m2 side
1222 pass # we'll deal with it on m2 side
1223 elif f in movewithdir: # directory rename, move local
1223 elif f in movewithdir: # directory rename, move local
1224 f2 = movewithdir[f]
1224 f2 = movewithdir[f]
1225 if f2 in m2:
1225 if f2 in m2:
1226 actions[f2] = (ACTION_MERGE, (f, f2, None, True, pa.node()),
1226 actions[f2] = (ACTION_MERGE, (f, f2, None, True, pa.node()),
1227 'remote directory rename, both created')
1227 'remote directory rename, both created')
1228 else:
1228 else:
1229 actions[f2] = (ACTION_DIR_RENAME_MOVE_LOCAL, (f, fl1),
1229 actions[f2] = (ACTION_DIR_RENAME_MOVE_LOCAL, (f, fl1),
1230 'remote directory rename - move from %s' % f)
1230 'remote directory rename - move from %s' % f)
1231 elif f in copy:
1231 elif f in copy:
1232 f2 = copy[f]
1232 f2 = copy[f]
1233 actions[f] = (ACTION_MERGE, (f, f2, f2, False, pa.node()),
1233 actions[f] = (ACTION_MERGE, (f, f2, f2, False, pa.node()),
1234 'local copied/moved from %s' % f2)
1234 'local copied/moved from %s' % f2)
1235 elif f in ma: # clean, a different, no remote
1235 elif f in ma: # clean, a different, no remote
1236 if n1 != ma[f]:
1236 if n1 != ma[f]:
1237 if acceptremote:
1237 if acceptremote:
1238 actions[f] = (ACTION_REMOVE, None, 'remote delete')
1238 actions[f] = (ACTION_REMOVE, None, 'remote delete')
1239 else:
1239 else:
1240 actions[f] = (ACTION_CHANGED_DELETED,
1240 actions[f] = (ACTION_CHANGED_DELETED,
1241 (f, None, f, False, pa.node()),
1241 (f, None, f, False, pa.node()),
1242 'prompt changed/deleted')
1242 'prompt changed/deleted')
1243 elif n1 == addednodeid:
1243 elif n1 == addednodeid:
1244 # This extra 'a' is added by working copy manifest to mark
1244 # This extra 'a' is added by working copy manifest to mark
1245 # the file as locally added. We should forget it instead of
1245 # the file as locally added. We should forget it instead of
1246 # deleting it.
1246 # deleting it.
1247 actions[f] = (ACTION_FORGET, None, 'remote deleted')
1247 actions[f] = (ACTION_FORGET, None, 'remote deleted')
1248 else:
1248 else:
1249 actions[f] = (ACTION_REMOVE, None, 'other deleted')
1249 actions[f] = (ACTION_REMOVE, None, 'other deleted')
1250 elif n2: # file exists only on remote side
1250 elif n2: # file exists only on remote side
1251 if f in copied:
1251 if f in copied:
1252 pass # we'll deal with it on m1 side
1252 pass # we'll deal with it on m1 side
1253 elif f in movewithdir:
1253 elif f in movewithdir:
1254 f2 = movewithdir[f]
1254 f2 = movewithdir[f]
1255 if f2 in m1:
1255 if f2 in m1:
1256 actions[f2] = (ACTION_MERGE,
1256 actions[f2] = (ACTION_MERGE,
1257 (f2, f, None, False, pa.node()),
1257 (f2, f, None, False, pa.node()),
1258 'local directory rename, both created')
1258 'local directory rename, both created')
1259 else:
1259 else:
1260 actions[f2] = (ACTION_LOCAL_DIR_RENAME_GET, (f, fl2),
1260 actions[f2] = (ACTION_LOCAL_DIR_RENAME_GET, (f, fl2),
1261 'local directory rename - get from %s' % f)
1261 'local directory rename - get from %s' % f)
1262 elif f in copy:
1262 elif f in copy:
1263 f2 = copy[f]
1263 f2 = copy[f]
1264 if f2 in m2:
1264 if f2 in m2:
1265 actions[f] = (ACTION_MERGE, (f2, f, f2, False, pa.node()),
1265 actions[f] = (ACTION_MERGE, (f2, f, f2, False, pa.node()),
1266 'remote copied from %s' % f2)
1266 'remote copied from %s' % f2)
1267 else:
1267 else:
1268 actions[f] = (ACTION_MERGE, (f2, f, f2, True, pa.node()),
1268 actions[f] = (ACTION_MERGE, (f2, f, f2, True, pa.node()),
1269 'remote moved from %s' % f2)
1269 'remote moved from %s' % f2)
1270 elif f not in ma:
1270 elif f not in ma:
1271 # local unknown, remote created: the logic is described by the
1271 # local unknown, remote created: the logic is described by the
1272 # following table:
1272 # following table:
1273 #
1273 #
1274 # force branchmerge different | action
1274 # force branchmerge different | action
1275 # n * * | create
1275 # n * * | create
1276 # y n * | create
1276 # y n * | create
1277 # y y n | create
1277 # y y n | create
1278 # y y y | merge
1278 # y y y | merge
1279 #
1279 #
1280 # Checking whether the files are different is expensive, so we
1280 # Checking whether the files are different is expensive, so we
1281 # don't do that when we can avoid it.
1281 # don't do that when we can avoid it.
1282 if not force:
1282 if not force:
1283 actions[f] = (ACTION_CREATED, (fl2,), 'remote created')
1283 actions[f] = (ACTION_CREATED, (fl2,), 'remote created')
1284 elif not branchmerge:
1284 elif not branchmerge:
1285 actions[f] = (ACTION_CREATED, (fl2,), 'remote created')
1285 actions[f] = (ACTION_CREATED, (fl2,), 'remote created')
1286 else:
1286 else:
1287 actions[f] = (ACTION_CREATED_MERGE, (fl2, pa.node()),
1287 actions[f] = (ACTION_CREATED_MERGE, (fl2, pa.node()),
1288 'remote created, get or merge')
1288 'remote created, get or merge')
1289 elif n2 != ma[f]:
1289 elif n2 != ma[f]:
1290 df = None
1290 df = None
1291 for d in dirmove:
1291 for d in dirmove:
1292 if f.startswith(d):
1292 if f.startswith(d):
1293 # new file added in a directory that was moved
1293 # new file added in a directory that was moved
1294 df = dirmove[d] + f[len(d):]
1294 df = dirmove[d] + f[len(d):]
1295 break
1295 break
1296 if df is not None and df in m1:
1296 if df is not None and df in m1:
1297 actions[df] = (ACTION_MERGE, (df, f, f, False, pa.node()),
1297 actions[df] = (ACTION_MERGE, (df, f, f, False, pa.node()),
1298 'local directory rename - respect move '
1298 'local directory rename - respect move '
1299 'from %s' % f)
1299 'from %s' % f)
1300 elif acceptremote:
1300 elif acceptremote:
1301 actions[f] = (ACTION_CREATED, (fl2,), 'remote recreating')
1301 actions[f] = (ACTION_CREATED, (fl2,), 'remote recreating')
1302 else:
1302 else:
1303 actions[f] = (ACTION_DELETED_CHANGED,
1303 actions[f] = (ACTION_DELETED_CHANGED,
1304 (None, f, f, False, pa.node()),
1304 (None, f, f, False, pa.node()),
1305 'prompt deleted/changed')
1305 'prompt deleted/changed')
1306
1306
1307 if repo.ui.configbool('experimental', 'merge.checkpathconflicts'):
1307 if repo.ui.configbool('experimental', 'merge.checkpathconflicts'):
1308 # If we are merging, look for path conflicts.
1308 # If we are merging, look for path conflicts.
1309 checkpathconflicts(repo, wctx, p2, actions)
1309 checkpathconflicts(repo, wctx, p2, actions)
1310
1310
1311 narrowmatch = repo.narrowmatch()
1311 narrowmatch = repo.narrowmatch()
1312 if not narrowmatch.always():
1312 if not narrowmatch.always():
1313 # Updates "actions" in place
1313 # Updates "actions" in place
1314 _filternarrowactions(narrowmatch, branchmerge, actions)
1314 _filternarrowactions(narrowmatch, branchmerge, actions)
1315
1315
1316 return actions, diverge, renamedelete
1316 return actions, diverge, renamedelete
1317
1317
1318 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
1318 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
1319 """Resolves false conflicts where the nodeid changed but the content
1319 """Resolves false conflicts where the nodeid changed but the content
1320 remained the same."""
1320 remained the same."""
1321 # We force a copy of actions.items() because we're going to mutate
1321 # We force a copy of actions.items() because we're going to mutate
1322 # actions as we resolve trivial conflicts.
1322 # actions as we resolve trivial conflicts.
1323 for f, (m, args, msg) in list(actions.items()):
1323 for f, (m, args, msg) in list(actions.items()):
1324 if (m == ACTION_CHANGED_DELETED and f in ancestor
1324 if (m == ACTION_CHANGED_DELETED and f in ancestor
1325 and not wctx[f].cmp(ancestor[f])):
1325 and not wctx[f].cmp(ancestor[f])):
1326 # local did change but ended up with same content
1326 # local did change but ended up with same content
1327 actions[f] = ACTION_REMOVE, None, 'prompt same'
1327 actions[f] = ACTION_REMOVE, None, 'prompt same'
1328 elif (m == ACTION_DELETED_CHANGED and f in ancestor
1328 elif (m == ACTION_DELETED_CHANGED and f in ancestor
1329 and not mctx[f].cmp(ancestor[f])):
1329 and not mctx[f].cmp(ancestor[f])):
1330 # remote did change but ended up with same content
1330 # remote did change but ended up with same content
1331 del actions[f] # don't get = keep local deleted
1331 del actions[f] # don't get = keep local deleted
1332
1332
1333 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
1333 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
1334 acceptremote, followcopies, matcher=None,
1334 acceptremote, followcopies, matcher=None,
1335 mergeforce=False):
1335 mergeforce=False):
1336 """Calculate the actions needed to merge mctx into wctx using ancestors"""
1336 """Calculate the actions needed to merge mctx into wctx using ancestors"""
1337 # Avoid cycle.
1337 # Avoid cycle.
1338 from . import sparse
1338 from . import sparse
1339
1339
1340 if len(ancestors) == 1: # default
1340 if len(ancestors) == 1: # default
1341 actions, diverge, renamedelete = manifestmerge(
1341 actions, diverge, renamedelete = manifestmerge(
1342 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
1342 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
1343 acceptremote, followcopies)
1343 acceptremote, followcopies)
1344 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1344 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1345
1345
1346 else: # only when merge.preferancestor=* - the default
1346 else: # only when merge.preferancestor=* - the default
1347 repo.ui.note(
1347 repo.ui.note(
1348 _("note: merging %s and %s using bids from ancestors %s\n") %
1348 _("note: merging %s and %s using bids from ancestors %s\n") %
1349 (wctx, mctx, _(' and ').join(pycompat.bytestr(anc)
1349 (wctx, mctx, _(' and ').join(pycompat.bytestr(anc)
1350 for anc in ancestors)))
1350 for anc in ancestors)))
1351
1351
1352 # Call for bids
1352 # Call for bids
1353 fbids = {} # mapping filename to bids (action method to list af actions)
1353 fbids = {} # mapping filename to bids (action method to list af actions)
1354 diverge, renamedelete = None, None
1354 diverge, renamedelete = None, None
1355 for ancestor in ancestors:
1355 for ancestor in ancestors:
1356 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
1356 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
1357 actions, diverge1, renamedelete1 = manifestmerge(
1357 actions, diverge1, renamedelete1 = manifestmerge(
1358 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
1358 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
1359 acceptremote, followcopies, forcefulldiff=True)
1359 acceptremote, followcopies, forcefulldiff=True)
1360 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1360 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
1361
1361
1362 # Track the shortest set of warning on the theory that bid
1362 # Track the shortest set of warning on the theory that bid
1363 # merge will correctly incorporate more information
1363 # merge will correctly incorporate more information
1364 if diverge is None or len(diverge1) < len(diverge):
1364 if diverge is None or len(diverge1) < len(diverge):
1365 diverge = diverge1
1365 diverge = diverge1
1366 if renamedelete is None or len(renamedelete) < len(renamedelete1):
1366 if renamedelete is None or len(renamedelete) < len(renamedelete1):
1367 renamedelete = renamedelete1
1367 renamedelete = renamedelete1
1368
1368
1369 for f, a in sorted(actions.iteritems()):
1369 for f, a in sorted(actions.iteritems()):
1370 m, args, msg = a
1370 m, args, msg = a
1371 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
1371 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
1372 if f in fbids:
1372 if f in fbids:
1373 d = fbids[f]
1373 d = fbids[f]
1374 if m in d:
1374 if m in d:
1375 d[m].append(a)
1375 d[m].append(a)
1376 else:
1376 else:
1377 d[m] = [a]
1377 d[m] = [a]
1378 else:
1378 else:
1379 fbids[f] = {m: [a]}
1379 fbids[f] = {m: [a]}
1380
1380
1381 # Pick the best bid for each file
1381 # Pick the best bid for each file
1382 repo.ui.note(_('\nauction for merging merge bids\n'))
1382 repo.ui.note(_('\nauction for merging merge bids\n'))
1383 actions = {}
1383 actions = {}
1384 for f, bids in sorted(fbids.items()):
1384 for f, bids in sorted(fbids.items()):
1385 # bids is a mapping from action method to list af actions
1385 # bids is a mapping from action method to list af actions
1386 # Consensus?
1386 # Consensus?
1387 if len(bids) == 1: # all bids are the same kind of method
1387 if len(bids) == 1: # all bids are the same kind of method
1388 m, l = list(bids.items())[0]
1388 m, l = list(bids.items())[0]
1389 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1389 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1390 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
1390 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
1391 actions[f] = l[0]
1391 actions[f] = l[0]
1392 continue
1392 continue
1393 # If keep is an option, just do it.
1393 # If keep is an option, just do it.
1394 if ACTION_KEEP in bids:
1394 if ACTION_KEEP in bids:
1395 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
1395 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
1396 actions[f] = bids[ACTION_KEEP][0]
1396 actions[f] = bids[ACTION_KEEP][0]
1397 continue
1397 continue
1398 # If there are gets and they all agree [how could they not?], do it.
1398 # If there are gets and they all agree [how could they not?], do it.
1399 if ACTION_GET in bids:
1399 if ACTION_GET in bids:
1400 ga0 = bids[ACTION_GET][0]
1400 ga0 = bids[ACTION_GET][0]
1401 if all(a == ga0 for a in bids[ACTION_GET][1:]):
1401 if all(a == ga0 for a in bids[ACTION_GET][1:]):
1402 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1402 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1403 actions[f] = ga0
1403 actions[f] = ga0
1404 continue
1404 continue
1405 # TODO: Consider other simple actions such as mode changes
1405 # TODO: Consider other simple actions such as mode changes
1406 # Handle inefficient democrazy.
1406 # Handle inefficient democrazy.
1407 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1407 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1408 for m, l in sorted(bids.items()):
1408 for m, l in sorted(bids.items()):
1409 for _f, args, msg in l:
1409 for _f, args, msg in l:
1410 repo.ui.note(' %s -> %s\n' % (msg, m))
1410 repo.ui.note(' %s -> %s\n' % (msg, m))
1411 # Pick random action. TODO: Instead, prompt user when resolving
1411 # Pick random action. TODO: Instead, prompt user when resolving
1412 m, l = list(bids.items())[0]
1412 m, l = list(bids.items())[0]
1413 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1413 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1414 (f, m))
1414 (f, m))
1415 actions[f] = l[0]
1415 actions[f] = l[0]
1416 continue
1416 continue
1417 repo.ui.note(_('end of auction\n\n'))
1417 repo.ui.note(_('end of auction\n\n'))
1418
1418
1419 if wctx.rev() is None:
1419 if wctx.rev() is None:
1420 fractions = _forgetremoved(wctx, mctx, branchmerge)
1420 fractions = _forgetremoved(wctx, mctx, branchmerge)
1421 actions.update(fractions)
1421 actions.update(fractions)
1422
1422
1423 prunedactions = sparse.filterupdatesactions(repo, wctx, mctx, branchmerge,
1423 prunedactions = sparse.filterupdatesactions(repo, wctx, mctx, branchmerge,
1424 actions)
1424 actions)
1425 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1425 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1426
1426
1427 return prunedactions, diverge, renamedelete
1427 return prunedactions, diverge, renamedelete
1428
1428
1429 def _getcwd():
1429 def _getcwd():
1430 try:
1430 try:
1431 return encoding.getcwd()
1431 return encoding.getcwd()
1432 except OSError as err:
1432 except OSError as err:
1433 if err.errno == errno.ENOENT:
1433 if err.errno == errno.ENOENT:
1434 return None
1434 return None
1435 raise
1435 raise
1436
1436
1437 def batchremove(repo, wctx, actions):
1437 def batchremove(repo, wctx, actions):
1438 """apply removes to the working directory
1438 """apply removes to the working directory
1439
1439
1440 yields tuples for progress updates
1440 yields tuples for progress updates
1441 """
1441 """
1442 verbose = repo.ui.verbose
1442 verbose = repo.ui.verbose
1443 cwd = _getcwd()
1443 cwd = _getcwd()
1444 i = 0
1444 i = 0
1445 for f, args, msg in actions:
1445 for f, args, msg in actions:
1446 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1446 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1447 if verbose:
1447 if verbose:
1448 repo.ui.note(_("removing %s\n") % f)
1448 repo.ui.note(_("removing %s\n") % f)
1449 wctx[f].audit()
1449 wctx[f].audit()
1450 try:
1450 try:
1451 wctx[f].remove(ignoremissing=True)
1451 wctx[f].remove(ignoremissing=True)
1452 except OSError as inst:
1452 except OSError as inst:
1453 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1453 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1454 (f, inst.strerror))
1454 (f, inst.strerror))
1455 if i == 100:
1455 if i == 100:
1456 yield i, f
1456 yield i, f
1457 i = 0
1457 i = 0
1458 i += 1
1458 i += 1
1459 if i > 0:
1459 if i > 0:
1460 yield i, f
1460 yield i, f
1461
1461
1462 if cwd and not _getcwd():
1462 if cwd and not _getcwd():
1463 # cwd was removed in the course of removing files; print a helpful
1463 # cwd was removed in the course of removing files; print a helpful
1464 # warning.
1464 # warning.
1465 repo.ui.warn(_("current directory was removed\n"
1465 repo.ui.warn(_("current directory was removed\n"
1466 "(consider changing to repo root: %s)\n") % repo.root)
1466 "(consider changing to repo root: %s)\n") % repo.root)
1467
1467
1468 def batchget(repo, mctx, wctx, wantfiledata, actions):
1468 def batchget(repo, mctx, wctx, wantfiledata, actions):
1469 """apply gets to the working directory
1469 """apply gets to the working directory
1470
1470
1471 mctx is the context to get from
1471 mctx is the context to get from
1472
1472
1473 Yields arbitrarily many (False, tuple) for progress updates, followed by
1473 Yields arbitrarily many (False, tuple) for progress updates, followed by
1474 exactly one (True, filedata). When wantfiledata is false, filedata is an
1474 exactly one (True, filedata). When wantfiledata is false, filedata is an
1475 empty dict. When wantfiledata is true, filedata[f] is a triple (mode, size,
1475 empty dict. When wantfiledata is true, filedata[f] is a triple (mode, size,
1476 mtime) of the file f written for each action.
1476 mtime) of the file f written for each action.
1477 """
1477 """
1478 filedata = {}
1478 filedata = {}
1479 verbose = repo.ui.verbose
1479 verbose = repo.ui.verbose
1480 fctx = mctx.filectx
1480 fctx = mctx.filectx
1481 ui = repo.ui
1481 ui = repo.ui
1482 i = 0
1482 i = 0
1483 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1483 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1484 for f, (flags, backup), msg in actions:
1484 for f, (flags, backup), msg in actions:
1485 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1485 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1486 if verbose:
1486 if verbose:
1487 repo.ui.note(_("getting %s\n") % f)
1487 repo.ui.note(_("getting %s\n") % f)
1488
1488
1489 if backup:
1489 if backup:
1490 # If a file or directory exists with the same name, back that
1490 # If a file or directory exists with the same name, back that
1491 # up. Otherwise, look to see if there is a file that conflicts
1491 # up. Otherwise, look to see if there is a file that conflicts
1492 # with a directory this file is in, and if so, back that up.
1492 # with a directory this file is in, and if so, back that up.
1493 conflicting = f
1493 conflicting = f
1494 if not repo.wvfs.lexists(f):
1494 if not repo.wvfs.lexists(f):
1495 for p in util.finddirs(f):
1495 for p in util.finddirs(f):
1496 if repo.wvfs.isfileorlink(p):
1496 if repo.wvfs.isfileorlink(p):
1497 conflicting = p
1497 conflicting = p
1498 break
1498 break
1499 if repo.wvfs.lexists(conflicting):
1499 if repo.wvfs.lexists(conflicting):
1500 orig = scmutil.backuppath(ui, repo, conflicting)
1500 orig = scmutil.backuppath(ui, repo, conflicting)
1501 util.rename(repo.wjoin(conflicting), orig)
1501 util.rename(repo.wjoin(conflicting), orig)
1502 wfctx = wctx[f]
1502 wfctx = wctx[f]
1503 wfctx.clearunknown()
1503 wfctx.clearunknown()
1504 atomictemp = ui.configbool("experimental", "update.atomic-file")
1504 atomictemp = ui.configbool("experimental", "update.atomic-file")
1505 size = wfctx.write(fctx(f).data(), flags,
1505 size = wfctx.write(fctx(f).data(), flags,
1506 backgroundclose=True,
1506 backgroundclose=True,
1507 atomictemp=atomictemp)
1507 atomictemp=atomictemp)
1508 if wantfiledata:
1508 if wantfiledata:
1509 s = wfctx.lstat()
1509 s = wfctx.lstat()
1510 mode = s.st_mode
1510 mode = s.st_mode
1511 mtime = s[stat.ST_MTIME]
1511 mtime = s[stat.ST_MTIME]
1512 filedata[f] = ((mode, size, mtime)) # for dirstate.normal
1512 filedata[f] = ((mode, size, mtime)) # for dirstate.normal
1513 if i == 100:
1513 if i == 100:
1514 yield False, (i, f)
1514 yield False, (i, f)
1515 i = 0
1515 i = 0
1516 i += 1
1516 i += 1
1517 if i > 0:
1517 if i > 0:
1518 yield False, (i, f)
1518 yield False, (i, f)
1519 yield True, filedata
1519 yield True, filedata
1520
1520
1521 def _prefetchfiles(repo, ctx, actions):
1521 def _prefetchfiles(repo, ctx, actions):
1522 """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict
1522 """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict
1523 of merge actions. ``ctx`` is the context being merged in."""
1523 of merge actions. ``ctx`` is the context being merged in."""
1524
1524
1525 # Skipping 'a', 'am', 'f', 'r', 'dm', 'e', 'k', 'p' and 'pr', because they
1525 # Skipping 'a', 'am', 'f', 'r', 'dm', 'e', 'k', 'p' and 'pr', because they
1526 # don't touch the context to be merged in. 'cd' is skipped, because
1526 # don't touch the context to be merged in. 'cd' is skipped, because
1527 # changed/deleted never resolves to something from the remote side.
1527 # changed/deleted never resolves to something from the remote side.
1528 oplist = [actions[a] for a in (ACTION_GET, ACTION_DELETED_CHANGED,
1528 oplist = [actions[a] for a in (ACTION_GET, ACTION_DELETED_CHANGED,
1529 ACTION_LOCAL_DIR_RENAME_GET, ACTION_MERGE)]
1529 ACTION_LOCAL_DIR_RENAME_GET, ACTION_MERGE)]
1530 prefetch = scmutil.prefetchfiles
1530 prefetch = scmutil.prefetchfiles
1531 matchfiles = scmutil.matchfiles
1531 matchfiles = scmutil.matchfiles
1532 prefetch(repo, [ctx.rev()],
1532 prefetch(repo, [ctx.rev()],
1533 matchfiles(repo,
1533 matchfiles(repo,
1534 [f for sublist in oplist for f, args, msg in sublist]))
1534 [f for sublist in oplist for f, args, msg in sublist]))
1535
1535
1536 @attr.s(frozen=True)
1536 @attr.s(frozen=True)
1537 class updateresult(object):
1537 class updateresult(object):
1538 updatedcount = attr.ib()
1538 updatedcount = attr.ib()
1539 mergedcount = attr.ib()
1539 mergedcount = attr.ib()
1540 removedcount = attr.ib()
1540 removedcount = attr.ib()
1541 unresolvedcount = attr.ib()
1541 unresolvedcount = attr.ib()
1542
1542
1543 def isempty(self):
1543 def isempty(self):
1544 return not (self.updatedcount or self.mergedcount
1544 return not (self.updatedcount or self.mergedcount
1545 or self.removedcount or self.unresolvedcount)
1545 or self.removedcount or self.unresolvedcount)
1546
1546
1547 def emptyactions():
1547 def emptyactions():
1548 """create an actions dict, to be populated and passed to applyupdates()"""
1548 """create an actions dict, to be populated and passed to applyupdates()"""
1549 return dict((m, [])
1549 return dict((m, [])
1550 for m in (
1550 for m in (
1551 ACTION_ADD,
1551 ACTION_ADD,
1552 ACTION_ADD_MODIFIED,
1552 ACTION_ADD_MODIFIED,
1553 ACTION_FORGET,
1553 ACTION_FORGET,
1554 ACTION_GET,
1554 ACTION_GET,
1555 ACTION_CHANGED_DELETED,
1555 ACTION_CHANGED_DELETED,
1556 ACTION_DELETED_CHANGED,
1556 ACTION_DELETED_CHANGED,
1557 ACTION_REMOVE,
1557 ACTION_REMOVE,
1558 ACTION_DIR_RENAME_MOVE_LOCAL,
1558 ACTION_DIR_RENAME_MOVE_LOCAL,
1559 ACTION_LOCAL_DIR_RENAME_GET,
1559 ACTION_LOCAL_DIR_RENAME_GET,
1560 ACTION_MERGE,
1560 ACTION_MERGE,
1561 ACTION_EXEC,
1561 ACTION_EXEC,
1562 ACTION_KEEP,
1562 ACTION_KEEP,
1563 ACTION_PATH_CONFLICT,
1563 ACTION_PATH_CONFLICT,
1564 ACTION_PATH_CONFLICT_RESOLVE))
1564 ACTION_PATH_CONFLICT_RESOLVE))
1565
1565
1566 def applyupdates(repo, actions, wctx, mctx, overwrite, wantfiledata,
1566 def applyupdates(repo, actions, wctx, mctx, overwrite, wantfiledata,
1567 labels=None):
1567 labels=None):
1568 """apply the merge action list to the working directory
1568 """apply the merge action list to the working directory
1569
1569
1570 wctx is the working copy context
1570 wctx is the working copy context
1571 mctx is the context to be merged into the working copy
1571 mctx is the context to be merged into the working copy
1572
1572
1573 Return a tuple of (counts, filedata), where counts is a tuple
1573 Return a tuple of (counts, filedata), where counts is a tuple
1574 (updated, merged, removed, unresolved) that describes how many
1574 (updated, merged, removed, unresolved) that describes how many
1575 files were affected by the update, and filedata is as described in
1575 files were affected by the update, and filedata is as described in
1576 batchget.
1576 batchget.
1577 """
1577 """
1578
1578
1579 _prefetchfiles(repo, mctx, actions)
1579 _prefetchfiles(repo, mctx, actions)
1580
1580
1581 updated, merged, removed = 0, 0, 0
1581 updated, merged, removed = 0, 0, 0
1582 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1582 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1583 moves = []
1583 moves = []
1584 for m, l in actions.items():
1584 for m, l in actions.items():
1585 l.sort()
1585 l.sort()
1586
1586
1587 # 'cd' and 'dc' actions are treated like other merge conflicts
1587 # 'cd' and 'dc' actions are treated like other merge conflicts
1588 mergeactions = sorted(actions[ACTION_CHANGED_DELETED])
1588 mergeactions = sorted(actions[ACTION_CHANGED_DELETED])
1589 mergeactions.extend(sorted(actions[ACTION_DELETED_CHANGED]))
1589 mergeactions.extend(sorted(actions[ACTION_DELETED_CHANGED]))
1590 mergeactions.extend(actions[ACTION_MERGE])
1590 mergeactions.extend(actions[ACTION_MERGE])
1591 for f, args, msg in mergeactions:
1591 for f, args, msg in mergeactions:
1592 f1, f2, fa, move, anc = args
1592 f1, f2, fa, move, anc = args
1593 if f == '.hgsubstate': # merged internally
1593 if f == '.hgsubstate': # merged internally
1594 continue
1594 continue
1595 if f1 is None:
1595 if f1 is None:
1596 fcl = filemerge.absentfilectx(wctx, fa)
1596 fcl = filemerge.absentfilectx(wctx, fa)
1597 else:
1597 else:
1598 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1598 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1599 fcl = wctx[f1]
1599 fcl = wctx[f1]
1600 if f2 is None:
1600 if f2 is None:
1601 fco = filemerge.absentfilectx(mctx, fa)
1601 fco = filemerge.absentfilectx(mctx, fa)
1602 else:
1602 else:
1603 fco = mctx[f2]
1603 fco = mctx[f2]
1604 actx = repo[anc]
1604 actx = repo[anc]
1605 if fa in actx:
1605 if fa in actx:
1606 fca = actx[fa]
1606 fca = actx[fa]
1607 else:
1607 else:
1608 # TODO: move to absentfilectx
1608 # TODO: move to absentfilectx
1609 fca = repo.filectx(f1, fileid=nullrev)
1609 fca = repo.filectx(f1, fileid=nullrev)
1610 ms.add(fcl, fco, fca, f)
1610 ms.add(fcl, fco, fca, f)
1611 if f1 != f and move:
1611 if f1 != f and move:
1612 moves.append(f1)
1612 moves.append(f1)
1613
1613
1614 # remove renamed files after safely stored
1614 # remove renamed files after safely stored
1615 for f in moves:
1615 for f in moves:
1616 if wctx[f].lexists():
1616 if wctx[f].lexists():
1617 repo.ui.debug("removing %s\n" % f)
1617 repo.ui.debug("removing %s\n" % f)
1618 wctx[f].audit()
1618 wctx[f].audit()
1619 wctx[f].remove()
1619 wctx[f].remove()
1620
1620
1621 numupdates = sum(len(l) for m, l in actions.items()
1621 numupdates = sum(len(l) for m, l in actions.items()
1622 if m != ACTION_KEEP)
1622 if m != ACTION_KEEP)
1623 progress = repo.ui.makeprogress(_('updating'), unit=_('files'),
1623 progress = repo.ui.makeprogress(_('updating'), unit=_('files'),
1624 total=numupdates)
1624 total=numupdates)
1625
1625
1626 if [a for a in actions[ACTION_REMOVE] if a[0] == '.hgsubstate']:
1626 if [a for a in actions[ACTION_REMOVE] if a[0] == '.hgsubstate']:
1627 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1627 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1628
1628
1629 # record path conflicts
1629 # record path conflicts
1630 for f, args, msg in actions[ACTION_PATH_CONFLICT]:
1630 for f, args, msg in actions[ACTION_PATH_CONFLICT]:
1631 f1, fo = args
1631 f1, fo = args
1632 s = repo.ui.status
1632 s = repo.ui.status
1633 s(_("%s: path conflict - a file or link has the same name as a "
1633 s(_("%s: path conflict - a file or link has the same name as a "
1634 "directory\n") % f)
1634 "directory\n") % f)
1635 if fo == 'l':
1635 if fo == 'l':
1636 s(_("the local file has been renamed to %s\n") % f1)
1636 s(_("the local file has been renamed to %s\n") % f1)
1637 else:
1637 else:
1638 s(_("the remote file has been renamed to %s\n") % f1)
1638 s(_("the remote file has been renamed to %s\n") % f1)
1639 s(_("resolve manually then use 'hg resolve --mark %s'\n") % f)
1639 s(_("resolve manually then use 'hg resolve --mark %s'\n") % f)
1640 ms.addpath(f, f1, fo)
1640 ms.addpath(f, f1, fo)
1641 progress.increment(item=f)
1641 progress.increment(item=f)
1642
1642
1643 # When merging in-memory, we can't support worker processes, so set the
1643 # When merging in-memory, we can't support worker processes, so set the
1644 # per-item cost at 0 in that case.
1644 # per-item cost at 0 in that case.
1645 cost = 0 if wctx.isinmemory() else 0.001
1645 cost = 0 if wctx.isinmemory() else 0.001
1646
1646
1647 # remove in parallel (must come before resolving path conflicts and getting)
1647 # remove in parallel (must come before resolving path conflicts and getting)
1648 prog = worker.worker(repo.ui, cost, batchremove, (repo, wctx),
1648 prog = worker.worker(repo.ui, cost, batchremove, (repo, wctx),
1649 actions[ACTION_REMOVE])
1649 actions[ACTION_REMOVE])
1650 for i, item in prog:
1650 for i, item in prog:
1651 progress.increment(step=i, item=item)
1651 progress.increment(step=i, item=item)
1652 removed = len(actions[ACTION_REMOVE])
1652 removed = len(actions[ACTION_REMOVE])
1653
1653
1654 # resolve path conflicts (must come before getting)
1654 # resolve path conflicts (must come before getting)
1655 for f, args, msg in actions[ACTION_PATH_CONFLICT_RESOLVE]:
1655 for f, args, msg in actions[ACTION_PATH_CONFLICT_RESOLVE]:
1656 repo.ui.debug(" %s: %s -> pr\n" % (f, msg))
1656 repo.ui.debug(" %s: %s -> pr\n" % (f, msg))
1657 f0, = args
1657 f0, = args
1658 if wctx[f0].lexists():
1658 if wctx[f0].lexists():
1659 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1659 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1660 wctx[f].audit()
1660 wctx[f].audit()
1661 wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags())
1661 wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags())
1662 wctx[f0].remove()
1662 wctx[f0].remove()
1663 progress.increment(item=f)
1663 progress.increment(item=f)
1664
1664
1665 # get in parallel.
1665 # get in parallel.
1666 threadsafe = repo.ui.configbool('experimental',
1666 threadsafe = repo.ui.configbool('experimental',
1667 'worker.wdir-get-thread-safe')
1667 'worker.wdir-get-thread-safe')
1668 prog = worker.worker(repo.ui, cost, batchget,
1668 prog = worker.worker(repo.ui, cost, batchget,
1669 (repo, mctx, wctx, wantfiledata),
1669 (repo, mctx, wctx, wantfiledata),
1670 actions[ACTION_GET],
1670 actions[ACTION_GET],
1671 threadsafe=threadsafe,
1671 threadsafe=threadsafe,
1672 hasretval=True)
1672 hasretval=True)
1673 getfiledata = {}
1673 getfiledata = {}
1674 for final, res in prog:
1674 for final, res in prog:
1675 if final:
1675 if final:
1676 getfiledata = res
1676 getfiledata = res
1677 else:
1677 else:
1678 i, item = res
1678 i, item = res
1679 progress.increment(step=i, item=item)
1679 progress.increment(step=i, item=item)
1680 updated = len(actions[ACTION_GET])
1680 updated = len(actions[ACTION_GET])
1681
1681
1682 if [a for a in actions[ACTION_GET] if a[0] == '.hgsubstate']:
1682 if [a for a in actions[ACTION_GET] if a[0] == '.hgsubstate']:
1683 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1683 subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1684
1684
1685 # forget (manifest only, just log it) (must come first)
1685 # forget (manifest only, just log it) (must come first)
1686 for f, args, msg in actions[ACTION_FORGET]:
1686 for f, args, msg in actions[ACTION_FORGET]:
1687 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1687 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1688 progress.increment(item=f)
1688 progress.increment(item=f)
1689
1689
1690 # re-add (manifest only, just log it)
1690 # re-add (manifest only, just log it)
1691 for f, args, msg in actions[ACTION_ADD]:
1691 for f, args, msg in actions[ACTION_ADD]:
1692 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1692 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1693 progress.increment(item=f)
1693 progress.increment(item=f)
1694
1694
1695 # re-add/mark as modified (manifest only, just log it)
1695 # re-add/mark as modified (manifest only, just log it)
1696 for f, args, msg in actions[ACTION_ADD_MODIFIED]:
1696 for f, args, msg in actions[ACTION_ADD_MODIFIED]:
1697 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1697 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1698 progress.increment(item=f)
1698 progress.increment(item=f)
1699
1699
1700 # keep (noop, just log it)
1700 # keep (noop, just log it)
1701 for f, args, msg in actions[ACTION_KEEP]:
1701 for f, args, msg in actions[ACTION_KEEP]:
1702 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1702 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1703 # no progress
1703 # no progress
1704
1704
1705 # directory rename, move local
1705 # directory rename, move local
1706 for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]:
1706 for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]:
1707 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1707 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1708 progress.increment(item=f)
1708 progress.increment(item=f)
1709 f0, flags = args
1709 f0, flags = args
1710 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1710 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1711 wctx[f].audit()
1711 wctx[f].audit()
1712 wctx[f].write(wctx.filectx(f0).data(), flags)
1712 wctx[f].write(wctx.filectx(f0).data(), flags)
1713 wctx[f0].remove()
1713 wctx[f0].remove()
1714 updated += 1
1714 updated += 1
1715
1715
1716 # local directory rename, get
1716 # local directory rename, get
1717 for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]:
1717 for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]:
1718 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1718 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1719 progress.increment(item=f)
1719 progress.increment(item=f)
1720 f0, flags = args
1720 f0, flags = args
1721 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1721 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1722 wctx[f].write(mctx.filectx(f0).data(), flags)
1722 wctx[f].write(mctx.filectx(f0).data(), flags)
1723 updated += 1
1723 updated += 1
1724
1724
1725 # exec
1725 # exec
1726 for f, args, msg in actions[ACTION_EXEC]:
1726 for f, args, msg in actions[ACTION_EXEC]:
1727 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1727 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1728 progress.increment(item=f)
1728 progress.increment(item=f)
1729 flags, = args
1729 flags, = args
1730 wctx[f].audit()
1730 wctx[f].audit()
1731 wctx[f].setflags('l' in flags, 'x' in flags)
1731 wctx[f].setflags('l' in flags, 'x' in flags)
1732 updated += 1
1732 updated += 1
1733
1733
1734 # the ordering is important here -- ms.mergedriver will raise if the merge
1734 # the ordering is important here -- ms.mergedriver will raise if the merge
1735 # driver has changed, and we want to be able to bypass it when overwrite is
1735 # driver has changed, and we want to be able to bypass it when overwrite is
1736 # True
1736 # True
1737 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1737 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1738
1738
1739 if usemergedriver:
1739 if usemergedriver:
1740 if wctx.isinmemory():
1740 if wctx.isinmemory():
1741 raise error.InMemoryMergeConflictsError("in-memory merge does not "
1741 raise error.InMemoryMergeConflictsError("in-memory merge does not "
1742 "support mergedriver")
1742 "support mergedriver")
1743 ms.commit()
1743 ms.commit()
1744 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1744 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1745 # the driver might leave some files unresolved
1745 # the driver might leave some files unresolved
1746 unresolvedf = set(ms.unresolved())
1746 unresolvedf = set(ms.unresolved())
1747 if not proceed:
1747 if not proceed:
1748 # XXX setting unresolved to at least 1 is a hack to make sure we
1748 # XXX setting unresolved to at least 1 is a hack to make sure we
1749 # error out
1749 # error out
1750 return updateresult(updated, merged, removed,
1750 return updateresult(updated, merged, removed,
1751 max(len(unresolvedf), 1))
1751 max(len(unresolvedf), 1))
1752 newactions = []
1752 newactions = []
1753 for f, args, msg in mergeactions:
1753 for f, args, msg in mergeactions:
1754 if f in unresolvedf:
1754 if f in unresolvedf:
1755 newactions.append((f, args, msg))
1755 newactions.append((f, args, msg))
1756 mergeactions = newactions
1756 mergeactions = newactions
1757
1757
1758 try:
1758 try:
1759 # premerge
1759 # premerge
1760 tocomplete = []
1760 tocomplete = []
1761 for f, args, msg in mergeactions:
1761 for f, args, msg in mergeactions:
1762 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1762 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1763 progress.increment(item=f)
1763 progress.increment(item=f)
1764 if f == '.hgsubstate': # subrepo states need updating
1764 if f == '.hgsubstate': # subrepo states need updating
1765 subrepoutil.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1765 subrepoutil.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1766 overwrite, labels)
1766 overwrite, labels)
1767 continue
1767 continue
1768 wctx[f].audit()
1768 wctx[f].audit()
1769 complete, r = ms.preresolve(f, wctx)
1769 complete, r = ms.preresolve(f, wctx)
1770 if not complete:
1770 if not complete:
1771 numupdates += 1
1771 numupdates += 1
1772 tocomplete.append((f, args, msg))
1772 tocomplete.append((f, args, msg))
1773
1773
1774 # merge
1774 # merge
1775 for f, args, msg in tocomplete:
1775 for f, args, msg in tocomplete:
1776 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1776 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1777 progress.increment(item=f, total=numupdates)
1777 progress.increment(item=f, total=numupdates)
1778 ms.resolve(f, wctx)
1778 ms.resolve(f, wctx)
1779
1779
1780 finally:
1780 finally:
1781 ms.commit()
1781 ms.commit()
1782
1782
1783 unresolved = ms.unresolvedcount()
1783 unresolved = ms.unresolvedcount()
1784
1784
1785 if (usemergedriver and not unresolved
1785 if (usemergedriver and not unresolved
1786 and ms.mdstate() != MERGE_DRIVER_STATE_SUCCESS):
1786 and ms.mdstate() != MERGE_DRIVER_STATE_SUCCESS):
1787 if not driverconclude(repo, ms, wctx, labels=labels):
1787 if not driverconclude(repo, ms, wctx, labels=labels):
1788 # XXX setting unresolved to at least 1 is a hack to make sure we
1788 # XXX setting unresolved to at least 1 is a hack to make sure we
1789 # error out
1789 # error out
1790 unresolved = max(unresolved, 1)
1790 unresolved = max(unresolved, 1)
1791
1791
1792 ms.commit()
1792 ms.commit()
1793
1793
1794 msupdated, msmerged, msremoved = ms.counts()
1794 msupdated, msmerged, msremoved = ms.counts()
1795 updated += msupdated
1795 updated += msupdated
1796 merged += msmerged
1796 merged += msmerged
1797 removed += msremoved
1797 removed += msremoved
1798
1798
1799 extraactions = ms.actions()
1799 extraactions = ms.actions()
1800 if extraactions:
1800 if extraactions:
1801 mfiles = set(a[0] for a in actions[ACTION_MERGE])
1801 mfiles = set(a[0] for a in actions[ACTION_MERGE])
1802 for k, acts in extraactions.iteritems():
1802 for k, acts in extraactions.iteritems():
1803 actions[k].extend(acts)
1803 actions[k].extend(acts)
1804 if k == ACTION_GET and wantfiledata:
1804 if k == ACTION_GET and wantfiledata:
1805 # no filedata until mergestate is updated to provide it
1805 # no filedata until mergestate is updated to provide it
1806 for a in acts:
1806 for a in acts:
1807 getfiledata[a[0]] = None
1807 getfiledata[a[0]] = None
1808 # Remove these files from actions[ACTION_MERGE] as well. This is
1808 # Remove these files from actions[ACTION_MERGE] as well. This is
1809 # important because in recordupdates, files in actions[ACTION_MERGE]
1809 # important because in recordupdates, files in actions[ACTION_MERGE]
1810 # are processed after files in other actions, and the merge driver
1810 # are processed after files in other actions, and the merge driver
1811 # might add files to those actions via extraactions above. This can
1811 # might add files to those actions via extraactions above. This can
1812 # lead to a file being recorded twice, with poor results. This is
1812 # lead to a file being recorded twice, with poor results. This is
1813 # especially problematic for actions[ACTION_REMOVE] (currently only
1813 # especially problematic for actions[ACTION_REMOVE] (currently only
1814 # possible with the merge driver in the initial merge process;
1814 # possible with the merge driver in the initial merge process;
1815 # interrupted merges don't go through this flow).
1815 # interrupted merges don't go through this flow).
1816 #
1816 #
1817 # The real fix here is to have indexes by both file and action so
1817 # The real fix here is to have indexes by both file and action so
1818 # that when the action for a file is changed it is automatically
1818 # that when the action for a file is changed it is automatically
1819 # reflected in the other action lists. But that involves a more
1819 # reflected in the other action lists. But that involves a more
1820 # complex data structure, so this will do for now.
1820 # complex data structure, so this will do for now.
1821 #
1821 #
1822 # We don't need to do the same operation for 'dc' and 'cd' because
1822 # We don't need to do the same operation for 'dc' and 'cd' because
1823 # those lists aren't consulted again.
1823 # those lists aren't consulted again.
1824 mfiles.difference_update(a[0] for a in acts)
1824 mfiles.difference_update(a[0] for a in acts)
1825
1825
1826 actions[ACTION_MERGE] = [a for a in actions[ACTION_MERGE]
1826 actions[ACTION_MERGE] = [a for a in actions[ACTION_MERGE]
1827 if a[0] in mfiles]
1827 if a[0] in mfiles]
1828
1828
1829 progress.complete()
1829 progress.complete()
1830 assert len(getfiledata) == (len(actions[ACTION_GET]) if wantfiledata else 0)
1830 assert len(getfiledata) == (len(actions[ACTION_GET]) if wantfiledata else 0)
1831 return updateresult(updated, merged, removed, unresolved), getfiledata
1831 return updateresult(updated, merged, removed, unresolved), getfiledata
1832
1832
1833 def recordupdates(repo, actions, branchmerge, getfiledata):
1833 def recordupdates(repo, actions, branchmerge, getfiledata):
1834 "record merge actions to the dirstate"
1834 "record merge actions to the dirstate"
1835 # remove (must come first)
1835 # remove (must come first)
1836 for f, args, msg in actions.get(ACTION_REMOVE, []):
1836 for f, args, msg in actions.get(ACTION_REMOVE, []):
1837 if branchmerge:
1837 if branchmerge:
1838 repo.dirstate.remove(f)
1838 repo.dirstate.remove(f)
1839 else:
1839 else:
1840 repo.dirstate.drop(f)
1840 repo.dirstate.drop(f)
1841
1841
1842 # forget (must come first)
1842 # forget (must come first)
1843 for f, args, msg in actions.get(ACTION_FORGET, []):
1843 for f, args, msg in actions.get(ACTION_FORGET, []):
1844 repo.dirstate.drop(f)
1844 repo.dirstate.drop(f)
1845
1845
1846 # resolve path conflicts
1846 # resolve path conflicts
1847 for f, args, msg in actions.get(ACTION_PATH_CONFLICT_RESOLVE, []):
1847 for f, args, msg in actions.get(ACTION_PATH_CONFLICT_RESOLVE, []):
1848 f0, = args
1848 f0, = args
1849 origf0 = repo.dirstate.copied(f0) or f0
1849 origf0 = repo.dirstate.copied(f0) or f0
1850 repo.dirstate.add(f)
1850 repo.dirstate.add(f)
1851 repo.dirstate.copy(origf0, f)
1851 repo.dirstate.copy(origf0, f)
1852 if f0 == origf0:
1852 if f0 == origf0:
1853 repo.dirstate.remove(f0)
1853 repo.dirstate.remove(f0)
1854 else:
1854 else:
1855 repo.dirstate.drop(f0)
1855 repo.dirstate.drop(f0)
1856
1856
1857 # re-add
1857 # re-add
1858 for f, args, msg in actions.get(ACTION_ADD, []):
1858 for f, args, msg in actions.get(ACTION_ADD, []):
1859 repo.dirstate.add(f)
1859 repo.dirstate.add(f)
1860
1860
1861 # re-add/mark as modified
1861 # re-add/mark as modified
1862 for f, args, msg in actions.get(ACTION_ADD_MODIFIED, []):
1862 for f, args, msg in actions.get(ACTION_ADD_MODIFIED, []):
1863 if branchmerge:
1863 if branchmerge:
1864 repo.dirstate.normallookup(f)
1864 repo.dirstate.normallookup(f)
1865 else:
1865 else:
1866 repo.dirstate.add(f)
1866 repo.dirstate.add(f)
1867
1867
1868 # exec change
1868 # exec change
1869 for f, args, msg in actions.get(ACTION_EXEC, []):
1869 for f, args, msg in actions.get(ACTION_EXEC, []):
1870 repo.dirstate.normallookup(f)
1870 repo.dirstate.normallookup(f)
1871
1871
1872 # keep
1872 # keep
1873 for f, args, msg in actions.get(ACTION_KEEP, []):
1873 for f, args, msg in actions.get(ACTION_KEEP, []):
1874 pass
1874 pass
1875
1875
1876 # get
1876 # get
1877 for f, args, msg in actions.get(ACTION_GET, []):
1877 for f, args, msg in actions.get(ACTION_GET, []):
1878 if branchmerge:
1878 if branchmerge:
1879 repo.dirstate.otherparent(f)
1879 repo.dirstate.otherparent(f)
1880 else:
1880 else:
1881 parentfiledata = getfiledata[f] if getfiledata else None
1881 parentfiledata = getfiledata[f] if getfiledata else None
1882 repo.dirstate.normal(f, parentfiledata=parentfiledata)
1882 repo.dirstate.normal(f, parentfiledata=parentfiledata)
1883
1883
1884 # merge
1884 # merge
1885 for f, args, msg in actions.get(ACTION_MERGE, []):
1885 for f, args, msg in actions.get(ACTION_MERGE, []):
1886 f1, f2, fa, move, anc = args
1886 f1, f2, fa, move, anc = args
1887 if branchmerge:
1887 if branchmerge:
1888 # We've done a branch merge, mark this file as merged
1888 # We've done a branch merge, mark this file as merged
1889 # so that we properly record the merger later
1889 # so that we properly record the merger later
1890 repo.dirstate.merge(f)
1890 repo.dirstate.merge(f)
1891 if f1 != f2: # copy/rename
1891 if f1 != f2: # copy/rename
1892 if move:
1892 if move:
1893 repo.dirstate.remove(f1)
1893 repo.dirstate.remove(f1)
1894 if f1 != f:
1894 if f1 != f:
1895 repo.dirstate.copy(f1, f)
1895 repo.dirstate.copy(f1, f)
1896 else:
1896 else:
1897 repo.dirstate.copy(f2, f)
1897 repo.dirstate.copy(f2, f)
1898 else:
1898 else:
1899 # We've update-merged a locally modified file, so
1899 # We've update-merged a locally modified file, so
1900 # we set the dirstate to emulate a normal checkout
1900 # we set the dirstate to emulate a normal checkout
1901 # of that file some time in the past. Thus our
1901 # of that file some time in the past. Thus our
1902 # merge will appear as a normal local file
1902 # merge will appear as a normal local file
1903 # modification.
1903 # modification.
1904 if f2 == f: # file not locally copied/moved
1904 if f2 == f: # file not locally copied/moved
1905 repo.dirstate.normallookup(f)
1905 repo.dirstate.normallookup(f)
1906 if move:
1906 if move:
1907 repo.dirstate.drop(f1)
1907 repo.dirstate.drop(f1)
1908
1908
1909 # directory rename, move local
1909 # directory rename, move local
1910 for f, args, msg in actions.get(ACTION_DIR_RENAME_MOVE_LOCAL, []):
1910 for f, args, msg in actions.get(ACTION_DIR_RENAME_MOVE_LOCAL, []):
1911 f0, flag = args
1911 f0, flag = args
1912 if branchmerge:
1912 if branchmerge:
1913 repo.dirstate.add(f)
1913 repo.dirstate.add(f)
1914 repo.dirstate.remove(f0)
1914 repo.dirstate.remove(f0)
1915 repo.dirstate.copy(f0, f)
1915 repo.dirstate.copy(f0, f)
1916 else:
1916 else:
1917 repo.dirstate.normal(f)
1917 repo.dirstate.normal(f)
1918 repo.dirstate.drop(f0)
1918 repo.dirstate.drop(f0)
1919
1919
1920 # directory rename, get
1920 # directory rename, get
1921 for f, args, msg in actions.get(ACTION_LOCAL_DIR_RENAME_GET, []):
1921 for f, args, msg in actions.get(ACTION_LOCAL_DIR_RENAME_GET, []):
1922 f0, flag = args
1922 f0, flag = args
1923 if branchmerge:
1923 if branchmerge:
1924 repo.dirstate.add(f)
1924 repo.dirstate.add(f)
1925 repo.dirstate.copy(f0, f)
1925 repo.dirstate.copy(f0, f)
1926 else:
1926 else:
1927 repo.dirstate.normal(f)
1927 repo.dirstate.normal(f)
1928
1928
1929 def update(repo, node, branchmerge, force, ancestor=None,
1929 def update(repo, node, branchmerge, force, ancestor=None,
1930 mergeancestor=False, labels=None, matcher=None, mergeforce=False,
1930 mergeancestor=False, labels=None, matcher=None, mergeforce=False,
1931 updatecheck=None, wc=None):
1931 updatecheck=None, wc=None):
1932 """
1932 """
1933 Perform a merge between the working directory and the given node
1933 Perform a merge between the working directory and the given node
1934
1934
1935 node = the node to update to
1935 node = the node to update to
1936 branchmerge = whether to merge between branches
1936 branchmerge = whether to merge between branches
1937 force = whether to force branch merging or file overwriting
1937 force = whether to force branch merging or file overwriting
1938 matcher = a matcher to filter file lists (dirstate not updated)
1938 matcher = a matcher to filter file lists (dirstate not updated)
1939 mergeancestor = whether it is merging with an ancestor. If true,
1939 mergeancestor = whether it is merging with an ancestor. If true,
1940 we should accept the incoming changes for any prompts that occur.
1940 we should accept the incoming changes for any prompts that occur.
1941 If false, merging with an ancestor (fast-forward) is only allowed
1941 If false, merging with an ancestor (fast-forward) is only allowed
1942 between different named branches. This flag is used by rebase extension
1942 between different named branches. This flag is used by rebase extension
1943 as a temporary fix and should be avoided in general.
1943 as a temporary fix and should be avoided in general.
1944 labels = labels to use for base, local and other
1944 labels = labels to use for base, local and other
1945 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1945 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1946 this is True, then 'force' should be True as well.
1946 this is True, then 'force' should be True as well.
1947
1947
1948 The table below shows all the behaviors of the update command given the
1948 The table below shows all the behaviors of the update command given the
1949 -c/--check and -C/--clean or no options, whether the working directory is
1949 -c/--check and -C/--clean or no options, whether the working directory is
1950 dirty, whether a revision is specified, and the relationship of the parent
1950 dirty, whether a revision is specified, and the relationship of the parent
1951 rev to the target rev (linear or not). Match from top first. The -n
1951 rev to the target rev (linear or not). Match from top first. The -n
1952 option doesn't exist on the command line, but represents the
1952 option doesn't exist on the command line, but represents the
1953 experimental.updatecheck=noconflict option.
1953 experimental.updatecheck=noconflict option.
1954
1954
1955 This logic is tested by test-update-branches.t.
1955 This logic is tested by test-update-branches.t.
1956
1956
1957 -c -C -n -m dirty rev linear | result
1957 -c -C -n -m dirty rev linear | result
1958 y y * * * * * | (1)
1958 y y * * * * * | (1)
1959 y * y * * * * | (1)
1959 y * y * * * * | (1)
1960 y * * y * * * | (1)
1960 y * * y * * * | (1)
1961 * y y * * * * | (1)
1961 * y y * * * * | (1)
1962 * y * y * * * | (1)
1962 * y * y * * * | (1)
1963 * * y y * * * | (1)
1963 * * y y * * * | (1)
1964 * * * * * n n | x
1964 * * * * * n n | x
1965 * * * * n * * | ok
1965 * * * * n * * | ok
1966 n n n n y * y | merge
1966 n n n n y * y | merge
1967 n n n n y y n | (2)
1967 n n n n y y n | (2)
1968 n n n y y * * | merge
1968 n n n y y * * | merge
1969 n n y n y * * | merge if no conflict
1969 n n y n y * * | merge if no conflict
1970 n y n n y * * | discard
1970 n y n n y * * | discard
1971 y n n n y * * | (3)
1971 y n n n y * * | (3)
1972
1972
1973 x = can't happen
1973 x = can't happen
1974 * = don't-care
1974 * = don't-care
1975 1 = incompatible options (checked in commands.py)
1975 1 = incompatible options (checked in commands.py)
1976 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1976 2 = abort: uncommitted changes (commit or update --clean to discard changes)
1977 3 = abort: uncommitted changes (checked in commands.py)
1977 3 = abort: uncommitted changes (checked in commands.py)
1978
1978
1979 The merge is performed inside ``wc``, a workingctx-like objects. It defaults
1979 The merge is performed inside ``wc``, a workingctx-like objects. It defaults
1980 to repo[None] if None is passed.
1980 to repo[None] if None is passed.
1981
1981
1982 Return the same tuple as applyupdates().
1982 Return the same tuple as applyupdates().
1983 """
1983 """
1984 # Avoid cycle.
1984 # Avoid cycle.
1985 from . import sparse
1985 from . import sparse
1986
1986
1987 # This function used to find the default destination if node was None, but
1987 # This function used to find the default destination if node was None, but
1988 # that's now in destutil.py.
1988 # that's now in destutil.py.
1989 assert node is not None
1989 assert node is not None
1990 if not branchmerge and not force:
1990 if not branchmerge and not force:
1991 # TODO: remove the default once all callers that pass branchmerge=False
1991 # TODO: remove the default once all callers that pass branchmerge=False
1992 # and force=False pass a value for updatecheck. We may want to allow
1992 # and force=False pass a value for updatecheck. We may want to allow
1993 # updatecheck='abort' to better suppport some of these callers.
1993 # updatecheck='abort' to better suppport some of these callers.
1994 if updatecheck is None:
1994 if updatecheck is None:
1995 updatecheck = 'linear'
1995 updatecheck = 'linear'
1996 assert updatecheck in ('none', 'linear', 'noconflict')
1996 assert updatecheck in ('none', 'linear', 'noconflict')
1997 # If we're doing a partial update, we need to skip updating
1997 # If we're doing a partial update, we need to skip updating
1998 # the dirstate, so make a note of any partial-ness to the
1998 # the dirstate, so make a note of any partial-ness to the
1999 # update here.
1999 # update here.
2000 if matcher is None or matcher.always():
2000 if matcher is None or matcher.always():
2001 partial = False
2001 partial = False
2002 else:
2002 else:
2003 partial = True
2003 partial = True
2004 with repo.wlock():
2004 with repo.wlock():
2005 if wc is None:
2005 if wc is None:
2006 wc = repo[None]
2006 wc = repo[None]
2007 pl = wc.parents()
2007 pl = wc.parents()
2008 p1 = pl[0]
2008 p1 = pl[0]
2009 p2 = repo[node]
2009 p2 = repo[node]
2010 if ancestor is not None:
2010 if ancestor is not None:
2011 pas = [repo[ancestor]]
2011 pas = [repo[ancestor]]
2012 else:
2012 else:
2013 if repo.ui.configlist('merge', 'preferancestor') == ['*']:
2013 if repo.ui.configlist('merge', 'preferancestor') == ['*']:
2014 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
2014 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
2015 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
2015 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
2016 else:
2016 else:
2017 pas = [p1.ancestor(p2, warn=branchmerge)]
2017 pas = [p1.ancestor(p2, warn=branchmerge)]
2018
2018
2019 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), bytes(p1), bytes(p2)
2019 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), bytes(p1), bytes(p2)
2020
2020
2021 overwrite = force and not branchmerge
2021 overwrite = force and not branchmerge
2022 ### check phase
2022 ### check phase
2023 if not overwrite:
2023 if not overwrite:
2024 if len(pl) > 1:
2024 if len(pl) > 1:
2025 raise error.Abort(_("outstanding uncommitted merge"))
2025 raise error.Abort(_("outstanding uncommitted merge"))
2026 ms = mergestate.read(repo)
2026 ms = mergestate.read(repo)
2027 if list(ms.unresolved()):
2027 if list(ms.unresolved()):
2028 raise error.Abort(_("outstanding merge conflicts"))
2028 raise error.Abort(_("outstanding merge conflicts"),
2029 hint=_("use 'hg resolve' to resolve"))
2029 if branchmerge:
2030 if branchmerge:
2030 if pas == [p2]:
2031 if pas == [p2]:
2031 raise error.Abort(_("merging with a working directory ancestor"
2032 raise error.Abort(_("merging with a working directory ancestor"
2032 " has no effect"))
2033 " has no effect"))
2033 elif pas == [p1]:
2034 elif pas == [p1]:
2034 if not mergeancestor and wc.branch() == p2.branch():
2035 if not mergeancestor and wc.branch() == p2.branch():
2035 raise error.Abort(_("nothing to merge"),
2036 raise error.Abort(_("nothing to merge"),
2036 hint=_("use 'hg update' "
2037 hint=_("use 'hg update' "
2037 "or check 'hg heads'"))
2038 "or check 'hg heads'"))
2038 if not force and (wc.files() or wc.deleted()):
2039 if not force and (wc.files() or wc.deleted()):
2039 raise error.Abort(_("uncommitted changes"),
2040 raise error.Abort(_("uncommitted changes"),
2040 hint=_("use 'hg status' to list changes"))
2041 hint=_("use 'hg status' to list changes"))
2041 if not wc.isinmemory():
2042 if not wc.isinmemory():
2042 for s in sorted(wc.substate):
2043 for s in sorted(wc.substate):
2043 wc.sub(s).bailifchanged()
2044 wc.sub(s).bailifchanged()
2044
2045
2045 elif not overwrite:
2046 elif not overwrite:
2046 if p1 == p2: # no-op update
2047 if p1 == p2: # no-op update
2047 # call the hooks and exit early
2048 # call the hooks and exit early
2048 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
2049 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
2049 repo.hook('update', parent1=xp2, parent2='', error=0)
2050 repo.hook('update', parent1=xp2, parent2='', error=0)
2050 return updateresult(0, 0, 0, 0)
2051 return updateresult(0, 0, 0, 0)
2051
2052
2052 if (updatecheck == 'linear' and
2053 if (updatecheck == 'linear' and
2053 pas not in ([p1], [p2])): # nonlinear
2054 pas not in ([p1], [p2])): # nonlinear
2054 dirty = wc.dirty(missing=True)
2055 dirty = wc.dirty(missing=True)
2055 if dirty:
2056 if dirty:
2056 # Branching is a bit strange to ensure we do the minimal
2057 # Branching is a bit strange to ensure we do the minimal
2057 # amount of call to obsutil.foreground.
2058 # amount of call to obsutil.foreground.
2058 foreground = obsutil.foreground(repo, [p1.node()])
2059 foreground = obsutil.foreground(repo, [p1.node()])
2059 # note: the <node> variable contains a random identifier
2060 # note: the <node> variable contains a random identifier
2060 if repo[node].node() in foreground:
2061 if repo[node].node() in foreground:
2061 pass # allow updating to successors
2062 pass # allow updating to successors
2062 else:
2063 else:
2063 msg = _("uncommitted changes")
2064 msg = _("uncommitted changes")
2064 hint = _("commit or update --clean to discard changes")
2065 hint = _("commit or update --clean to discard changes")
2065 raise error.UpdateAbort(msg, hint=hint)
2066 raise error.UpdateAbort(msg, hint=hint)
2066 else:
2067 else:
2067 # Allow jumping branches if clean and specific rev given
2068 # Allow jumping branches if clean and specific rev given
2068 pass
2069 pass
2069
2070
2070 if overwrite:
2071 if overwrite:
2071 pas = [wc]
2072 pas = [wc]
2072 elif not branchmerge:
2073 elif not branchmerge:
2073 pas = [p1]
2074 pas = [p1]
2074
2075
2075 # deprecated config: merge.followcopies
2076 # deprecated config: merge.followcopies
2076 followcopies = repo.ui.configbool('merge', 'followcopies')
2077 followcopies = repo.ui.configbool('merge', 'followcopies')
2077 if overwrite:
2078 if overwrite:
2078 followcopies = False
2079 followcopies = False
2079 elif not pas[0]:
2080 elif not pas[0]:
2080 followcopies = False
2081 followcopies = False
2081 if not branchmerge and not wc.dirty(missing=True):
2082 if not branchmerge and not wc.dirty(missing=True):
2082 followcopies = False
2083 followcopies = False
2083
2084
2084 ### calculate phase
2085 ### calculate phase
2085 actionbyfile, diverge, renamedelete = calculateupdates(
2086 actionbyfile, diverge, renamedelete = calculateupdates(
2086 repo, wc, p2, pas, branchmerge, force, mergeancestor,
2087 repo, wc, p2, pas, branchmerge, force, mergeancestor,
2087 followcopies, matcher=matcher, mergeforce=mergeforce)
2088 followcopies, matcher=matcher, mergeforce=mergeforce)
2088
2089
2089 if updatecheck == 'noconflict':
2090 if updatecheck == 'noconflict':
2090 for f, (m, args, msg) in actionbyfile.iteritems():
2091 for f, (m, args, msg) in actionbyfile.iteritems():
2091 if m not in (ACTION_GET, ACTION_KEEP, ACTION_EXEC,
2092 if m not in (ACTION_GET, ACTION_KEEP, ACTION_EXEC,
2092 ACTION_REMOVE, ACTION_PATH_CONFLICT_RESOLVE):
2093 ACTION_REMOVE, ACTION_PATH_CONFLICT_RESOLVE):
2093 msg = _("conflicting changes")
2094 msg = _("conflicting changes")
2094 hint = _("commit or update --clean to discard changes")
2095 hint = _("commit or update --clean to discard changes")
2095 raise error.Abort(msg, hint=hint)
2096 raise error.Abort(msg, hint=hint)
2096
2097
2097 # Prompt and create actions. Most of this is in the resolve phase
2098 # Prompt and create actions. Most of this is in the resolve phase
2098 # already, but we can't handle .hgsubstate in filemerge or
2099 # already, but we can't handle .hgsubstate in filemerge or
2099 # subrepoutil.submerge yet so we have to keep prompting for it.
2100 # subrepoutil.submerge yet so we have to keep prompting for it.
2100 if '.hgsubstate' in actionbyfile:
2101 if '.hgsubstate' in actionbyfile:
2101 f = '.hgsubstate'
2102 f = '.hgsubstate'
2102 m, args, msg = actionbyfile[f]
2103 m, args, msg = actionbyfile[f]
2103 prompts = filemerge.partextras(labels)
2104 prompts = filemerge.partextras(labels)
2104 prompts['f'] = f
2105 prompts['f'] = f
2105 if m == ACTION_CHANGED_DELETED:
2106 if m == ACTION_CHANGED_DELETED:
2106 if repo.ui.promptchoice(
2107 if repo.ui.promptchoice(
2107 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
2108 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
2108 "use (c)hanged version or (d)elete?"
2109 "use (c)hanged version or (d)elete?"
2109 "$$ &Changed $$ &Delete") % prompts, 0):
2110 "$$ &Changed $$ &Delete") % prompts, 0):
2110 actionbyfile[f] = (ACTION_REMOVE, None, 'prompt delete')
2111 actionbyfile[f] = (ACTION_REMOVE, None, 'prompt delete')
2111 elif f in p1:
2112 elif f in p1:
2112 actionbyfile[f] = (ACTION_ADD_MODIFIED, None, 'prompt keep')
2113 actionbyfile[f] = (ACTION_ADD_MODIFIED, None, 'prompt keep')
2113 else:
2114 else:
2114 actionbyfile[f] = (ACTION_ADD, None, 'prompt keep')
2115 actionbyfile[f] = (ACTION_ADD, None, 'prompt keep')
2115 elif m == ACTION_DELETED_CHANGED:
2116 elif m == ACTION_DELETED_CHANGED:
2116 f1, f2, fa, move, anc = args
2117 f1, f2, fa, move, anc = args
2117 flags = p2[f2].flags()
2118 flags = p2[f2].flags()
2118 if repo.ui.promptchoice(
2119 if repo.ui.promptchoice(
2119 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
2120 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
2120 "use (c)hanged version or leave (d)eleted?"
2121 "use (c)hanged version or leave (d)eleted?"
2121 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
2122 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
2122 actionbyfile[f] = (ACTION_GET, (flags, False),
2123 actionbyfile[f] = (ACTION_GET, (flags, False),
2123 'prompt recreating')
2124 'prompt recreating')
2124 else:
2125 else:
2125 del actionbyfile[f]
2126 del actionbyfile[f]
2126
2127
2127 # Convert to dictionary-of-lists format
2128 # Convert to dictionary-of-lists format
2128 actions = emptyactions()
2129 actions = emptyactions()
2129 for f, (m, args, msg) in actionbyfile.iteritems():
2130 for f, (m, args, msg) in actionbyfile.iteritems():
2130 if m not in actions:
2131 if m not in actions:
2131 actions[m] = []
2132 actions[m] = []
2132 actions[m].append((f, args, msg))
2133 actions[m].append((f, args, msg))
2133
2134
2134 if not util.fscasesensitive(repo.path):
2135 if not util.fscasesensitive(repo.path):
2135 # check collision between files only in p2 for clean update
2136 # check collision between files only in p2 for clean update
2136 if (not branchmerge and
2137 if (not branchmerge and
2137 (force or not wc.dirty(missing=True, branch=False))):
2138 (force or not wc.dirty(missing=True, branch=False))):
2138 _checkcollision(repo, p2.manifest(), None)
2139 _checkcollision(repo, p2.manifest(), None)
2139 else:
2140 else:
2140 _checkcollision(repo, wc.manifest(), actions)
2141 _checkcollision(repo, wc.manifest(), actions)
2141
2142
2142 # divergent renames
2143 # divergent renames
2143 for f, fl in sorted(diverge.iteritems()):
2144 for f, fl in sorted(diverge.iteritems()):
2144 repo.ui.warn(_("note: possible conflict - %s was renamed "
2145 repo.ui.warn(_("note: possible conflict - %s was renamed "
2145 "multiple times to:\n") % f)
2146 "multiple times to:\n") % f)
2146 for nf in sorted(fl):
2147 for nf in sorted(fl):
2147 repo.ui.warn(" %s\n" % nf)
2148 repo.ui.warn(" %s\n" % nf)
2148
2149
2149 # rename and delete
2150 # rename and delete
2150 for f, fl in sorted(renamedelete.iteritems()):
2151 for f, fl in sorted(renamedelete.iteritems()):
2151 repo.ui.warn(_("note: possible conflict - %s was deleted "
2152 repo.ui.warn(_("note: possible conflict - %s was deleted "
2152 "and renamed to:\n") % f)
2153 "and renamed to:\n") % f)
2153 for nf in sorted(fl):
2154 for nf in sorted(fl):
2154 repo.ui.warn(" %s\n" % nf)
2155 repo.ui.warn(" %s\n" % nf)
2155
2156
2156 ### apply phase
2157 ### apply phase
2157 if not branchmerge: # just jump to the new rev
2158 if not branchmerge: # just jump to the new rev
2158 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
2159 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
2159 if not partial and not wc.isinmemory():
2160 if not partial and not wc.isinmemory():
2160 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
2161 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
2161 # note that we're in the middle of an update
2162 # note that we're in the middle of an update
2162 repo.vfs.write('updatestate', p2.hex())
2163 repo.vfs.write('updatestate', p2.hex())
2163
2164
2164 # Advertise fsmonitor when its presence could be useful.
2165 # Advertise fsmonitor when its presence could be useful.
2165 #
2166 #
2166 # We only advertise when performing an update from an empty working
2167 # We only advertise when performing an update from an empty working
2167 # directory. This typically only occurs during initial clone.
2168 # directory. This typically only occurs during initial clone.
2168 #
2169 #
2169 # We give users a mechanism to disable the warning in case it is
2170 # We give users a mechanism to disable the warning in case it is
2170 # annoying.
2171 # annoying.
2171 #
2172 #
2172 # We only allow on Linux and MacOS because that's where fsmonitor is
2173 # We only allow on Linux and MacOS because that's where fsmonitor is
2173 # considered stable.
2174 # considered stable.
2174 fsmonitorwarning = repo.ui.configbool('fsmonitor', 'warn_when_unused')
2175 fsmonitorwarning = repo.ui.configbool('fsmonitor', 'warn_when_unused')
2175 fsmonitorthreshold = repo.ui.configint('fsmonitor',
2176 fsmonitorthreshold = repo.ui.configint('fsmonitor',
2176 'warn_update_file_count')
2177 'warn_update_file_count')
2177 try:
2178 try:
2178 # avoid cycle: extensions -> cmdutil -> merge
2179 # avoid cycle: extensions -> cmdutil -> merge
2179 from . import extensions
2180 from . import extensions
2180 extensions.find('fsmonitor')
2181 extensions.find('fsmonitor')
2181 fsmonitorenabled = repo.ui.config('fsmonitor', 'mode') != 'off'
2182 fsmonitorenabled = repo.ui.config('fsmonitor', 'mode') != 'off'
2182 # We intentionally don't look at whether fsmonitor has disabled
2183 # We intentionally don't look at whether fsmonitor has disabled
2183 # itself because a) fsmonitor may have already printed a warning
2184 # itself because a) fsmonitor may have already printed a warning
2184 # b) we only care about the config state here.
2185 # b) we only care about the config state here.
2185 except KeyError:
2186 except KeyError:
2186 fsmonitorenabled = False
2187 fsmonitorenabled = False
2187
2188
2188 if (fsmonitorwarning
2189 if (fsmonitorwarning
2189 and not fsmonitorenabled
2190 and not fsmonitorenabled
2190 and p1.node() == nullid
2191 and p1.node() == nullid
2191 and len(actions[ACTION_GET]) >= fsmonitorthreshold
2192 and len(actions[ACTION_GET]) >= fsmonitorthreshold
2192 and pycompat.sysplatform.startswith(('linux', 'darwin'))):
2193 and pycompat.sysplatform.startswith(('linux', 'darwin'))):
2193 repo.ui.warn(
2194 repo.ui.warn(
2194 _('(warning: large working directory being used without '
2195 _('(warning: large working directory being used without '
2195 'fsmonitor enabled; enable fsmonitor to improve performance; '
2196 'fsmonitor enabled; enable fsmonitor to improve performance; '
2196 'see "hg help -e fsmonitor")\n'))
2197 'see "hg help -e fsmonitor")\n'))
2197
2198
2198 updatedirstate = not partial and not wc.isinmemory()
2199 updatedirstate = not partial and not wc.isinmemory()
2199 wantfiledata = updatedirstate and not branchmerge
2200 wantfiledata = updatedirstate and not branchmerge
2200 stats, getfiledata = applyupdates(repo, actions, wc, p2, overwrite,
2201 stats, getfiledata = applyupdates(repo, actions, wc, p2, overwrite,
2201 wantfiledata, labels=labels)
2202 wantfiledata, labels=labels)
2202
2203
2203 if updatedirstate:
2204 if updatedirstate:
2204 with repo.dirstate.parentchange():
2205 with repo.dirstate.parentchange():
2205 repo.setparents(fp1, fp2)
2206 repo.setparents(fp1, fp2)
2206 recordupdates(repo, actions, branchmerge, getfiledata)
2207 recordupdates(repo, actions, branchmerge, getfiledata)
2207 # update completed, clear state
2208 # update completed, clear state
2208 util.unlink(repo.vfs.join('updatestate'))
2209 util.unlink(repo.vfs.join('updatestate'))
2209
2210
2210 if not branchmerge:
2211 if not branchmerge:
2211 repo.dirstate.setbranch(p2.branch())
2212 repo.dirstate.setbranch(p2.branch())
2212
2213
2213 # If we're updating to a location, clean up any stale temporary includes
2214 # If we're updating to a location, clean up any stale temporary includes
2214 # (ex: this happens during hg rebase --abort).
2215 # (ex: this happens during hg rebase --abort).
2215 if not branchmerge:
2216 if not branchmerge:
2216 sparse.prunetemporaryincludes(repo)
2217 sparse.prunetemporaryincludes(repo)
2217
2218
2218 if not partial:
2219 if not partial:
2219 repo.hook('update', parent1=xp1, parent2=xp2,
2220 repo.hook('update', parent1=xp1, parent2=xp2,
2220 error=stats.unresolvedcount)
2221 error=stats.unresolvedcount)
2221 return stats
2222 return stats
2222
2223
2223 def graft(repo, ctx, pctx, labels=None, keepparent=False,
2224 def graft(repo, ctx, pctx, labels=None, keepparent=False,
2224 keepconflictparent=False):
2225 keepconflictparent=False):
2225 """Do a graft-like merge.
2226 """Do a graft-like merge.
2226
2227
2227 This is a merge where the merge ancestor is chosen such that one
2228 This is a merge where the merge ancestor is chosen such that one
2228 or more changesets are grafted onto the current changeset. In
2229 or more changesets are grafted onto the current changeset. In
2229 addition to the merge, this fixes up the dirstate to include only
2230 addition to the merge, this fixes up the dirstate to include only
2230 a single parent (if keepparent is False) and tries to duplicate any
2231 a single parent (if keepparent is False) and tries to duplicate any
2231 renames/copies appropriately.
2232 renames/copies appropriately.
2232
2233
2233 ctx - changeset to rebase
2234 ctx - changeset to rebase
2234 pctx - merge base, usually ctx.p1()
2235 pctx - merge base, usually ctx.p1()
2235 labels - merge labels eg ['local', 'graft']
2236 labels - merge labels eg ['local', 'graft']
2236 keepparent - keep second parent if any
2237 keepparent - keep second parent if any
2237 keepconflictparent - if unresolved, keep parent used for the merge
2238 keepconflictparent - if unresolved, keep parent used for the merge
2238
2239
2239 """
2240 """
2240 # If we're grafting a descendant onto an ancestor, be sure to pass
2241 # If we're grafting a descendant onto an ancestor, be sure to pass
2241 # mergeancestor=True to update. This does two things: 1) allows the merge if
2242 # mergeancestor=True to update. This does two things: 1) allows the merge if
2242 # the destination is the same as the parent of the ctx (so we can use graft
2243 # the destination is the same as the parent of the ctx (so we can use graft
2243 # to copy commits), and 2) informs update that the incoming changes are
2244 # to copy commits), and 2) informs update that the incoming changes are
2244 # newer than the destination so it doesn't prompt about "remote changed foo
2245 # newer than the destination so it doesn't prompt about "remote changed foo
2245 # which local deleted".
2246 # which local deleted".
2246 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
2247 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
2247
2248
2248 stats = update(repo, ctx.node(), True, True, pctx.node(),
2249 stats = update(repo, ctx.node(), True, True, pctx.node(),
2249 mergeancestor=mergeancestor, labels=labels)
2250 mergeancestor=mergeancestor, labels=labels)
2250
2251
2251
2252
2252 if keepconflictparent and stats.unresolvedcount:
2253 if keepconflictparent and stats.unresolvedcount:
2253 pother = ctx.node()
2254 pother = ctx.node()
2254 else:
2255 else:
2255 pother = nullid
2256 pother = nullid
2256 parents = ctx.parents()
2257 parents = ctx.parents()
2257 if keepparent and len(parents) == 2 and pctx in parents:
2258 if keepparent and len(parents) == 2 and pctx in parents:
2258 parents.remove(pctx)
2259 parents.remove(pctx)
2259 pother = parents[0].node()
2260 pother = parents[0].node()
2260
2261
2261 with repo.dirstate.parentchange():
2262 with repo.dirstate.parentchange():
2262 repo.setparents(repo['.'].node(), pother)
2263 repo.setparents(repo['.'].node(), pother)
2263 repo.dirstate.write(repo.currenttransaction())
2264 repo.dirstate.write(repo.currenttransaction())
2264 # fix up dirstate for copies and renames
2265 # fix up dirstate for copies and renames
2265 copies.duplicatecopies(repo, repo[None], ctx.rev(), pctx.rev())
2266 copies.duplicatecopies(repo, repo[None], ctx.rev(), pctx.rev())
2266 return stats
2267 return stats
2267
2268
2268 def purge(repo, matcher, ignored=False, removeemptydirs=True,
2269 def purge(repo, matcher, ignored=False, removeemptydirs=True,
2269 removefiles=True, abortonerror=False, noop=False):
2270 removefiles=True, abortonerror=False, noop=False):
2270 """Purge the working directory of untracked files.
2271 """Purge the working directory of untracked files.
2271
2272
2272 ``matcher`` is a matcher configured to scan the working directory -
2273 ``matcher`` is a matcher configured to scan the working directory -
2273 potentially a subset.
2274 potentially a subset.
2274
2275
2275 ``ignored`` controls whether ignored files should also be purged.
2276 ``ignored`` controls whether ignored files should also be purged.
2276
2277
2277 ``removeemptydirs`` controls whether empty directories should be removed.
2278 ``removeemptydirs`` controls whether empty directories should be removed.
2278
2279
2279 ``removefiles`` controls whether files are removed.
2280 ``removefiles`` controls whether files are removed.
2280
2281
2281 ``abortonerror`` causes an exception to be raised if an error occurs
2282 ``abortonerror`` causes an exception to be raised if an error occurs
2282 deleting a file or directory.
2283 deleting a file or directory.
2283
2284
2284 ``noop`` controls whether to actually remove files. If not defined, actions
2285 ``noop`` controls whether to actually remove files. If not defined, actions
2285 will be taken.
2286 will be taken.
2286
2287
2287 Returns an iterable of relative paths in the working directory that were
2288 Returns an iterable of relative paths in the working directory that were
2288 or would be removed.
2289 or would be removed.
2289 """
2290 """
2290
2291
2291 def remove(removefn, path):
2292 def remove(removefn, path):
2292 try:
2293 try:
2293 removefn(path)
2294 removefn(path)
2294 except OSError:
2295 except OSError:
2295 m = _('%s cannot be removed') % path
2296 m = _('%s cannot be removed') % path
2296 if abortonerror:
2297 if abortonerror:
2297 raise error.Abort(m)
2298 raise error.Abort(m)
2298 else:
2299 else:
2299 repo.ui.warn(_('warning: %s\n') % m)
2300 repo.ui.warn(_('warning: %s\n') % m)
2300
2301
2301 # There's no API to copy a matcher. So mutate the passed matcher and
2302 # There's no API to copy a matcher. So mutate the passed matcher and
2302 # restore it when we're done.
2303 # restore it when we're done.
2303 oldexplicitdir = matcher.explicitdir
2304 oldexplicitdir = matcher.explicitdir
2304 oldtraversedir = matcher.traversedir
2305 oldtraversedir = matcher.traversedir
2305
2306
2306 res = []
2307 res = []
2307
2308
2308 try:
2309 try:
2309 if removeemptydirs:
2310 if removeemptydirs:
2310 directories = []
2311 directories = []
2311 matcher.explicitdir = matcher.traversedir = directories.append
2312 matcher.explicitdir = matcher.traversedir = directories.append
2312
2313
2313 status = repo.status(match=matcher, ignored=ignored, unknown=True)
2314 status = repo.status(match=matcher, ignored=ignored, unknown=True)
2314
2315
2315 if removefiles:
2316 if removefiles:
2316 for f in sorted(status.unknown + status.ignored):
2317 for f in sorted(status.unknown + status.ignored):
2317 if not noop:
2318 if not noop:
2318 repo.ui.note(_('removing file %s\n') % f)
2319 repo.ui.note(_('removing file %s\n') % f)
2319 remove(repo.wvfs.unlink, f)
2320 remove(repo.wvfs.unlink, f)
2320 res.append(f)
2321 res.append(f)
2321
2322
2322 if removeemptydirs:
2323 if removeemptydirs:
2323 for f in sorted(directories, reverse=True):
2324 for f in sorted(directories, reverse=True):
2324 if matcher(f) and not repo.wvfs.listdir(f):
2325 if matcher(f) and not repo.wvfs.listdir(f):
2325 if not noop:
2326 if not noop:
2326 repo.ui.note(_('removing directory %s\n') % f)
2327 repo.ui.note(_('removing directory %s\n') % f)
2327 remove(repo.wvfs.rmdir, f)
2328 remove(repo.wvfs.rmdir, f)
2328 res.append(f)
2329 res.append(f)
2329
2330
2330 return res
2331 return res
2331
2332
2332 finally:
2333 finally:
2333 matcher.explicitdir = oldexplicitdir
2334 matcher.explicitdir = oldexplicitdir
2334 matcher.traversedir = oldtraversedir
2335 matcher.traversedir = oldtraversedir
@@ -1,863 +1,864 b''
1 #require symlink execbit
1 #require symlink execbit
2 $ cat << EOF >> $HGRCPATH
2 $ cat << EOF >> $HGRCPATH
3 > [phases]
3 > [phases]
4 > publish=False
4 > publish=False
5 > [extensions]
5 > [extensions]
6 > amend=
6 > amend=
7 > rebase=
7 > rebase=
8 > debugdrawdag=$TESTDIR/drawdag.py
8 > debugdrawdag=$TESTDIR/drawdag.py
9 > strip=
9 > strip=
10 > [rebase]
10 > [rebase]
11 > experimental.inmemory=1
11 > experimental.inmemory=1
12 > [diff]
12 > [diff]
13 > git=1
13 > git=1
14 > [alias]
14 > [alias]
15 > tglog = log -G --template "{rev}: {node|short} '{desc}'\n"
15 > tglog = log -G --template "{rev}: {node|short} '{desc}'\n"
16 > EOF
16 > EOF
17
17
18 Rebase a simple DAG:
18 Rebase a simple DAG:
19 $ hg init repo1
19 $ hg init repo1
20 $ cd repo1
20 $ cd repo1
21 $ hg debugdrawdag <<'EOS'
21 $ hg debugdrawdag <<'EOS'
22 > c b
22 > c b
23 > |/
23 > |/
24 > d
24 > d
25 > |
25 > |
26 > a
26 > a
27 > EOS
27 > EOS
28 $ hg up -C a
28 $ hg up -C a
29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 $ hg tglog
30 $ hg tglog
31 o 3: 814f6bd05178 'c'
31 o 3: 814f6bd05178 'c'
32 |
32 |
33 | o 2: db0e82a16a62 'b'
33 | o 2: db0e82a16a62 'b'
34 |/
34 |/
35 o 1: 02952614a83d 'd'
35 o 1: 02952614a83d 'd'
36 |
36 |
37 @ 0: b173517d0057 'a'
37 @ 0: b173517d0057 'a'
38
38
39 $ hg cat -r 3 c
39 $ hg cat -r 3 c
40 c (no-eol)
40 c (no-eol)
41 $ hg cat -r 2 b
41 $ hg cat -r 2 b
42 b (no-eol)
42 b (no-eol)
43 $ hg rebase --debug -r b -d c | grep rebasing
43 $ hg rebase --debug -r b -d c | grep rebasing
44 rebasing in-memory
44 rebasing in-memory
45 rebasing 2:db0e82a16a62 "b" (b)
45 rebasing 2:db0e82a16a62 "b" (b)
46 $ hg tglog
46 $ hg tglog
47 o 3: ca58782ad1e4 'b'
47 o 3: ca58782ad1e4 'b'
48 |
48 |
49 o 2: 814f6bd05178 'c'
49 o 2: 814f6bd05178 'c'
50 |
50 |
51 o 1: 02952614a83d 'd'
51 o 1: 02952614a83d 'd'
52 |
52 |
53 @ 0: b173517d0057 'a'
53 @ 0: b173517d0057 'a'
54
54
55 $ hg cat -r 3 b
55 $ hg cat -r 3 b
56 b (no-eol)
56 b (no-eol)
57 $ hg cat -r 2 c
57 $ hg cat -r 2 c
58 c (no-eol)
58 c (no-eol)
59 $ cd ..
59 $ cd ..
60
60
61 Case 2:
61 Case 2:
62 $ hg init repo2
62 $ hg init repo2
63 $ cd repo2
63 $ cd repo2
64 $ hg debugdrawdag <<'EOS'
64 $ hg debugdrawdag <<'EOS'
65 > c b
65 > c b
66 > |/
66 > |/
67 > d
67 > d
68 > |
68 > |
69 > a
69 > a
70 > EOS
70 > EOS
71
71
72 Add a symlink and executable file:
72 Add a symlink and executable file:
73 $ hg up -C c
73 $ hg up -C c
74 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
74 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
75 $ ln -s somefile e
75 $ ln -s somefile e
76 $ echo f > f
76 $ echo f > f
77 $ chmod +x f
77 $ chmod +x f
78 $ hg add e f
78 $ hg add e f
79 $ hg amend -q
79 $ hg amend -q
80 $ hg up -Cq a
80 $ hg up -Cq a
81
81
82 Write files to the working copy, and ensure they're still there after the rebase
82 Write files to the working copy, and ensure they're still there after the rebase
83 $ echo "abc" > a
83 $ echo "abc" > a
84 $ ln -s def b
84 $ ln -s def b
85 $ echo "ghi" > c
85 $ echo "ghi" > c
86 $ echo "jkl" > d
86 $ echo "jkl" > d
87 $ echo "mno" > e
87 $ echo "mno" > e
88 $ hg tglog
88 $ hg tglog
89 o 3: f56b71190a8f 'c'
89 o 3: f56b71190a8f 'c'
90 |
90 |
91 | o 2: db0e82a16a62 'b'
91 | o 2: db0e82a16a62 'b'
92 |/
92 |/
93 o 1: 02952614a83d 'd'
93 o 1: 02952614a83d 'd'
94 |
94 |
95 @ 0: b173517d0057 'a'
95 @ 0: b173517d0057 'a'
96
96
97 $ hg cat -r 3 c
97 $ hg cat -r 3 c
98 c (no-eol)
98 c (no-eol)
99 $ hg cat -r 2 b
99 $ hg cat -r 2 b
100 b (no-eol)
100 b (no-eol)
101 $ hg cat -r 3 e
101 $ hg cat -r 3 e
102 somefile (no-eol)
102 somefile (no-eol)
103 $ hg rebase --debug -s b -d a | grep rebasing
103 $ hg rebase --debug -s b -d a | grep rebasing
104 rebasing in-memory
104 rebasing in-memory
105 rebasing 2:db0e82a16a62 "b" (b)
105 rebasing 2:db0e82a16a62 "b" (b)
106 $ hg tglog
106 $ hg tglog
107 o 3: fc055c3b4d33 'b'
107 o 3: fc055c3b4d33 'b'
108 |
108 |
109 | o 2: f56b71190a8f 'c'
109 | o 2: f56b71190a8f 'c'
110 | |
110 | |
111 | o 1: 02952614a83d 'd'
111 | o 1: 02952614a83d 'd'
112 |/
112 |/
113 @ 0: b173517d0057 'a'
113 @ 0: b173517d0057 'a'
114
114
115 $ hg cat -r 2 c
115 $ hg cat -r 2 c
116 c (no-eol)
116 c (no-eol)
117 $ hg cat -r 3 b
117 $ hg cat -r 3 b
118 b (no-eol)
118 b (no-eol)
119 $ hg rebase --debug -s 1 -d 3 | grep rebasing
119 $ hg rebase --debug -s 1 -d 3 | grep rebasing
120 rebasing in-memory
120 rebasing in-memory
121 rebasing 1:02952614a83d "d" (d)
121 rebasing 1:02952614a83d "d" (d)
122 rebasing 2:f56b71190a8f "c"
122 rebasing 2:f56b71190a8f "c"
123 $ hg tglog
123 $ hg tglog
124 o 3: 753feb6fd12a 'c'
124 o 3: 753feb6fd12a 'c'
125 |
125 |
126 o 2: 09c044d2cb43 'd'
126 o 2: 09c044d2cb43 'd'
127 |
127 |
128 o 1: fc055c3b4d33 'b'
128 o 1: fc055c3b4d33 'b'
129 |
129 |
130 @ 0: b173517d0057 'a'
130 @ 0: b173517d0057 'a'
131
131
132 Ensure working copy files are still there:
132 Ensure working copy files are still there:
133 $ cat a
133 $ cat a
134 abc
134 abc
135 $ readlink.py b
135 $ readlink.py b
136 b -> def
136 b -> def
137 $ cat e
137 $ cat e
138 mno
138 mno
139
139
140 Ensure symlink and executable files were rebased properly:
140 Ensure symlink and executable files were rebased properly:
141 $ hg up -Cq 3
141 $ hg up -Cq 3
142 $ readlink.py e
142 $ readlink.py e
143 e -> somefile
143 e -> somefile
144 $ ls -l f | cut -c -10
144 $ ls -l f | cut -c -10
145 -rwxr-xr-x
145 -rwxr-xr-x
146
146
147 Rebase the working copy parent
147 Rebase the working copy parent
148 $ hg up -C 3
148 $ hg up -C 3
149 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
149 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 $ hg rebase -r 3 -d 0 --debug | grep rebasing
150 $ hg rebase -r 3 -d 0 --debug | grep rebasing
151 rebasing in-memory
151 rebasing in-memory
152 rebasing 3:753feb6fd12a "c" (tip)
152 rebasing 3:753feb6fd12a "c" (tip)
153 $ hg tglog
153 $ hg tglog
154 @ 3: 844a7de3e617 'c'
154 @ 3: 844a7de3e617 'c'
155 |
155 |
156 | o 2: 09c044d2cb43 'd'
156 | o 2: 09c044d2cb43 'd'
157 | |
157 | |
158 | o 1: fc055c3b4d33 'b'
158 | o 1: fc055c3b4d33 'b'
159 |/
159 |/
160 o 0: b173517d0057 'a'
160 o 0: b173517d0057 'a'
161
161
162
162
163 Test reporting of path conflicts
163 Test reporting of path conflicts
164
164
165 $ hg rm a
165 $ hg rm a
166 $ mkdir a
166 $ mkdir a
167 $ touch a/a
167 $ touch a/a
168 $ hg ci -Am "a/a"
168 $ hg ci -Am "a/a"
169 adding a/a
169 adding a/a
170 $ hg tglog
170 $ hg tglog
171 @ 4: daf7dfc139cb 'a/a'
171 @ 4: daf7dfc139cb 'a/a'
172 |
172 |
173 o 3: 844a7de3e617 'c'
173 o 3: 844a7de3e617 'c'
174 |
174 |
175 | o 2: 09c044d2cb43 'd'
175 | o 2: 09c044d2cb43 'd'
176 | |
176 | |
177 | o 1: fc055c3b4d33 'b'
177 | o 1: fc055c3b4d33 'b'
178 |/
178 |/
179 o 0: b173517d0057 'a'
179 o 0: b173517d0057 'a'
180
180
181 $ hg rebase -r . -d 2
181 $ hg rebase -r . -d 2
182 rebasing 4:daf7dfc139cb "a/a" (tip)
182 rebasing 4:daf7dfc139cb "a/a" (tip)
183 saved backup bundle to $TESTTMP/repo2/.hg/strip-backup/daf7dfc139cb-fdbfcf4f-rebase.hg
183 saved backup bundle to $TESTTMP/repo2/.hg/strip-backup/daf7dfc139cb-fdbfcf4f-rebase.hg
184
184
185 $ hg tglog
185 $ hg tglog
186 @ 4: c6ad37a4f250 'a/a'
186 @ 4: c6ad37a4f250 'a/a'
187 |
187 |
188 | o 3: 844a7de3e617 'c'
188 | o 3: 844a7de3e617 'c'
189 | |
189 | |
190 o | 2: 09c044d2cb43 'd'
190 o | 2: 09c044d2cb43 'd'
191 | |
191 | |
192 o | 1: fc055c3b4d33 'b'
192 o | 1: fc055c3b4d33 'b'
193 |/
193 |/
194 o 0: b173517d0057 'a'
194 o 0: b173517d0057 'a'
195
195
196 $ echo foo > foo
196 $ echo foo > foo
197 $ hg ci -Aqm "added foo"
197 $ hg ci -Aqm "added foo"
198 $ hg up '.^'
198 $ hg up '.^'
199 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
199 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
200 $ echo bar > bar
200 $ echo bar > bar
201 $ hg ci -Aqm "added bar"
201 $ hg ci -Aqm "added bar"
202 $ hg rm a/a
202 $ hg rm a/a
203 $ echo a > a
203 $ echo a > a
204 $ hg ci -Aqm "added a back!"
204 $ hg ci -Aqm "added a back!"
205 $ hg tglog
205 $ hg tglog
206 @ 7: 855e9797387e 'added a back!'
206 @ 7: 855e9797387e 'added a back!'
207 |
207 |
208 o 6: d14530e5e3e6 'added bar'
208 o 6: d14530e5e3e6 'added bar'
209 |
209 |
210 | o 5: 9b94b9373deb 'added foo'
210 | o 5: 9b94b9373deb 'added foo'
211 |/
211 |/
212 o 4: c6ad37a4f250 'a/a'
212 o 4: c6ad37a4f250 'a/a'
213 |
213 |
214 | o 3: 844a7de3e617 'c'
214 | o 3: 844a7de3e617 'c'
215 | |
215 | |
216 o | 2: 09c044d2cb43 'd'
216 o | 2: 09c044d2cb43 'd'
217 | |
217 | |
218 o | 1: fc055c3b4d33 'b'
218 o | 1: fc055c3b4d33 'b'
219 |/
219 |/
220 o 0: b173517d0057 'a'
220 o 0: b173517d0057 'a'
221
221
222 $ hg rebase -r . -d 5
222 $ hg rebase -r . -d 5
223 rebasing 7:855e9797387e "added a back!" (tip)
223 rebasing 7:855e9797387e "added a back!" (tip)
224 saved backup bundle to $TESTTMP/repo2/.hg/strip-backup/855e9797387e-81ee4c5d-rebase.hg
224 saved backup bundle to $TESTTMP/repo2/.hg/strip-backup/855e9797387e-81ee4c5d-rebase.hg
225
225
226 $ hg tglog
226 $ hg tglog
227 @ 7: bb3f02be2688 'added a back!'
227 @ 7: bb3f02be2688 'added a back!'
228 |
228 |
229 | o 6: d14530e5e3e6 'added bar'
229 | o 6: d14530e5e3e6 'added bar'
230 | |
230 | |
231 o | 5: 9b94b9373deb 'added foo'
231 o | 5: 9b94b9373deb 'added foo'
232 |/
232 |/
233 o 4: c6ad37a4f250 'a/a'
233 o 4: c6ad37a4f250 'a/a'
234 |
234 |
235 | o 3: 844a7de3e617 'c'
235 | o 3: 844a7de3e617 'c'
236 | |
236 | |
237 o | 2: 09c044d2cb43 'd'
237 o | 2: 09c044d2cb43 'd'
238 | |
238 | |
239 o | 1: fc055c3b4d33 'b'
239 o | 1: fc055c3b4d33 'b'
240 |/
240 |/
241 o 0: b173517d0057 'a'
241 o 0: b173517d0057 'a'
242
242
243 $ mkdir -p c/subdir
243 $ mkdir -p c/subdir
244 $ echo c > c/subdir/file.txt
244 $ echo c > c/subdir/file.txt
245 $ hg add c/subdir/file.txt
245 $ hg add c/subdir/file.txt
246 $ hg ci -m 'c/subdir/file.txt'
246 $ hg ci -m 'c/subdir/file.txt'
247 $ hg rebase -r . -d 3 -n
247 $ hg rebase -r . -d 3 -n
248 starting dry-run rebase; repository will not be changed
248 starting dry-run rebase; repository will not be changed
249 rebasing 8:e147e6e3c490 "c/subdir/file.txt" (tip)
249 rebasing 8:e147e6e3c490 "c/subdir/file.txt" (tip)
250 abort: error: 'c/subdir/file.txt' conflicts with file 'c' in 3.
250 abort: error: 'c/subdir/file.txt' conflicts with file 'c' in 3.
251 [255]
251 [255]
252 $ hg rebase -r 3 -d . -n
252 $ hg rebase -r 3 -d . -n
253 starting dry-run rebase; repository will not be changed
253 starting dry-run rebase; repository will not be changed
254 rebasing 3:844a7de3e617 "c"
254 rebasing 3:844a7de3e617 "c"
255 abort: error: file 'c' cannot be written because 'c/' is a directory in e147e6e3c490 (containing 1 entries: c/subdir/file.txt)
255 abort: error: file 'c' cannot be written because 'c/' is a directory in e147e6e3c490 (containing 1 entries: c/subdir/file.txt)
256 [255]
256 [255]
257
257
258 $ cd ..
258 $ cd ..
259
259
260 Test path auditing (issue5818)
260 Test path auditing (issue5818)
261
261
262 $ mkdir lib_
262 $ mkdir lib_
263 $ ln -s lib_ lib
263 $ ln -s lib_ lib
264 $ hg init repo
264 $ hg init repo
265 $ cd repo
265 $ cd repo
266 $ mkdir -p ".$TESTTMP/lib"
266 $ mkdir -p ".$TESTTMP/lib"
267 $ touch ".$TESTTMP/lib/a"
267 $ touch ".$TESTTMP/lib/a"
268 $ hg add ".$TESTTMP/lib/a"
268 $ hg add ".$TESTTMP/lib/a"
269 $ hg ci -m 'a'
269 $ hg ci -m 'a'
270
270
271 $ touch ".$TESTTMP/lib/b"
271 $ touch ".$TESTTMP/lib/b"
272 $ hg add ".$TESTTMP/lib/b"
272 $ hg add ".$TESTTMP/lib/b"
273 $ hg ci -m 'b'
273 $ hg ci -m 'b'
274
274
275 $ hg up -q '.^'
275 $ hg up -q '.^'
276 $ touch ".$TESTTMP/lib/c"
276 $ touch ".$TESTTMP/lib/c"
277 $ hg add ".$TESTTMP/lib/c"
277 $ hg add ".$TESTTMP/lib/c"
278 $ hg ci -m 'c'
278 $ hg ci -m 'c'
279 created new head
279 created new head
280 $ hg rebase -s 1 -d .
280 $ hg rebase -s 1 -d .
281 rebasing 1:* "b" (glob)
281 rebasing 1:* "b" (glob)
282 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-rebase.hg (glob)
282 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-rebase.hg (glob)
283 $ cd ..
283 $ cd ..
284
284
285 Test dry-run rebasing
285 Test dry-run rebasing
286
286
287 $ hg init repo3
287 $ hg init repo3
288 $ cd repo3
288 $ cd repo3
289 $ echo a>a
289 $ echo a>a
290 $ hg ci -Aqma
290 $ hg ci -Aqma
291 $ echo b>b
291 $ echo b>b
292 $ hg ci -Aqmb
292 $ hg ci -Aqmb
293 $ echo c>c
293 $ echo c>c
294 $ hg ci -Aqmc
294 $ hg ci -Aqmc
295 $ echo d>d
295 $ echo d>d
296 $ hg ci -Aqmd
296 $ hg ci -Aqmd
297 $ echo e>e
297 $ echo e>e
298 $ hg ci -Aqme
298 $ hg ci -Aqme
299
299
300 $ hg up 1 -q
300 $ hg up 1 -q
301 $ echo f>f
301 $ echo f>f
302 $ hg ci -Amf
302 $ hg ci -Amf
303 adding f
303 adding f
304 created new head
304 created new head
305 $ echo g>g
305 $ echo g>g
306 $ hg ci -Aqmg
306 $ hg ci -Aqmg
307 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
307 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
308 @ 6:baf10c5166d4 test
308 @ 6:baf10c5166d4 test
309 | g
309 | g
310 |
310 |
311 o 5:6343ca3eff20 test
311 o 5:6343ca3eff20 test
312 | f
312 | f
313 |
313 |
314 | o 4:e860deea161a test
314 | o 4:e860deea161a test
315 | | e
315 | | e
316 | |
316 | |
317 | o 3:055a42cdd887 test
317 | o 3:055a42cdd887 test
318 | | d
318 | | d
319 | |
319 | |
320 | o 2:177f92b77385 test
320 | o 2:177f92b77385 test
321 |/ c
321 |/ c
322 |
322 |
323 o 1:d2ae7f538514 test
323 o 1:d2ae7f538514 test
324 | b
324 | b
325 |
325 |
326 o 0:cb9a9f314b8b test
326 o 0:cb9a9f314b8b test
327 a
327 a
328
328
329 Make sure it throws error while passing --continue or --abort with --dry-run
329 Make sure it throws error while passing --continue or --abort with --dry-run
330 $ hg rebase -s 2 -d 6 -n --continue
330 $ hg rebase -s 2 -d 6 -n --continue
331 abort: cannot specify both --dry-run and --continue
331 abort: cannot specify both --dry-run and --continue
332 [255]
332 [255]
333 $ hg rebase -s 2 -d 6 -n --abort
333 $ hg rebase -s 2 -d 6 -n --abort
334 abort: cannot specify both --dry-run and --abort
334 abort: cannot specify both --dry-run and --abort
335 [255]
335 [255]
336
336
337 Check dryrun gives correct results when there is no conflict in rebasing
337 Check dryrun gives correct results when there is no conflict in rebasing
338 $ hg rebase -s 2 -d 6 -n
338 $ hg rebase -s 2 -d 6 -n
339 starting dry-run rebase; repository will not be changed
339 starting dry-run rebase; repository will not be changed
340 rebasing 2:177f92b77385 "c"
340 rebasing 2:177f92b77385 "c"
341 rebasing 3:055a42cdd887 "d"
341 rebasing 3:055a42cdd887 "d"
342 rebasing 4:e860deea161a "e"
342 rebasing 4:e860deea161a "e"
343 dry-run rebase completed successfully; run without -n/--dry-run to perform this rebase
343 dry-run rebase completed successfully; run without -n/--dry-run to perform this rebase
344
344
345 $ hg diff
345 $ hg diff
346 $ hg status
346 $ hg status
347
347
348 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
348 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
349 @ 6:baf10c5166d4 test
349 @ 6:baf10c5166d4 test
350 | g
350 | g
351 |
351 |
352 o 5:6343ca3eff20 test
352 o 5:6343ca3eff20 test
353 | f
353 | f
354 |
354 |
355 | o 4:e860deea161a test
355 | o 4:e860deea161a test
356 | | e
356 | | e
357 | |
357 | |
358 | o 3:055a42cdd887 test
358 | o 3:055a42cdd887 test
359 | | d
359 | | d
360 | |
360 | |
361 | o 2:177f92b77385 test
361 | o 2:177f92b77385 test
362 |/ c
362 |/ c
363 |
363 |
364 o 1:d2ae7f538514 test
364 o 1:d2ae7f538514 test
365 | b
365 | b
366 |
366 |
367 o 0:cb9a9f314b8b test
367 o 0:cb9a9f314b8b test
368 a
368 a
369
369
370 Check dryrun working with --collapse when there is no conflict
370 Check dryrun working with --collapse when there is no conflict
371 $ hg rebase -s 2 -d 6 -n --collapse
371 $ hg rebase -s 2 -d 6 -n --collapse
372 starting dry-run rebase; repository will not be changed
372 starting dry-run rebase; repository will not be changed
373 rebasing 2:177f92b77385 "c"
373 rebasing 2:177f92b77385 "c"
374 rebasing 3:055a42cdd887 "d"
374 rebasing 3:055a42cdd887 "d"
375 rebasing 4:e860deea161a "e"
375 rebasing 4:e860deea161a "e"
376 dry-run rebase completed successfully; run without -n/--dry-run to perform this rebase
376 dry-run rebase completed successfully; run without -n/--dry-run to perform this rebase
377
377
378 Check dryrun gives correct results when there is conflict in rebasing
378 Check dryrun gives correct results when there is conflict in rebasing
379 Make a conflict:
379 Make a conflict:
380 $ hg up 6 -q
380 $ hg up 6 -q
381 $ echo conflict>e
381 $ echo conflict>e
382 $ hg ci -Aqm "conflict with e"
382 $ hg ci -Aqm "conflict with e"
383 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
383 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
384 @ 7:d2c195b28050 test
384 @ 7:d2c195b28050 test
385 | conflict with e
385 | conflict with e
386 |
386 |
387 o 6:baf10c5166d4 test
387 o 6:baf10c5166d4 test
388 | g
388 | g
389 |
389 |
390 o 5:6343ca3eff20 test
390 o 5:6343ca3eff20 test
391 | f
391 | f
392 |
392 |
393 | o 4:e860deea161a test
393 | o 4:e860deea161a test
394 | | e
394 | | e
395 | |
395 | |
396 | o 3:055a42cdd887 test
396 | o 3:055a42cdd887 test
397 | | d
397 | | d
398 | |
398 | |
399 | o 2:177f92b77385 test
399 | o 2:177f92b77385 test
400 |/ c
400 |/ c
401 |
401 |
402 o 1:d2ae7f538514 test
402 o 1:d2ae7f538514 test
403 | b
403 | b
404 |
404 |
405 o 0:cb9a9f314b8b test
405 o 0:cb9a9f314b8b test
406 a
406 a
407
407
408 $ hg rebase -s 2 -d 7 -n
408 $ hg rebase -s 2 -d 7 -n
409 starting dry-run rebase; repository will not be changed
409 starting dry-run rebase; repository will not be changed
410 rebasing 2:177f92b77385 "c"
410 rebasing 2:177f92b77385 "c"
411 rebasing 3:055a42cdd887 "d"
411 rebasing 3:055a42cdd887 "d"
412 rebasing 4:e860deea161a "e"
412 rebasing 4:e860deea161a "e"
413 merging e
413 merging e
414 transaction abort!
414 transaction abort!
415 rollback completed
415 rollback completed
416 hit a merge conflict
416 hit a merge conflict
417 [1]
417 [1]
418 $ hg diff
418 $ hg diff
419 $ hg status
419 $ hg status
420 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
420 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
421 @ 7:d2c195b28050 test
421 @ 7:d2c195b28050 test
422 | conflict with e
422 | conflict with e
423 |
423 |
424 o 6:baf10c5166d4 test
424 o 6:baf10c5166d4 test
425 | g
425 | g
426 |
426 |
427 o 5:6343ca3eff20 test
427 o 5:6343ca3eff20 test
428 | f
428 | f
429 |
429 |
430 | o 4:e860deea161a test
430 | o 4:e860deea161a test
431 | | e
431 | | e
432 | |
432 | |
433 | o 3:055a42cdd887 test
433 | o 3:055a42cdd887 test
434 | | d
434 | | d
435 | |
435 | |
436 | o 2:177f92b77385 test
436 | o 2:177f92b77385 test
437 |/ c
437 |/ c
438 |
438 |
439 o 1:d2ae7f538514 test
439 o 1:d2ae7f538514 test
440 | b
440 | b
441 |
441 |
442 o 0:cb9a9f314b8b test
442 o 0:cb9a9f314b8b test
443 a
443 a
444
444
445 Check dryrun working with --collapse when there is conflicts
445 Check dryrun working with --collapse when there is conflicts
446 $ hg rebase -s 2 -d 7 -n --collapse
446 $ hg rebase -s 2 -d 7 -n --collapse
447 starting dry-run rebase; repository will not be changed
447 starting dry-run rebase; repository will not be changed
448 rebasing 2:177f92b77385 "c"
448 rebasing 2:177f92b77385 "c"
449 rebasing 3:055a42cdd887 "d"
449 rebasing 3:055a42cdd887 "d"
450 rebasing 4:e860deea161a "e"
450 rebasing 4:e860deea161a "e"
451 merging e
451 merging e
452 hit a merge conflict
452 hit a merge conflict
453 [1]
453 [1]
454
454
455 In-memory rebase that fails due to merge conflicts
455 In-memory rebase that fails due to merge conflicts
456
456
457 $ hg rebase -s 2 -d 7
457 $ hg rebase -s 2 -d 7
458 rebasing 2:177f92b77385 "c"
458 rebasing 2:177f92b77385 "c"
459 rebasing 3:055a42cdd887 "d"
459 rebasing 3:055a42cdd887 "d"
460 rebasing 4:e860deea161a "e"
460 rebasing 4:e860deea161a "e"
461 merging e
461 merging e
462 transaction abort!
462 transaction abort!
463 rollback completed
463 rollback completed
464 hit merge conflicts; re-running rebase without in-memory merge
464 hit merge conflicts; re-running rebase without in-memory merge
465 rebasing 2:177f92b77385 "c"
465 rebasing 2:177f92b77385 "c"
466 rebasing 3:055a42cdd887 "d"
466 rebasing 3:055a42cdd887 "d"
467 rebasing 4:e860deea161a "e"
467 rebasing 4:e860deea161a "e"
468 merging e
468 merging e
469 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
469 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
470 unresolved conflicts (see hg resolve, then hg rebase --continue)
470 unresolved conflicts (see hg resolve, then hg rebase --continue)
471 [1]
471 [1]
472 $ hg rebase --abort
472 $ hg rebase --abort
473 saved backup bundle to $TESTTMP/repo3/.hg/strip-backup/c1e524d4287c-f91f82e1-backup.hg
473 saved backup bundle to $TESTTMP/repo3/.hg/strip-backup/c1e524d4287c-f91f82e1-backup.hg
474 rebase aborted
474 rebase aborted
475
475
476 Retrying without in-memory merge won't lose working copy changes
476 Retrying without in-memory merge won't lose working copy changes
477 $ cd ..
477 $ cd ..
478 $ hg clone repo3 repo3-dirty -q
478 $ hg clone repo3 repo3-dirty -q
479 $ cd repo3-dirty
479 $ cd repo3-dirty
480 $ echo dirty > a
480 $ echo dirty > a
481 $ hg rebase -s 2 -d 7
481 $ hg rebase -s 2 -d 7
482 rebasing 2:177f92b77385 "c"
482 rebasing 2:177f92b77385 "c"
483 rebasing 3:055a42cdd887 "d"
483 rebasing 3:055a42cdd887 "d"
484 rebasing 4:e860deea161a "e"
484 rebasing 4:e860deea161a "e"
485 merging e
485 merging e
486 transaction abort!
486 transaction abort!
487 rollback completed
487 rollback completed
488 hit merge conflicts; re-running rebase without in-memory merge
488 hit merge conflicts; re-running rebase without in-memory merge
489 abort: uncommitted changes
489 abort: uncommitted changes
490 [255]
490 [255]
491 $ cat a
491 $ cat a
492 dirty
492 dirty
493
493
494 Retrying without in-memory merge won't lose merge state
494 Retrying without in-memory merge won't lose merge state
495 $ cd ..
495 $ cd ..
496 $ hg clone repo3 repo3-merge-state -q
496 $ hg clone repo3 repo3-merge-state -q
497 $ cd repo3-merge-state
497 $ cd repo3-merge-state
498 $ hg merge 4
498 $ hg merge 4
499 merging e
499 merging e
500 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
500 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
501 2 files updated, 0 files merged, 0 files removed, 1 files unresolved
501 2 files updated, 0 files merged, 0 files removed, 1 files unresolved
502 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
502 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
503 [1]
503 [1]
504 $ hg resolve -l
504 $ hg resolve -l
505 U e
505 U e
506 $ hg rebase -s 2 -d 7
506 $ hg rebase -s 2 -d 7
507 rebasing 2:177f92b77385 "c"
507 rebasing 2:177f92b77385 "c"
508 abort: outstanding merge conflicts
508 abort: outstanding merge conflicts
509 (use 'hg resolve' to resolve)
509 [255]
510 [255]
510 $ hg resolve -l
511 $ hg resolve -l
511 U e
512 U e
512
513
513 ==========================
514 ==========================
514 Test for --confirm option|
515 Test for --confirm option|
515 ==========================
516 ==========================
516 $ cd ..
517 $ cd ..
517 $ hg clone repo3 repo4 -q
518 $ hg clone repo3 repo4 -q
518 $ cd repo4
519 $ cd repo4
519 $ hg strip 7 -q
520 $ hg strip 7 -q
520 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
521 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
521 @ 6:baf10c5166d4 test
522 @ 6:baf10c5166d4 test
522 | g
523 | g
523 |
524 |
524 o 5:6343ca3eff20 test
525 o 5:6343ca3eff20 test
525 | f
526 | f
526 |
527 |
527 | o 4:e860deea161a test
528 | o 4:e860deea161a test
528 | | e
529 | | e
529 | |
530 | |
530 | o 3:055a42cdd887 test
531 | o 3:055a42cdd887 test
531 | | d
532 | | d
532 | |
533 | |
533 | o 2:177f92b77385 test
534 | o 2:177f92b77385 test
534 |/ c
535 |/ c
535 |
536 |
536 o 1:d2ae7f538514 test
537 o 1:d2ae7f538514 test
537 | b
538 | b
538 |
539 |
539 o 0:cb9a9f314b8b test
540 o 0:cb9a9f314b8b test
540 a
541 a
541
542
542 Check it gives error when both --dryrun and --confirm is used:
543 Check it gives error when both --dryrun and --confirm is used:
543 $ hg rebase -s 2 -d . --confirm --dry-run
544 $ hg rebase -s 2 -d . --confirm --dry-run
544 abort: cannot specify both --confirm and --dry-run
545 abort: cannot specify both --confirm and --dry-run
545 [255]
546 [255]
546 $ hg rebase -s 2 -d . --confirm --abort
547 $ hg rebase -s 2 -d . --confirm --abort
547 abort: cannot specify both --confirm and --abort
548 abort: cannot specify both --confirm and --abort
548 [255]
549 [255]
549 $ hg rebase -s 2 -d . --confirm --continue
550 $ hg rebase -s 2 -d . --confirm --continue
550 abort: cannot specify both --confirm and --continue
551 abort: cannot specify both --confirm and --continue
551 [255]
552 [255]
552
553
553 Test --confirm option when there are no conflicts:
554 Test --confirm option when there are no conflicts:
554 $ hg rebase -s 2 -d . --keep --config ui.interactive=True --confirm << EOF
555 $ hg rebase -s 2 -d . --keep --config ui.interactive=True --confirm << EOF
555 > n
556 > n
556 > EOF
557 > EOF
557 starting in-memory rebase
558 starting in-memory rebase
558 rebasing 2:177f92b77385 "c"
559 rebasing 2:177f92b77385 "c"
559 rebasing 3:055a42cdd887 "d"
560 rebasing 3:055a42cdd887 "d"
560 rebasing 4:e860deea161a "e"
561 rebasing 4:e860deea161a "e"
561 rebase completed successfully
562 rebase completed successfully
562 apply changes (yn)? n
563 apply changes (yn)? n
563 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
564 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
564 @ 6:baf10c5166d4 test
565 @ 6:baf10c5166d4 test
565 | g
566 | g
566 |
567 |
567 o 5:6343ca3eff20 test
568 o 5:6343ca3eff20 test
568 | f
569 | f
569 |
570 |
570 | o 4:e860deea161a test
571 | o 4:e860deea161a test
571 | | e
572 | | e
572 | |
573 | |
573 | o 3:055a42cdd887 test
574 | o 3:055a42cdd887 test
574 | | d
575 | | d
575 | |
576 | |
576 | o 2:177f92b77385 test
577 | o 2:177f92b77385 test
577 |/ c
578 |/ c
578 |
579 |
579 o 1:d2ae7f538514 test
580 o 1:d2ae7f538514 test
580 | b
581 | b
581 |
582 |
582 o 0:cb9a9f314b8b test
583 o 0:cb9a9f314b8b test
583 a
584 a
584
585
585 $ hg rebase -s 2 -d . --keep --config ui.interactive=True --confirm << EOF
586 $ hg rebase -s 2 -d . --keep --config ui.interactive=True --confirm << EOF
586 > y
587 > y
587 > EOF
588 > EOF
588 starting in-memory rebase
589 starting in-memory rebase
589 rebasing 2:177f92b77385 "c"
590 rebasing 2:177f92b77385 "c"
590 rebasing 3:055a42cdd887 "d"
591 rebasing 3:055a42cdd887 "d"
591 rebasing 4:e860deea161a "e"
592 rebasing 4:e860deea161a "e"
592 rebase completed successfully
593 rebase completed successfully
593 apply changes (yn)? y
594 apply changes (yn)? y
594 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
595 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
595 o 9:9fd28f55f6dc test
596 o 9:9fd28f55f6dc test
596 | e
597 | e
597 |
598 |
598 o 8:12cbf031f469 test
599 o 8:12cbf031f469 test
599 | d
600 | d
600 |
601 |
601 o 7:c83b1da5b1ae test
602 o 7:c83b1da5b1ae test
602 | c
603 | c
603 |
604 |
604 @ 6:baf10c5166d4 test
605 @ 6:baf10c5166d4 test
605 | g
606 | g
606 |
607 |
607 o 5:6343ca3eff20 test
608 o 5:6343ca3eff20 test
608 | f
609 | f
609 |
610 |
610 | o 4:e860deea161a test
611 | o 4:e860deea161a test
611 | | e
612 | | e
612 | |
613 | |
613 | o 3:055a42cdd887 test
614 | o 3:055a42cdd887 test
614 | | d
615 | | d
615 | |
616 | |
616 | o 2:177f92b77385 test
617 | o 2:177f92b77385 test
617 |/ c
618 |/ c
618 |
619 |
619 o 1:d2ae7f538514 test
620 o 1:d2ae7f538514 test
620 | b
621 | b
621 |
622 |
622 o 0:cb9a9f314b8b test
623 o 0:cb9a9f314b8b test
623 a
624 a
624
625
625 Test --confirm option when there is a conflict
626 Test --confirm option when there is a conflict
626 $ hg up tip -q
627 $ hg up tip -q
627 $ echo ee>e
628 $ echo ee>e
628 $ hg ci --amend -m "conflict with e" -q
629 $ hg ci --amend -m "conflict with e" -q
629 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
630 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
630 @ 9:906d72f66a59 test
631 @ 9:906d72f66a59 test
631 | conflict with e
632 | conflict with e
632 |
633 |
633 o 8:12cbf031f469 test
634 o 8:12cbf031f469 test
634 | d
635 | d
635 |
636 |
636 o 7:c83b1da5b1ae test
637 o 7:c83b1da5b1ae test
637 | c
638 | c
638 |
639 |
639 o 6:baf10c5166d4 test
640 o 6:baf10c5166d4 test
640 | g
641 | g
641 |
642 |
642 o 5:6343ca3eff20 test
643 o 5:6343ca3eff20 test
643 | f
644 | f
644 |
645 |
645 | o 4:e860deea161a test
646 | o 4:e860deea161a test
646 | | e
647 | | e
647 | |
648 | |
648 | o 3:055a42cdd887 test
649 | o 3:055a42cdd887 test
649 | | d
650 | | d
650 | |
651 | |
651 | o 2:177f92b77385 test
652 | o 2:177f92b77385 test
652 |/ c
653 |/ c
653 |
654 |
654 o 1:d2ae7f538514 test
655 o 1:d2ae7f538514 test
655 | b
656 | b
656 |
657 |
657 o 0:cb9a9f314b8b test
658 o 0:cb9a9f314b8b test
658 a
659 a
659
660
660 $ hg rebase -s 4 -d . --keep --confirm
661 $ hg rebase -s 4 -d . --keep --confirm
661 starting in-memory rebase
662 starting in-memory rebase
662 rebasing 4:e860deea161a "e"
663 rebasing 4:e860deea161a "e"
663 merging e
664 merging e
664 hit a merge conflict
665 hit a merge conflict
665 [1]
666 [1]
666 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
667 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
667 @ 9:906d72f66a59 test
668 @ 9:906d72f66a59 test
668 | conflict with e
669 | conflict with e
669 |
670 |
670 o 8:12cbf031f469 test
671 o 8:12cbf031f469 test
671 | d
672 | d
672 |
673 |
673 o 7:c83b1da5b1ae test
674 o 7:c83b1da5b1ae test
674 | c
675 | c
675 |
676 |
676 o 6:baf10c5166d4 test
677 o 6:baf10c5166d4 test
677 | g
678 | g
678 |
679 |
679 o 5:6343ca3eff20 test
680 o 5:6343ca3eff20 test
680 | f
681 | f
681 |
682 |
682 | o 4:e860deea161a test
683 | o 4:e860deea161a test
683 | | e
684 | | e
684 | |
685 | |
685 | o 3:055a42cdd887 test
686 | o 3:055a42cdd887 test
686 | | d
687 | | d
687 | |
688 | |
688 | o 2:177f92b77385 test
689 | o 2:177f92b77385 test
689 |/ c
690 |/ c
690 |
691 |
691 o 1:d2ae7f538514 test
692 o 1:d2ae7f538514 test
692 | b
693 | b
693 |
694 |
694 o 0:cb9a9f314b8b test
695 o 0:cb9a9f314b8b test
695 a
696 a
696
697
697 Test a metadata-only in-memory merge
698 Test a metadata-only in-memory merge
698 $ cd $TESTTMP
699 $ cd $TESTTMP
699 $ hg init no_exception
700 $ hg init no_exception
700 $ cd no_exception
701 $ cd no_exception
701 # Produce the following graph:
702 # Produce the following graph:
702 # o 'add +x to foo.txt'
703 # o 'add +x to foo.txt'
703 # | o r1 (adds bar.txt, just for something to rebase to)
704 # | o r1 (adds bar.txt, just for something to rebase to)
704 # |/
705 # |/
705 # o r0 (adds foo.txt, no +x)
706 # o r0 (adds foo.txt, no +x)
706 $ echo hi > foo.txt
707 $ echo hi > foo.txt
707 $ hg ci -qAm r0
708 $ hg ci -qAm r0
708 $ echo hi > bar.txt
709 $ echo hi > bar.txt
709 $ hg ci -qAm r1
710 $ hg ci -qAm r1
710 $ hg co -qr ".^"
711 $ hg co -qr ".^"
711 $ chmod +x foo.txt
712 $ chmod +x foo.txt
712 $ hg ci -qAm 'add +x to foo.txt'
713 $ hg ci -qAm 'add +x to foo.txt'
713 issue5960: this was raising an AttributeError exception
714 issue5960: this was raising an AttributeError exception
714 $ hg rebase -r . -d 1
715 $ hg rebase -r . -d 1
715 rebasing 2:539b93e77479 "add +x to foo.txt" (tip)
716 rebasing 2:539b93e77479 "add +x to foo.txt" (tip)
716 saved backup bundle to $TESTTMP/no_exception/.hg/strip-backup/*.hg (glob)
717 saved backup bundle to $TESTTMP/no_exception/.hg/strip-backup/*.hg (glob)
717 $ hg diff -c tip
718 $ hg diff -c tip
718 diff --git a/foo.txt b/foo.txt
719 diff --git a/foo.txt b/foo.txt
719 old mode 100644
720 old mode 100644
720 new mode 100755
721 new mode 100755
721
722
722 Test rebasing a commit with copy information, but no content changes
723 Test rebasing a commit with copy information, but no content changes
723
724
724 $ cd ..
725 $ cd ..
725 $ hg clone -q repo1 merge-and-rename
726 $ hg clone -q repo1 merge-and-rename
726 $ cd merge-and-rename
727 $ cd merge-and-rename
727 $ cat << EOF >> .hg/hgrc
728 $ cat << EOF >> .hg/hgrc
728 > [experimental]
729 > [experimental]
729 > evolution.createmarkers=True
730 > evolution.createmarkers=True
730 > evolution.allowunstable=True
731 > evolution.allowunstable=True
731 > EOF
732 > EOF
732 $ hg co -q 1
733 $ hg co -q 1
733 $ hg mv d e
734 $ hg mv d e
734 $ hg ci -qm 'rename d to e'
735 $ hg ci -qm 'rename d to e'
735 $ hg co -q 3
736 $ hg co -q 3
736 $ hg merge -q 4
737 $ hg merge -q 4
737 $ hg ci -m 'merge'
738 $ hg ci -m 'merge'
738 $ hg co -q 2
739 $ hg co -q 2
739 $ mv d e
740 $ mv d e
740 $ hg addremove -qs 0
741 $ hg addremove -qs 0
741 $ hg ci -qm 'untracked rename of d to e'
742 $ hg ci -qm 'untracked rename of d to e'
742 $ hg debugobsolete -q `hg log -T '{node}' -r 4` `hg log -T '{node}' -r .`
743 $ hg debugobsolete -q `hg log -T '{node}' -r 4` `hg log -T '{node}' -r .`
743 1 new orphan changesets
744 1 new orphan changesets
744 $ hg tglog
745 $ hg tglog
745 @ 6: 676538af172d 'untracked rename of d to e'
746 @ 6: 676538af172d 'untracked rename of d to e'
746 |
747 |
747 | * 5: 574d92ad16fc 'merge'
748 | * 5: 574d92ad16fc 'merge'
748 | |\
749 | |\
749 | | x 4: 2c8b5dad7956 'rename d to e'
750 | | x 4: 2c8b5dad7956 'rename d to e'
750 | | |
751 | | |
751 | o | 3: ca58782ad1e4 'b'
752 | o | 3: ca58782ad1e4 'b'
752 |/ /
753 |/ /
753 o / 2: 814f6bd05178 'c'
754 o / 2: 814f6bd05178 'c'
754 |/
755 |/
755 o 1: 02952614a83d 'd'
756 o 1: 02952614a83d 'd'
756 |
757 |
757 o 0: b173517d0057 'a'
758 o 0: b173517d0057 'a'
758
759
759 $ hg rebase -b 5 -d tip
760 $ hg rebase -b 5 -d tip
760 rebasing 3:ca58782ad1e4 "b"
761 rebasing 3:ca58782ad1e4 "b"
761 rebasing 5:574d92ad16fc "merge"
762 rebasing 5:574d92ad16fc "merge"
762 note: not rebasing 5:574d92ad16fc "merge", its destination already has all its changes
763 note: not rebasing 5:574d92ad16fc "merge", its destination already has all its changes
763
764
764 $ cd ..
765 $ cd ..
765
766
766 Test rebasing a commit with copy information
767 Test rebasing a commit with copy information
767
768
768 $ hg init rebase-rename
769 $ hg init rebase-rename
769 $ cd rebase-rename
770 $ cd rebase-rename
770 $ echo a > a
771 $ echo a > a
771 $ hg ci -Aqm 'add a'
772 $ hg ci -Aqm 'add a'
772 $ echo a2 > a
773 $ echo a2 > a
773 $ hg ci -m 'modify a'
774 $ hg ci -m 'modify a'
774 $ hg co -q 0
775 $ hg co -q 0
775 $ hg mv a b
776 $ hg mv a b
776 $ hg ci -qm 'rename a to b'
777 $ hg ci -qm 'rename a to b'
777 $ hg rebase -d 1
778 $ hg rebase -d 1
778 rebasing 2:b977edf6f839 "rename a to b" (tip)
779 rebasing 2:b977edf6f839 "rename a to b" (tip)
779 merging a and b to b
780 merging a and b to b
780 saved backup bundle to $TESTTMP/rebase-rename/.hg/strip-backup/b977edf6f839-0864f570-rebase.hg
781 saved backup bundle to $TESTTMP/rebase-rename/.hg/strip-backup/b977edf6f839-0864f570-rebase.hg
781 $ hg st --copies --change .
782 $ hg st --copies --change .
782 A b
783 A b
783 a
784 a
784 R a
785 R a
785 $ cd ..
786 $ cd ..
786
787
787 Test rebasing a commit with copy information, where the target is empty
788 Test rebasing a commit with copy information, where the target is empty
788
789
789 $ hg init rebase-rename-empty
790 $ hg init rebase-rename-empty
790 $ cd rebase-rename-empty
791 $ cd rebase-rename-empty
791 $ echo a > a
792 $ echo a > a
792 $ hg ci -Aqm 'add a'
793 $ hg ci -Aqm 'add a'
793 $ cat > a
794 $ cat > a
794 $ hg ci -m 'make a empty'
795 $ hg ci -m 'make a empty'
795 $ hg co -q 0
796 $ hg co -q 0
796 $ hg mv a b
797 $ hg mv a b
797 $ hg ci -qm 'rename a to b'
798 $ hg ci -qm 'rename a to b'
798 $ hg rebase -d 1
799 $ hg rebase -d 1
799 rebasing 2:b977edf6f839 "rename a to b" (tip)
800 rebasing 2:b977edf6f839 "rename a to b" (tip)
800 merging a and b to b
801 merging a and b to b
801 saved backup bundle to $TESTTMP/rebase-rename-empty/.hg/strip-backup/b977edf6f839-0864f570-rebase.hg
802 saved backup bundle to $TESTTMP/rebase-rename-empty/.hg/strip-backup/b977edf6f839-0864f570-rebase.hg
802 $ hg st --copies --change .
803 $ hg st --copies --change .
803 A b
804 A b
804 a
805 a
805 R a
806 R a
806 $ cd ..
807 $ cd ..
807 Rebase across a copy with --collapse
808 Rebase across a copy with --collapse
808
809
809 $ hg init rebase-rename-collapse
810 $ hg init rebase-rename-collapse
810 $ cd rebase-rename-collapse
811 $ cd rebase-rename-collapse
811 $ echo a > a
812 $ echo a > a
812 $ hg ci -Aqm 'add a'
813 $ hg ci -Aqm 'add a'
813 $ hg mv a b
814 $ hg mv a b
814 $ hg ci -m 'rename a to b'
815 $ hg ci -m 'rename a to b'
815 $ hg co -q 0
816 $ hg co -q 0
816 $ echo a2 > a
817 $ echo a2 > a
817 $ hg ci -qm 'modify a'
818 $ hg ci -qm 'modify a'
818 $ hg rebase -r . -d 1 --collapse
819 $ hg rebase -r . -d 1 --collapse
819 rebasing 2:41c4ea50d4cf "modify a" (tip)
820 rebasing 2:41c4ea50d4cf "modify a" (tip)
820 merging b and a to b
821 merging b and a to b
821 saved backup bundle to $TESTTMP/rebase-rename-collapse/.hg/strip-backup/41c4ea50d4cf-b90b7994-rebase.hg
822 saved backup bundle to $TESTTMP/rebase-rename-collapse/.hg/strip-backup/41c4ea50d4cf-b90b7994-rebase.hg
822 $ cd ..
823 $ cd ..
823
824
824 Test rebasing when the file we are merging in destination is empty
825 Test rebasing when the file we are merging in destination is empty
825
826
826 $ hg init test
827 $ hg init test
827 $ cd test
828 $ cd test
828 $ echo a > foo
829 $ echo a > foo
829 $ hg ci -Aqm 'added a to foo'
830 $ hg ci -Aqm 'added a to foo'
830
831
831 $ rm foo
832 $ rm foo
832 $ touch foo
833 $ touch foo
833 $ hg di
834 $ hg di
834 diff --git a/foo b/foo
835 diff --git a/foo b/foo
835 --- a/foo
836 --- a/foo
836 +++ b/foo
837 +++ b/foo
837 @@ -1,1 +0,0 @@
838 @@ -1,1 +0,0 @@
838 -a
839 -a
839
840
840 $ hg ci -m "make foo an empty file"
841 $ hg ci -m "make foo an empty file"
841
842
842 $ hg up '.^'
843 $ hg up '.^'
843 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
844 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
844 $ echo b > foo
845 $ echo b > foo
845 $ hg di
846 $ hg di
846 diff --git a/foo b/foo
847 diff --git a/foo b/foo
847 --- a/foo
848 --- a/foo
848 +++ b/foo
849 +++ b/foo
849 @@ -1,1 +1,1 @@
850 @@ -1,1 +1,1 @@
850 -a
851 -a
851 +b
852 +b
852 $ hg ci -m "add b to foo"
853 $ hg ci -m "add b to foo"
853 created new head
854 created new head
854
855
855 $ hg rebase -r . -d 1 --config ui.merge=internal:merge3
856 $ hg rebase -r . -d 1 --config ui.merge=internal:merge3
856 rebasing 2:fb62b706688e "add b to foo" (tip)
857 rebasing 2:fb62b706688e "add b to foo" (tip)
857 merging foo
858 merging foo
858 hit merge conflicts; re-running rebase without in-memory merge
859 hit merge conflicts; re-running rebase without in-memory merge
859 rebasing 2:fb62b706688e "add b to foo" (tip)
860 rebasing 2:fb62b706688e "add b to foo" (tip)
860 merging foo
861 merging foo
861 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
862 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
862 unresolved conflicts (see hg resolve, then hg rebase --continue)
863 unresolved conflicts (see hg resolve, then hg rebase --continue)
863 [1]
864 [1]
@@ -1,800 +1,803 b''
1 test that a commit clears the merge state.
1 test that a commit clears the merge state.
2
2
3 $ hg init repo
3 $ hg init repo
4 $ cd repo
4 $ cd repo
5
5
6 $ echo foo > file1
6 $ echo foo > file1
7 $ echo foo > file2
7 $ echo foo > file2
8 $ hg commit -Am 'add files'
8 $ hg commit -Am 'add files'
9 adding file1
9 adding file1
10 adding file2
10 adding file2
11
11
12 $ echo bar >> file1
12 $ echo bar >> file1
13 $ echo bar >> file2
13 $ echo bar >> file2
14 $ hg commit -Am 'append bar to files'
14 $ hg commit -Am 'append bar to files'
15
15
16 create a second head with conflicting edits
16 create a second head with conflicting edits
17
17
18 $ hg up -C 0
18 $ hg up -C 0
19 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
19 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 $ echo baz >> file1
20 $ echo baz >> file1
21 $ echo baz >> file2
21 $ echo baz >> file2
22 $ hg commit -Am 'append baz to files'
22 $ hg commit -Am 'append baz to files'
23 created new head
23 created new head
24
24
25 create a third head with no conflicting edits
25 create a third head with no conflicting edits
26 $ hg up -qC 0
26 $ hg up -qC 0
27 $ echo foo > file3
27 $ echo foo > file3
28 $ hg commit -Am 'add non-conflicting file'
28 $ hg commit -Am 'add non-conflicting file'
29 adding file3
29 adding file3
30 created new head
30 created new head
31
31
32 failing merge
32 failing merge
33
33
34 $ hg up -qC 2
34 $ hg up -qC 2
35 $ hg merge --tool=internal:fail 1
35 $ hg merge --tool=internal:fail 1
36 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
36 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
37 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
37 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
38 [1]
38 [1]
39
39
40 resolve -l should contain unresolved entries
40 resolve -l should contain unresolved entries
41
41
42 $ hg resolve -l
42 $ hg resolve -l
43 U file1
43 U file1
44 U file2
44 U file2
45
45
46 $ hg resolve -l --no-status
46 $ hg resolve -l --no-status
47 file1
47 file1
48 file2
48 file2
49
49
50 resolving an unknown path should emit a warning, but not for -l
50 resolving an unknown path should emit a warning, but not for -l
51
51
52 $ hg resolve -m does-not-exist
52 $ hg resolve -m does-not-exist
53 arguments do not match paths that need resolving
53 arguments do not match paths that need resolving
54 $ hg resolve -l does-not-exist
54 $ hg resolve -l does-not-exist
55
55
56 tell users how they could have used resolve
56 tell users how they could have used resolve
57
57
58 $ mkdir nested
58 $ mkdir nested
59 $ cd nested
59 $ cd nested
60 $ hg resolve -m file1
60 $ hg resolve -m file1
61 arguments do not match paths that need resolving
61 arguments do not match paths that need resolving
62 (try: hg resolve -m path:file1)
62 (try: hg resolve -m path:file1)
63 $ hg resolve -m file1 filez
63 $ hg resolve -m file1 filez
64 arguments do not match paths that need resolving
64 arguments do not match paths that need resolving
65 (try: hg resolve -m path:file1 path:filez)
65 (try: hg resolve -m path:file1 path:filez)
66 $ hg resolve -m path:file1 path:filez
66 $ hg resolve -m path:file1 path:filez
67 $ hg resolve -l
67 $ hg resolve -l
68 R file1
68 R file1
69 U file2
69 U file2
70 $ hg resolve -l --config ui.relative-paths=yes
70 $ hg resolve -l --config ui.relative-paths=yes
71 R ../file1
71 R ../file1
72 U ../file2
72 U ../file2
73 $ hg resolve --re-merge filez file2
73 $ hg resolve --re-merge filez file2
74 arguments do not match paths that need resolving
74 arguments do not match paths that need resolving
75 (try: hg resolve --re-merge path:filez path:file2)
75 (try: hg resolve --re-merge path:filez path:file2)
76 $ hg resolve -m filez file2
76 $ hg resolve -m filez file2
77 arguments do not match paths that need resolving
77 arguments do not match paths that need resolving
78 (try: hg resolve -m path:filez path:file2)
78 (try: hg resolve -m path:filez path:file2)
79 $ hg resolve -m path:filez path:file2
79 $ hg resolve -m path:filez path:file2
80 (no more unresolved files)
80 (no more unresolved files)
81 $ hg resolve -l
81 $ hg resolve -l
82 R file1
82 R file1
83 R file2
83 R file2
84
84
85 cleanup
85 cleanup
86 $ hg resolve -u
86 $ hg resolve -u
87 $ cd ..
87 $ cd ..
88 $ rmdir nested
88 $ rmdir nested
89
89
90 don't allow marking or unmarking driver-resolved files
90 don't allow marking or unmarking driver-resolved files
91
91
92 $ cat > $TESTTMP/markdriver.py << EOF
92 $ cat > $TESTTMP/markdriver.py << EOF
93 > '''mark and unmark files as driver-resolved'''
93 > '''mark and unmark files as driver-resolved'''
94 > from mercurial import (
94 > from mercurial import (
95 > merge,
95 > merge,
96 > pycompat,
96 > pycompat,
97 > registrar,
97 > registrar,
98 > scmutil,
98 > scmutil,
99 > )
99 > )
100 > cmdtable = {}
100 > cmdtable = {}
101 > command = registrar.command(cmdtable)
101 > command = registrar.command(cmdtable)
102 > @command(b'markdriver',
102 > @command(b'markdriver',
103 > [(b'u', b'unmark', None, b'')],
103 > [(b'u', b'unmark', None, b'')],
104 > b'FILE...')
104 > b'FILE...')
105 > def markdriver(ui, repo, *pats, **opts):
105 > def markdriver(ui, repo, *pats, **opts):
106 > wlock = repo.wlock()
106 > wlock = repo.wlock()
107 > opts = pycompat.byteskwargs(opts)
107 > opts = pycompat.byteskwargs(opts)
108 > try:
108 > try:
109 > ms = merge.mergestate.read(repo)
109 > ms = merge.mergestate.read(repo)
110 > m = scmutil.match(repo[None], pats, opts)
110 > m = scmutil.match(repo[None], pats, opts)
111 > for f in ms:
111 > for f in ms:
112 > if not m(f):
112 > if not m(f):
113 > continue
113 > continue
114 > if not opts[b'unmark']:
114 > if not opts[b'unmark']:
115 > ms.mark(f, b'd')
115 > ms.mark(f, b'd')
116 > else:
116 > else:
117 > ms.mark(f, b'u')
117 > ms.mark(f, b'u')
118 > ms.commit()
118 > ms.commit()
119 > finally:
119 > finally:
120 > wlock.release()
120 > wlock.release()
121 > EOF
121 > EOF
122 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver file1
122 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver file1
123 $ hg resolve --list
123 $ hg resolve --list
124 D file1
124 D file1
125 U file2
125 U file2
126 $ hg resolve --mark file1
126 $ hg resolve --mark file1
127 not marking file1 as it is driver-resolved
127 not marking file1 as it is driver-resolved
128 this should not print out file1
128 this should not print out file1
129 $ hg resolve --mark --all
129 $ hg resolve --mark --all
130 (no more unresolved files -- run "hg resolve --all" to conclude)
130 (no more unresolved files -- run "hg resolve --all" to conclude)
131 $ hg resolve --mark 'glob:file*'
131 $ hg resolve --mark 'glob:file*'
132 (no more unresolved files -- run "hg resolve --all" to conclude)
132 (no more unresolved files -- run "hg resolve --all" to conclude)
133 $ hg resolve --list
133 $ hg resolve --list
134 D file1
134 D file1
135 R file2
135 R file2
136 $ hg resolve --unmark file1
136 $ hg resolve --unmark file1
137 not unmarking file1 as it is driver-resolved
137 not unmarking file1 as it is driver-resolved
138 (no more unresolved files -- run "hg resolve --all" to conclude)
138 (no more unresolved files -- run "hg resolve --all" to conclude)
139 $ hg resolve --unmark --all
139 $ hg resolve --unmark --all
140 $ hg resolve --list
140 $ hg resolve --list
141 D file1
141 D file1
142 U file2
142 U file2
143 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver --unmark file1
143 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver --unmark file1
144 $ hg resolve --list
144 $ hg resolve --list
145 U file1
145 U file1
146 U file2
146 U file2
147
147
148 resolve the failure
148 resolve the failure
149
149
150 $ echo resolved > file1
150 $ echo resolved > file1
151 $ hg resolve -m file1
151 $ hg resolve -m file1
152
152
153 resolve -l should show resolved file as resolved
153 resolve -l should show resolved file as resolved
154
154
155 $ hg resolve -l
155 $ hg resolve -l
156 R file1
156 R file1
157 U file2
157 U file2
158
158
159 $ hg resolve -l -Tjson
159 $ hg resolve -l -Tjson
160 [
160 [
161 {
161 {
162 "mergestatus": "R",
162 "mergestatus": "R",
163 "path": "file1"
163 "path": "file1"
164 },
164 },
165 {
165 {
166 "mergestatus": "U",
166 "mergestatus": "U",
167 "path": "file2"
167 "path": "file2"
168 }
168 }
169 ]
169 ]
170
170
171 $ hg resolve -l -T '{path} {mergestatus} {status} {p1rev} {p2rev}\n'
171 $ hg resolve -l -T '{path} {mergestatus} {status} {p1rev} {p2rev}\n'
172 file1 R M 2 1
172 file1 R M 2 1
173 file2 U M 2 1
173 file2 U M 2 1
174
174
175 resolve -m without paths should mark all resolved
175 resolve -m without paths should mark all resolved
176
176
177 $ hg resolve -m
177 $ hg resolve -m
178 (no more unresolved files)
178 (no more unresolved files)
179 $ hg commit -m 'resolved'
179 $ hg commit -m 'resolved'
180
180
181 resolve -l should be empty after commit
181 resolve -l should be empty after commit
182
182
183 $ hg resolve -l
183 $ hg resolve -l
184
184
185 $ hg resolve -l -Tjson
185 $ hg resolve -l -Tjson
186 [
186 [
187 ]
187 ]
188
188
189 resolve --all should abort when no merge in progress
189 resolve --all should abort when no merge in progress
190
190
191 $ hg resolve --all
191 $ hg resolve --all
192 abort: resolve command not applicable when not merging
192 abort: resolve command not applicable when not merging
193 [255]
193 [255]
194
194
195 resolve -m should abort when no merge in progress
195 resolve -m should abort when no merge in progress
196
196
197 $ hg resolve -m
197 $ hg resolve -m
198 abort: resolve command not applicable when not merging
198 abort: resolve command not applicable when not merging
199 [255]
199 [255]
200
200
201 can not update or merge when there are unresolved conflicts
201 can not update or merge when there are unresolved conflicts
202
202
203 $ hg up -qC 0
203 $ hg up -qC 0
204 $ echo quux >> file1
204 $ echo quux >> file1
205 $ hg up 1
205 $ hg up 1
206 merging file1
206 merging file1
207 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
207 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
208 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
208 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
209 use 'hg resolve' to retry unresolved file merges
209 use 'hg resolve' to retry unresolved file merges
210 [1]
210 [1]
211 $ hg up 0
211 $ hg up 0
212 abort: outstanding merge conflicts
212 abort: outstanding merge conflicts
213 (use 'hg resolve' to resolve)
213 [255]
214 [255]
214 $ hg merge 2
215 $ hg merge 2
215 abort: outstanding merge conflicts
216 abort: outstanding merge conflicts
217 (use 'hg resolve' to resolve)
216 [255]
218 [255]
217 $ hg merge --force 2
219 $ hg merge --force 2
218 abort: outstanding merge conflicts
220 abort: outstanding merge conflicts
221 (use 'hg resolve' to resolve)
219 [255]
222 [255]
220
223
221 set up conflict-free merge
224 set up conflict-free merge
222
225
223 $ hg up -qC 3
226 $ hg up -qC 3
224 $ hg merge 1
227 $ hg merge 1
225 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
228 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
226 (branch merge, don't forget to commit)
229 (branch merge, don't forget to commit)
227
230
228 resolve --all should do nothing in merge without conflicts
231 resolve --all should do nothing in merge without conflicts
229 $ hg resolve --all
232 $ hg resolve --all
230 (no more unresolved files)
233 (no more unresolved files)
231
234
232 resolve -m should do nothing in merge without conflicts
235 resolve -m should do nothing in merge without conflicts
233
236
234 $ hg resolve -m
237 $ hg resolve -m
235 (no more unresolved files)
238 (no more unresolved files)
236
239
237 get back to conflicting state
240 get back to conflicting state
238
241
239 $ hg up -qC 2
242 $ hg up -qC 2
240 $ hg merge --tool=internal:fail 1
243 $ hg merge --tool=internal:fail 1
241 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
244 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
242 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
245 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
243 [1]
246 [1]
244
247
245 resolve without arguments should suggest --all
248 resolve without arguments should suggest --all
246 $ hg resolve
249 $ hg resolve
247 abort: no files or directories specified
250 abort: no files or directories specified
248 (use --all to re-merge all unresolved files)
251 (use --all to re-merge all unresolved files)
249 [255]
252 [255]
250
253
251 resolve --all should re-merge all unresolved files
254 resolve --all should re-merge all unresolved files
252 $ hg resolve --all
255 $ hg resolve --all
253 merging file1
256 merging file1
254 merging file2
257 merging file2
255 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
258 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
256 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
259 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
257 [1]
260 [1]
258 $ cat file1.orig
261 $ cat file1.orig
259 foo
262 foo
260 baz
263 baz
261 $ cat file2.orig
264 $ cat file2.orig
262 foo
265 foo
263 baz
266 baz
264
267
265 .orig files should exists where specified
268 .orig files should exists where specified
266 $ hg resolve --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
269 $ hg resolve --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
267 merging file1
270 merging file1
268 creating directory: $TESTTMP/repo/.hg/origbackups
271 creating directory: $TESTTMP/repo/.hg/origbackups
269 merging file2
272 merging file2
270 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
273 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
271 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
274 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
272 [1]
275 [1]
273 $ ls .hg/origbackups
276 $ ls .hg/origbackups
274 file1
277 file1
275 file2
278 file2
276 $ grep '<<<' file1 > /dev/null
279 $ grep '<<<' file1 > /dev/null
277 $ grep '<<<' file2 > /dev/null
280 $ grep '<<<' file2 > /dev/null
278
281
279 resolve <file> should re-merge file
282 resolve <file> should re-merge file
280 $ echo resolved > file1
283 $ echo resolved > file1
281 $ hg resolve -q file1
284 $ hg resolve -q file1
282 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
285 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
283 [1]
286 [1]
284 $ grep '<<<' file1 > /dev/null
287 $ grep '<<<' file1 > /dev/null
285
288
286 test .orig behavior with resolve
289 test .orig behavior with resolve
287
290
288 $ hg resolve -q file1 --tool "sh -c 'f --dump \"$TESTTMP/repo/file1.orig\"'"
291 $ hg resolve -q file1 --tool "sh -c 'f --dump \"$TESTTMP/repo/file1.orig\"'"
289 $TESTTMP/repo/file1.orig:
292 $TESTTMP/repo/file1.orig:
290 >>>
293 >>>
291 foo
294 foo
292 baz
295 baz
293 <<<
296 <<<
294
297
295 resolve <file> should do nothing if 'file' was marked resolved
298 resolve <file> should do nothing if 'file' was marked resolved
296 $ echo resolved > file1
299 $ echo resolved > file1
297 $ hg resolve -m file1
300 $ hg resolve -m file1
298 $ hg resolve -q file1
301 $ hg resolve -q file1
299 $ cat file1
302 $ cat file1
300 resolved
303 resolved
301
304
302 insert unsupported advisory merge record
305 insert unsupported advisory merge record
303
306
304 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
307 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
305 $ hg debugmergestate
308 $ hg debugmergestate
306 * version 2 records
309 * version 2 records
307 local: 57653b9f834a4493f7240b0681efcb9ae7cab745
310 local: 57653b9f834a4493f7240b0681efcb9ae7cab745
308 other: dc77451844e37f03f5c559e3b8529b2b48d381d1
311 other: dc77451844e37f03f5c559e3b8529b2b48d381d1
309 labels:
312 labels:
310 local: working copy
313 local: working copy
311 other: merge rev
314 other: merge rev
312 unrecognized entry: x advisory record
315 unrecognized entry: x advisory record
313 file extras: file1 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
316 file extras: file1 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
314 file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
317 file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
315 local path: file1 (flags "")
318 local path: file1 (flags "")
316 ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
319 ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
317 other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
320 other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
318 file extras: file2 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
321 file extras: file2 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
319 file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
322 file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
320 local path: file2 (flags "")
323 local path: file2 (flags "")
321 ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
324 ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
322 other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
325 other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
323 $ hg resolve -l
326 $ hg resolve -l
324 R file1
327 R file1
325 U file2
328 U file2
326
329
327 insert unsupported mandatory merge record
330 insert unsupported mandatory merge record
328
331
329 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
332 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
330 $ hg debugmergestate
333 $ hg debugmergestate
331 * version 2 records
334 * version 2 records
332 local: 57653b9f834a4493f7240b0681efcb9ae7cab745
335 local: 57653b9f834a4493f7240b0681efcb9ae7cab745
333 other: dc77451844e37f03f5c559e3b8529b2b48d381d1
336 other: dc77451844e37f03f5c559e3b8529b2b48d381d1
334 labels:
337 labels:
335 local: working copy
338 local: working copy
336 other: merge rev
339 other: merge rev
337 file extras: file1 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
340 file extras: file1 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
338 file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
341 file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
339 local path: file1 (flags "")
342 local path: file1 (flags "")
340 ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
343 ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
341 other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
344 other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
342 file extras: file2 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
345 file extras: file2 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac)
343 file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
346 file: file2 (record type "F", state "u", hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523)
344 local path: file2 (flags "")
347 local path: file2 (flags "")
345 ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
348 ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
346 other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
349 other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
347 unrecognized entry: X mandatory record
350 unrecognized entry: X mandatory record
348 $ hg resolve -l
351 $ hg resolve -l
349 abort: unsupported merge state records: X
352 abort: unsupported merge state records: X
350 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
353 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
351 [255]
354 [255]
352 $ hg resolve -ma
355 $ hg resolve -ma
353 abort: unsupported merge state records: X
356 abort: unsupported merge state records: X
354 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
357 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
355 [255]
358 [255]
356 $ hg summary
359 $ hg summary
357 warning: merge state has unsupported record types: X
360 warning: merge state has unsupported record types: X
358 parent: 2:57653b9f834a
361 parent: 2:57653b9f834a
359 append baz to files
362 append baz to files
360 parent: 1:dc77451844e3
363 parent: 1:dc77451844e3
361 append bar to files
364 append bar to files
362 branch: default
365 branch: default
363 commit: 2 modified, 2 unknown (merge)
366 commit: 2 modified, 2 unknown (merge)
364 update: 2 new changesets (update)
367 update: 2 new changesets (update)
365 phases: 5 draft
368 phases: 5 draft
366
369
367 update --clean shouldn't abort on unsupported records
370 update --clean shouldn't abort on unsupported records
368
371
369 $ hg up -qC 1
372 $ hg up -qC 1
370 $ hg debugmergestate
373 $ hg debugmergestate
371 no merge state found
374 no merge state found
372
375
373 test crashed merge with empty mergestate
376 test crashed merge with empty mergestate
374
377
375 $ mkdir .hg/merge
378 $ mkdir .hg/merge
376 $ touch .hg/merge/state
379 $ touch .hg/merge/state
377
380
378 resolve -l should be empty
381 resolve -l should be empty
379
382
380 $ hg resolve -l
383 $ hg resolve -l
381
384
382 resolve -m can be configured to look for remaining conflict markers
385 resolve -m can be configured to look for remaining conflict markers
383 $ hg up -qC 2
386 $ hg up -qC 2
384 $ hg merge -q --tool=internal:merge 1
387 $ hg merge -q --tool=internal:merge 1
385 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
388 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
386 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
389 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
387 [1]
390 [1]
388 $ hg resolve -l
391 $ hg resolve -l
389 U file1
392 U file1
390 U file2
393 U file2
391 $ echo 'remove markers' > file1
394 $ echo 'remove markers' > file1
392 $ hg --config commands.resolve.mark-check=abort resolve -m
395 $ hg --config commands.resolve.mark-check=abort resolve -m
393 warning: the following files still have conflict markers:
396 warning: the following files still have conflict markers:
394 file2
397 file2
395 abort: conflict markers detected
398 abort: conflict markers detected
396 (use --all to mark anyway)
399 (use --all to mark anyway)
397 [255]
400 [255]
398 $ hg resolve -l
401 $ hg resolve -l
399 U file1
402 U file1
400 U file2
403 U file2
401 Try with --all from the hint
404 Try with --all from the hint
402 $ hg --config commands.resolve.mark-check=abort resolve -m --all
405 $ hg --config commands.resolve.mark-check=abort resolve -m --all
403 warning: the following files still have conflict markers:
406 warning: the following files still have conflict markers:
404 file2
407 file2
405 (no more unresolved files)
408 (no more unresolved files)
406 $ hg resolve -l
409 $ hg resolve -l
407 R file1
410 R file1
408 R file2
411 R file2
409 Test option value 'warn'
412 Test option value 'warn'
410 $ hg resolve --unmark
413 $ hg resolve --unmark
411 $ hg resolve -l
414 $ hg resolve -l
412 U file1
415 U file1
413 U file2
416 U file2
414 $ hg --config commands.resolve.mark-check=warn resolve -m
417 $ hg --config commands.resolve.mark-check=warn resolve -m
415 warning: the following files still have conflict markers:
418 warning: the following files still have conflict markers:
416 file2
419 file2
417 (no more unresolved files)
420 (no more unresolved files)
418 $ hg resolve -l
421 $ hg resolve -l
419 R file1
422 R file1
420 R file2
423 R file2
421 If the file is already marked as resolved, we don't warn about it
424 If the file is already marked as resolved, we don't warn about it
422 $ hg resolve --unmark file1
425 $ hg resolve --unmark file1
423 $ hg resolve -l
426 $ hg resolve -l
424 U file1
427 U file1
425 R file2
428 R file2
426 $ hg --config commands.resolve.mark-check=warn resolve -m
429 $ hg --config commands.resolve.mark-check=warn resolve -m
427 (no more unresolved files)
430 (no more unresolved files)
428 $ hg resolve -l
431 $ hg resolve -l
429 R file1
432 R file1
430 R file2
433 R file2
431 If the user passes an invalid value, we treat it as 'none'.
434 If the user passes an invalid value, we treat it as 'none'.
432 $ hg resolve --unmark
435 $ hg resolve --unmark
433 $ hg resolve -l
436 $ hg resolve -l
434 U file1
437 U file1
435 U file2
438 U file2
436 $ hg --config commands.resolve.mark-check=nope resolve -m
439 $ hg --config commands.resolve.mark-check=nope resolve -m
437 (no more unresolved files)
440 (no more unresolved files)
438 $ hg resolve -l
441 $ hg resolve -l
439 R file1
442 R file1
440 R file2
443 R file2
441 Test explicitly setting the option to 'none'
444 Test explicitly setting the option to 'none'
442 $ hg resolve --unmark
445 $ hg resolve --unmark
443 $ hg resolve -l
446 $ hg resolve -l
444 U file1
447 U file1
445 U file2
448 U file2
446 $ hg --config commands.resolve.mark-check=none resolve -m
449 $ hg --config commands.resolve.mark-check=none resolve -m
447 (no more unresolved files)
450 (no more unresolved files)
448 $ hg resolve -l
451 $ hg resolve -l
449 R file1
452 R file1
450 R file2
453 R file2
451 Test with marking an explicit file as resolved, this should not abort (since
454 Test with marking an explicit file as resolved, this should not abort (since
452 there's no --force flag, we have no way of combining --all with a filename)
455 there's no --force flag, we have no way of combining --all with a filename)
453 $ hg resolve --unmark
456 $ hg resolve --unmark
454 $ hg resolve -l
457 $ hg resolve -l
455 U file1
458 U file1
456 U file2
459 U file2
457 (This downgrades to a warning since an explicit file was specified).
460 (This downgrades to a warning since an explicit file was specified).
458 $ hg --config commands.resolve.mark-check=abort resolve -m file2
461 $ hg --config commands.resolve.mark-check=abort resolve -m file2
459 warning: the following files still have conflict markers:
462 warning: the following files still have conflict markers:
460 file2
463 file2
461 $ hg resolve -l
464 $ hg resolve -l
462 U file1
465 U file1
463 R file2
466 R file2
464 Testing the --re-merge flag
467 Testing the --re-merge flag
465 $ hg resolve --unmark file1
468 $ hg resolve --unmark file1
466 $ hg resolve -l
469 $ hg resolve -l
467 U file1
470 U file1
468 R file2
471 R file2
469 $ hg resolve --mark --re-merge
472 $ hg resolve --mark --re-merge
470 abort: too many actions specified
473 abort: too many actions specified
471 [255]
474 [255]
472 $ hg resolve --re-merge --all
475 $ hg resolve --re-merge --all
473 merging file1
476 merging file1
474 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
477 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
475 [1]
478 [1]
476 Explicit re-merge
479 Explicit re-merge
477 $ hg resolve --unmark file1
480 $ hg resolve --unmark file1
478 $ hg resolve --config commands.resolve.explicit-re-merge=1 --all
481 $ hg resolve --config commands.resolve.explicit-re-merge=1 --all
479 abort: no action specified
482 abort: no action specified
480 (use --mark, --unmark, --list or --re-merge)
483 (use --mark, --unmark, --list or --re-merge)
481 [255]
484 [255]
482 $ hg resolve --config commands.resolve.explicit-re-merge=1 --re-merge --all
485 $ hg resolve --config commands.resolve.explicit-re-merge=1 --re-merge --all
483 merging file1
486 merging file1
484 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
487 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
485 [1]
488 [1]
486
489
487 $ cd ..
490 $ cd ..
488
491
489 ======================================================
492 ======================================================
490 Test 'hg resolve' confirm config option functionality |
493 Test 'hg resolve' confirm config option functionality |
491 ======================================================
494 ======================================================
492 $ cat >> $HGRCPATH << EOF
495 $ cat >> $HGRCPATH << EOF
493 > [extensions]
496 > [extensions]
494 > rebase=
497 > rebase=
495 > EOF
498 > EOF
496
499
497 $ hg init repo2
500 $ hg init repo2
498 $ cd repo2
501 $ cd repo2
499
502
500 $ echo boss > boss
503 $ echo boss > boss
501 $ hg ci -Am "add boss"
504 $ hg ci -Am "add boss"
502 adding boss
505 adding boss
503
506
504 $ for emp in emp1 emp2 emp3; do echo work > $emp; done;
507 $ for emp in emp1 emp2 emp3; do echo work > $emp; done;
505 $ hg ci -Aqm "added emp1 emp2 emp3"
508 $ hg ci -Aqm "added emp1 emp2 emp3"
506
509
507 $ hg up 0
510 $ hg up 0
508 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
511 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
509
512
510 $ for emp in emp1 emp2 emp3; do echo nowork > $emp; done;
513 $ for emp in emp1 emp2 emp3; do echo nowork > $emp; done;
511 $ hg ci -Aqm "added lazy emp1 emp2 emp3"
514 $ hg ci -Aqm "added lazy emp1 emp2 emp3"
512
515
513 $ hg log -GT "{rev} {node|short} {firstline(desc)}\n"
516 $ hg log -GT "{rev} {node|short} {firstline(desc)}\n"
514 @ 2 0acfd4a49af0 added lazy emp1 emp2 emp3
517 @ 2 0acfd4a49af0 added lazy emp1 emp2 emp3
515 |
518 |
516 | o 1 f30f98a8181f added emp1 emp2 emp3
519 | o 1 f30f98a8181f added emp1 emp2 emp3
517 |/
520 |/
518 o 0 88660038d466 add boss
521 o 0 88660038d466 add boss
519
522
520 $ hg rebase -s 1 -d 2
523 $ hg rebase -s 1 -d 2
521 rebasing 1:f30f98a8181f "added emp1 emp2 emp3"
524 rebasing 1:f30f98a8181f "added emp1 emp2 emp3"
522 merging emp1
525 merging emp1
523 merging emp2
526 merging emp2
524 merging emp3
527 merging emp3
525 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
528 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
526 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
529 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
527 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
530 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
528 unresolved conflicts (see hg resolve, then hg rebase --continue)
531 unresolved conflicts (see hg resolve, then hg rebase --continue)
529 [1]
532 [1]
530
533
531 Test when commands.resolve.confirm config option is not set:
534 Test when commands.resolve.confirm config option is not set:
532 ===========================================================
535 ===========================================================
533 $ hg resolve --all
536 $ hg resolve --all
534 merging emp1
537 merging emp1
535 merging emp2
538 merging emp2
536 merging emp3
539 merging emp3
537 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
540 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
538 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
541 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
539 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
542 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
540 [1]
543 [1]
541
544
542 Test when config option is set:
545 Test when config option is set:
543 ==============================
546 ==============================
544 $ cat >> .hg/hgrc << EOF
547 $ cat >> .hg/hgrc << EOF
545 > [ui]
548 > [ui]
546 > interactive = True
549 > interactive = True
547 > [commands]
550 > [commands]
548 > resolve.confirm = True
551 > resolve.confirm = True
549 > EOF
552 > EOF
550
553
551 $ hg resolve
554 $ hg resolve
552 abort: no files or directories specified
555 abort: no files or directories specified
553 (use --all to re-merge all unresolved files)
556 (use --all to re-merge all unresolved files)
554 [255]
557 [255]
555 $ hg resolve --all << EOF
558 $ hg resolve --all << EOF
556 > n
559 > n
557 > EOF
560 > EOF
558 re-merge all unresolved files (yn)? n
561 re-merge all unresolved files (yn)? n
559 abort: user quit
562 abort: user quit
560 [255]
563 [255]
561
564
562 $ hg resolve --all << EOF
565 $ hg resolve --all << EOF
563 > y
566 > y
564 > EOF
567 > EOF
565 re-merge all unresolved files (yn)? y
568 re-merge all unresolved files (yn)? y
566 merging emp1
569 merging emp1
567 merging emp2
570 merging emp2
568 merging emp3
571 merging emp3
569 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
572 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
570 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
573 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
571 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
574 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
572 [1]
575 [1]
573
576
574 Test that commands.resolve.confirm respect --mark option (only when no patterns args are given):
577 Test that commands.resolve.confirm respect --mark option (only when no patterns args are given):
575 ===============================================================================================
578 ===============================================================================================
576
579
577 $ hg resolve -m emp1
580 $ hg resolve -m emp1
578 $ hg resolve -l
581 $ hg resolve -l
579 R emp1
582 R emp1
580 U emp2
583 U emp2
581 U emp3
584 U emp3
582
585
583 $ hg resolve -m << EOF
586 $ hg resolve -m << EOF
584 > n
587 > n
585 > EOF
588 > EOF
586 mark all unresolved files as resolved (yn)? n
589 mark all unresolved files as resolved (yn)? n
587 abort: user quit
590 abort: user quit
588 [255]
591 [255]
589
592
590 $ hg resolve -m << EOF
593 $ hg resolve -m << EOF
591 > y
594 > y
592 > EOF
595 > EOF
593 mark all unresolved files as resolved (yn)? y
596 mark all unresolved files as resolved (yn)? y
594 (no more unresolved files)
597 (no more unresolved files)
595 continue: hg rebase --continue
598 continue: hg rebase --continue
596 $ hg resolve -l
599 $ hg resolve -l
597 R emp1
600 R emp1
598 R emp2
601 R emp2
599 R emp3
602 R emp3
600
603
601 Test that commands.resolve.confirm respect --unmark option (only when no patterns args are given):
604 Test that commands.resolve.confirm respect --unmark option (only when no patterns args are given):
602 =================================================================================================
605 =================================================================================================
603
606
604 $ hg resolve -u emp1
607 $ hg resolve -u emp1
605
608
606 $ hg resolve -l
609 $ hg resolve -l
607 U emp1
610 U emp1
608 R emp2
611 R emp2
609 R emp3
612 R emp3
610
613
611 $ hg resolve -u << EOF
614 $ hg resolve -u << EOF
612 > n
615 > n
613 > EOF
616 > EOF
614 mark all resolved files as unresolved (yn)? n
617 mark all resolved files as unresolved (yn)? n
615 abort: user quit
618 abort: user quit
616 [255]
619 [255]
617
620
618 $ hg resolve -m << EOF
621 $ hg resolve -m << EOF
619 > y
622 > y
620 > EOF
623 > EOF
621 mark all unresolved files as resolved (yn)? y
624 mark all unresolved files as resolved (yn)? y
622 (no more unresolved files)
625 (no more unresolved files)
623 continue: hg rebase --continue
626 continue: hg rebase --continue
624
627
625 $ hg resolve -l
628 $ hg resolve -l
626 R emp1
629 R emp1
627 R emp2
630 R emp2
628 R emp3
631 R emp3
629
632
630 $ hg rebase --abort
633 $ hg rebase --abort
631 rebase aborted
634 rebase aborted
632
635
633 Done with commands.resolve.confirm tests:
636 Done with commands.resolve.confirm tests:
634 $ cd ..
637 $ cd ..
635
638
636 Test that commands.resolve.mark-check works even if there are deleted files:
639 Test that commands.resolve.mark-check works even if there are deleted files:
637 $ hg init resolve-deleted
640 $ hg init resolve-deleted
638 $ cd resolve-deleted
641 $ cd resolve-deleted
639 $ echo r0 > file1
642 $ echo r0 > file1
640 $ hg ci -qAm r0
643 $ hg ci -qAm r0
641 $ echo r1 > file1
644 $ echo r1 > file1
642 $ hg ci -qm r1
645 $ hg ci -qm r1
643 $ hg co -qr 0
646 $ hg co -qr 0
644 $ hg rm file1
647 $ hg rm file1
645 $ hg ci -qm "r2 (delete file1)"
648 $ hg ci -qm "r2 (delete file1)"
646
649
647 (At this point we have r0 creating file1, and sibling commits r1 and r2, which
650 (At this point we have r0 creating file1, and sibling commits r1 and r2, which
648 modify and delete file1, respectively)
651 modify and delete file1, respectively)
649
652
650 $ hg merge -r 1
653 $ hg merge -r 1
651 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
654 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
652 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
655 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
653 What do you want to do? u
656 What do you want to do? u
654 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
657 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
655 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
658 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
656 [1]
659 [1]
657 $ hg resolve --list
660 $ hg resolve --list
658 U file1
661 U file1
659 Because we left it as 'unresolved' the file should still exist.
662 Because we left it as 'unresolved' the file should still exist.
660 $ [ -f file1 ] || echo "File does not exist?"
663 $ [ -f file1 ] || echo "File does not exist?"
661 BC behavior: `hg resolve --mark` accepts that the file is still there, and
664 BC behavior: `hg resolve --mark` accepts that the file is still there, and
662 doesn't have a problem with this situation.
665 doesn't have a problem with this situation.
663 $ hg resolve --mark --config commands.resolve.mark-check=abort
666 $ hg resolve --mark --config commands.resolve.mark-check=abort
664 (no more unresolved files)
667 (no more unresolved files)
665 $ hg resolve --list
668 $ hg resolve --list
666 R file1
669 R file1
667 The file is still there:
670 The file is still there:
668 $ [ -f file1 ] || echo "File does not exist?"
671 $ [ -f file1 ] || echo "File does not exist?"
669 Let's check mark-check=warn:
672 Let's check mark-check=warn:
670 $ hg resolve --unmark file1
673 $ hg resolve --unmark file1
671 $ hg resolve --mark --config commands.resolve.mark-check=warn
674 $ hg resolve --mark --config commands.resolve.mark-check=warn
672 (no more unresolved files)
675 (no more unresolved files)
673 $ hg resolve --list
676 $ hg resolve --list
674 R file1
677 R file1
675 The file is still there:
678 The file is still there:
676 $ [ -f file1 ] || echo "File does not exist?"
679 $ [ -f file1 ] || echo "File does not exist?"
677 Let's resolve the issue by deleting the file via `hg resolve`
680 Let's resolve the issue by deleting the file via `hg resolve`
678 $ hg resolve --unmark file1
681 $ hg resolve --unmark file1
679 $ echo 'd' | hg resolve file1 --config ui.interactive=1
682 $ echo 'd' | hg resolve file1 --config ui.interactive=1
680 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
683 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
681 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
684 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
682 What do you want to do? d
685 What do you want to do? d
683 (no more unresolved files)
686 (no more unresolved files)
684 $ hg resolve --list
687 $ hg resolve --list
685 R file1
688 R file1
686 The file is deleted:
689 The file is deleted:
687 $ [ -f file1 ] && echo "File still exists?" || true
690 $ [ -f file1 ] && echo "File still exists?" || true
688 Doing `hg resolve --mark` doesn't break now that the file is missing:
691 Doing `hg resolve --mark` doesn't break now that the file is missing:
689 $ hg resolve --mark --config commands.resolve.mark-check=abort
692 $ hg resolve --mark --config commands.resolve.mark-check=abort
690 (no more unresolved files)
693 (no more unresolved files)
691 $ hg resolve --mark --config commands.resolve.mark-check=warn
694 $ hg resolve --mark --config commands.resolve.mark-check=warn
692 (no more unresolved files)
695 (no more unresolved files)
693 Resurrect the file, and delete it outside of hg:
696 Resurrect the file, and delete it outside of hg:
694 $ hg resolve --unmark file1
697 $ hg resolve --unmark file1
695 $ hg resolve file1
698 $ hg resolve file1
696 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
699 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
697 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
700 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
698 What do you want to do? u
701 What do you want to do? u
699 [1]
702 [1]
700 $ [ -f file1 ] || echo "File does not exist?"
703 $ [ -f file1 ] || echo "File does not exist?"
701 $ hg resolve --list
704 $ hg resolve --list
702 U file1
705 U file1
703 $ rm file1
706 $ rm file1
704 $ hg resolve --mark --config commands.resolve.mark-check=abort
707 $ hg resolve --mark --config commands.resolve.mark-check=abort
705 (no more unresolved files)
708 (no more unresolved files)
706 $ hg resolve --list
709 $ hg resolve --list
707 R file1
710 R file1
708 $ hg resolve --unmark file1
711 $ hg resolve --unmark file1
709 $ hg resolve file1
712 $ hg resolve file1
710 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
713 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
711 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
714 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
712 What do you want to do? u
715 What do you want to do? u
713 [1]
716 [1]
714 $ [ -f file1 ] || echo "File does not exist?"
717 $ [ -f file1 ] || echo "File does not exist?"
715 $ hg resolve --list
718 $ hg resolve --list
716 U file1
719 U file1
717 $ rm file1
720 $ rm file1
718 $ hg resolve --mark --config commands.resolve.mark-check=warn
721 $ hg resolve --mark --config commands.resolve.mark-check=warn
719 (no more unresolved files)
722 (no more unresolved files)
720 $ hg resolve --list
723 $ hg resolve --list
721 R file1
724 R file1
722
725
723
726
724 For completeness, let's try that in the opposite direction (merging r2 into r1,
727 For completeness, let's try that in the opposite direction (merging r2 into r1,
725 instead of r1 into r2):
728 instead of r1 into r2):
726 $ hg update -qCr 1
729 $ hg update -qCr 1
727 $ hg merge -r 2
730 $ hg merge -r 2
728 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
731 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
729 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
732 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
730 What do you want to do? u
733 What do you want to do? u
731 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
734 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
732 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
735 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
733 [1]
736 [1]
734 $ hg resolve --list
737 $ hg resolve --list
735 U file1
738 U file1
736 Because we left it as 'unresolved' the file should still exist.
739 Because we left it as 'unresolved' the file should still exist.
737 $ [ -f file1 ] || echo "File does not exist?"
740 $ [ -f file1 ] || echo "File does not exist?"
738 BC behavior: `hg resolve --mark` accepts that the file is still there, and
741 BC behavior: `hg resolve --mark` accepts that the file is still there, and
739 doesn't have a problem with this situation.
742 doesn't have a problem with this situation.
740 $ hg resolve --mark --config commands.resolve.mark-check=abort
743 $ hg resolve --mark --config commands.resolve.mark-check=abort
741 (no more unresolved files)
744 (no more unresolved files)
742 $ hg resolve --list
745 $ hg resolve --list
743 R file1
746 R file1
744 The file is still there:
747 The file is still there:
745 $ [ -f file1 ] || echo "File does not exist?"
748 $ [ -f file1 ] || echo "File does not exist?"
746 Let's check mark-check=warn:
749 Let's check mark-check=warn:
747 $ hg resolve --unmark file1
750 $ hg resolve --unmark file1
748 $ hg resolve --mark --config commands.resolve.mark-check=warn
751 $ hg resolve --mark --config commands.resolve.mark-check=warn
749 (no more unresolved files)
752 (no more unresolved files)
750 $ hg resolve --list
753 $ hg resolve --list
751 R file1
754 R file1
752 The file is still there:
755 The file is still there:
753 $ [ -f file1 ] || echo "File does not exist?"
756 $ [ -f file1 ] || echo "File does not exist?"
754 Let's resolve the issue by deleting the file via `hg resolve`
757 Let's resolve the issue by deleting the file via `hg resolve`
755 $ hg resolve --unmark file1
758 $ hg resolve --unmark file1
756 $ echo 'd' | hg resolve file1 --config ui.interactive=1
759 $ echo 'd' | hg resolve file1 --config ui.interactive=1
757 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
760 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
758 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
761 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
759 What do you want to do? d
762 What do you want to do? d
760 (no more unresolved files)
763 (no more unresolved files)
761 $ hg resolve --list
764 $ hg resolve --list
762 R file1
765 R file1
763 The file is deleted:
766 The file is deleted:
764 $ [ -f file1 ] && echo "File still exists?" || true
767 $ [ -f file1 ] && echo "File still exists?" || true
765 Doing `hg resolve --mark` doesn't break now that the file is missing:
768 Doing `hg resolve --mark` doesn't break now that the file is missing:
766 $ hg resolve --mark --config commands.resolve.mark-check=abort
769 $ hg resolve --mark --config commands.resolve.mark-check=abort
767 (no more unresolved files)
770 (no more unresolved files)
768 $ hg resolve --mark --config commands.resolve.mark-check=warn
771 $ hg resolve --mark --config commands.resolve.mark-check=warn
769 (no more unresolved files)
772 (no more unresolved files)
770 Resurrect the file, and delete it outside of hg:
773 Resurrect the file, and delete it outside of hg:
771 $ hg resolve --unmark file1
774 $ hg resolve --unmark file1
772 $ hg resolve file1
775 $ hg resolve file1
773 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
776 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
774 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
777 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
775 What do you want to do? u
778 What do you want to do? u
776 [1]
779 [1]
777 $ [ -f file1 ] || echo "File does not exist?"
780 $ [ -f file1 ] || echo "File does not exist?"
778 $ hg resolve --list
781 $ hg resolve --list
779 U file1
782 U file1
780 $ rm file1
783 $ rm file1
781 $ hg resolve --mark --config commands.resolve.mark-check=abort
784 $ hg resolve --mark --config commands.resolve.mark-check=abort
782 (no more unresolved files)
785 (no more unresolved files)
783 $ hg resolve --list
786 $ hg resolve --list
784 R file1
787 R file1
785 $ hg resolve --unmark file1
788 $ hg resolve --unmark file1
786 $ hg resolve file1
789 $ hg resolve file1
787 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
790 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
788 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
791 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
789 What do you want to do? u
792 What do you want to do? u
790 [1]
793 [1]
791 $ [ -f file1 ] || echo "File does not exist?"
794 $ [ -f file1 ] || echo "File does not exist?"
792 $ hg resolve --list
795 $ hg resolve --list
793 U file1
796 U file1
794 $ rm file1
797 $ rm file1
795 $ hg resolve --mark --config commands.resolve.mark-check=warn
798 $ hg resolve --mark --config commands.resolve.mark-check=warn
796 (no more unresolved files)
799 (no more unresolved files)
797 $ hg resolve --list
800 $ hg resolve --list
798 R file1
801 R file1
799
802
800 $ cd ..
803 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now