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